如果写入比给定页面粒度更精细的给定内存位置,是否可以强制崩溃?

时间:2010-03-20 22:04:52

标签: c++ shared-memory virtual-memory reference-counting memory-corruption

我正在编写一个程序,出于性能原因使用共享内存(套接字和管道作为替代方案已被评估,并且它们对我的任务来说不够快,一般来说任何涉及副本的IPC方法都太慢)。在共享内存区域中,我正在编写许多固定大小的结构。有一个程序负责将结构写入共享内存,以及从中读取的许多客户端。但是,每个结构中都有一个客户端需要写入的成员(引用计数,它们将以原子方式更新)。所有其他成员都应该只读给客户。

由于客户端需要更改该成员,因此无法将共享内存区域映射为只读。但他们也不应该修改其他成员,因为这些程序是用C ++编写的,所以内存损坏是可能的。理想情况下,一个客户端应该尽可能地使另一个客户端崩溃。我只担心有问题的客户,而不是恶意客户,因此不允许使用不完善的解决方案。

我可以尝试通过声明用作const的标头中的成员来阻止客户端覆盖,但这不会阻止内存损坏(缓冲区溢出,错误转换等)被覆盖。我可以插入canaries,但是我必须经常支付检查费用。

不是直接存储引用计数成员,而是可以在单独的映射只写页面中存储指向实际数据的指针,同时将结构保留在只读映射页面中。这将起作用,如果我尝试写入指向的数据,操作系统将强制我的应用程序崩溃,但在尝试编写lock free algorithms时间接存储可能是不合需要的,因为需要遵循另一级别的间接可以改变是否可以原子地完成某些事情。

有没有办法标记较小的内存区域,这样写入它们会导致应用程序崩溃?有些平台有硬件观察点,也许我可以激活其中一个内联汇编,但我只能在32位x86上一次只限4个,每个只能覆盖部分结构,因为它们有限到4个字节。它也会让我的程序调试变得痛苦;)

编辑:我找到this rather eye popping paper,但遗憾的是它需要使用ECC内存和修改过的Linux内核。

3 个答案:

答案 0 :(得分:6)

我认为不可能只在操作系统级别读取一些位。

刚才我发现的一件事是你可以像你建议的那样将引用计数放在不同的页面中。如果结构是一个通用大小,并且都在顺序存储器位置,您可以使用指针算法从结构指针中定位引用计数,而不是在结构中有一个指针。这可能比为您的用例指针更好。

long *refCountersBase;//The start address of the ref counters page
MyStruct *structsBase;//The start address of your structures page

//get address to reference counter
long *getRefCounter(MyStruct *myStruct )
{
    size_t n = myStruct - structsBase;
    long *ref = refCountersBase + n;
    return ref;
}

答案 1 :(得分:1)

您需要为SIGSEGV添加一个信号处理程序,该处理程序从异常中恢复,但仅适用于某些地址。起点可能是http://www.opengroup.org/onlinepubs/009695399/basedefs/signal.h.html以及操作系统的相应文档。

编辑:我相信您想要的是执行写入并返回写入地址实际上是否正常,并尾调用前一个异常处理程序(安装异常时获得的指针) handler)如果要传播异常。我对这些事情没有经验。

答案 2 :(得分:1)

我从未听说过以少于一页的粒度强制执行只读,所以除非你可以将每个结构放在两个页面上,否则你可能会在这个方向上运气不好。如果每个结构可以支付两个页面,则可以将引用计数放在其中一个页面上,并将另一个页面设置为只读页面。

您可以编写API而不只是使用标头。强制客户端使用API​​将消除大多数损坏问题。

使用引用计数而不是在不同的页面上保存数据将有助于数据的位置,从而提高缓存性能。

您需要考虑读者可能遇到问题并且无法正确更新其引用计数。此外,作者可能无法完成更新。处理这些事情需要额外的检查。您可以将此类检查与API结合使用。可能值得尝试测量某种完整性检查的性能影响。保持校验和可能足够快,就像adler32一样简单。