在这种情况下捕获访问冲突是否安全?

时间:2010-05-28 22:05:17

标签: c++ undefined-behavior

我已经阅读了很多内容,包括here on SO,这表明这是一个非常糟糕的主意,你唯一可以安全做的就是退出程序。我不确定这是真的。

这是一个池化内存分配器,它将大量分配交给malloc。在pool_free()期间,需要检查指针它是属于池还是使用malloc分配。通过将地址向下舍入到最接近的1MB边界,我得到一个指向池中内存块开头的指针,或者如果使用malloc则得到undefined。在第一种情况下,我可以轻松验证内存块是否属于池,但是,如果不是,我将验证失败, OR 我将获得访问冲突(注意这是一个只读过程)。难道我不能用SEH(Windows)或处理信号(POSIX)来解决这个问题并将其视为失败的验证吗? (即只有在使用malloc时才有可能,所以将ptr传递给free())

编辑:人们似乎错过了上面的OR。如果指针分配了malloc,我不希望得到访问冲突,但这是一种可能的结果。使用指向块开头的指针(在1MB边界处)的过程是验证幻数,然后跟随指向内存池的指针,并检查它实际上是否包含前面指向块的指针。如果这些只读步骤中的任何一个产生访问冲突,则无法验证,就像任何单个步骤失败一样。

4 个答案:

答案 0 :(得分:2)

您需要更好的测试。如果使用malloc作为向下舍入点也可能已经分配给您的应用程序,那么您将无法保证获得AV,因此您将可以访问该内存。

答案 1 :(得分:1)

无需实施反应机制。您可以通过将堆分配与1 MB边界对齐来解决问题:

  1. Windows:_aligned_malloc(size, 1<<20)
  2. Unix:memalign(1<<20, size)
  3. 使用这种方法,向下舍入到1 MB可以保证指向已分配的内存块,您只需要辨别该地址是在池中还是在池外(在这种情况下,它显然是{{1} } ED)。

    您需要小心谨慎,您只对正常大的对象使用对齐堆分配。如果你使用它,比如尺寸&gt; 100 kB,分配器将在对象之间留下巨大的空隙。理想情况下,仅将其用于不适合1 MB池块的对象。

答案 2 :(得分:0)

我认为它应该是安全的。但可能是一个坏主意。

但是,如果您需要将池分配的内存与非池分配的内存混合使用,我认为您需要在某处存储有关该内存的信息。也许使用pool_alloc()进行的每个内存分配都可能有一个很小的标题,在实际分配之前“隐藏”。此标头可以包含有关如何分配的信息。所以char * block =(char *)pool_alloc(32)实际上会分配32 + sizeof(BlockHeader)字节。而block - sizeof(BlockHeader)将提供对标题的访问。

根据msdn:

,IsBadReadPtr已经过时了
  

重要此功能已过时,不应使用。尽管它的名称,它不保证指针有效或指向的内存是安全的使用。有关详细信息,请参阅此页面上的备注。

答案 3 :(得分:0)

好的,inazaruk发布了一个严重低估的答案,然后删除了建议使用IsBadReadPtr + VirtualQuery(以避免保护或没有访问页面。)阅读他发布的链接提醒我注意读取随机内存区域的事实潜在的副作用比访问违规更糟糕。

在线程堆栈的末尾意外访问一个保护页面,如果该线程堆栈增长,将导致程序突然终止。

因此,捕获访问冲突可能不安全。这回答了这个问题。