如果以比页粒度更精细的方式对给定内存位置进行写入,您是否可以强制崩溃?
我正在编写一个程序,出于性能原因使用共享内存(已经评估了套接字和管道作为替代方案,它们对于我的任务来说不够快,一般来说,任何涉及副本的 IPC 方法都太慢)。在共享内存区域中,我正在编写许多固定大小的结构。有一个程序负责将结构写入共享内存,并且有许多客户端从中读取。然而,客户端需要写入每个结构体的一个成员(引用计数,它们将自动更新)。所有其他成员应该仅供客户阅读。
由于客户端需要更改该成员,因此它们无法将共享内存区域映射为只读。但他们也不应该修改其他成员,并且由于这些程序是用 C++ 编写的,因此内存损坏是可能的。理想情况下,一个客户端应该尽可能难以让另一个客户端崩溃。我只担心有缺陷的客户端,而不是恶意的客户端,因此允许不完美的解决方案。
我可以尝试通过将标头中的成员声明为 const 来阻止客户端覆盖,但这并不能防止覆盖导致内存损坏(缓冲区溢出、错误的转换等)。我可以插入金丝雀,但随后我必须不断支付检查它们的费用。
我可以将指向实际数据的指针存储在单独的映射只写页面中,同时将结构保留在只读映射页面中,而不是直接存储引用计数成员。这将起作用,如果我尝试写入指向的数据,操作系统将迫使我的应用程序崩溃,但在尝试写入 无锁算法,因为需要遵循另一个间接级别可以改变是否可以原子地完成某些操作。
有没有什么方法可以标记较小的内存区域,这样写入它们就会导致您的应用程序崩溃?有些平台有硬件观察点,也许我可以通过内联汇编激活其中一个观察点,但在 32 位 x86 上我一次只能激活 4 个观察点,并且每个观察点只能覆盖结构的一部分,因为它们是有限的至 4 字节。这也会使我的程序调试起来很痛苦;)
编辑:我发现 这篇相当令人瞠目结舌的论文,但不幸的是它需要使用 ECC 内存和修改后的 Linux 内核。
I'm writing a program that for performance reasons uses shared memory (sockets and pipes as alternatives have been evaluated, and they are not fast enough for my task, generally speaking any IPC method that involves copies is too slow). In the shared memory region I am writing many structs of a fixed size. There is one program responsible for writing the structs into shared memory, and many clients that read from it. However, there is one member of each struct that clients need to write to (a reference count, which they will update atomically). All of the other members should be read only to the clients.
Because clients need to change that one member, they can't map the shared memory region as read only. But they shouldn't be tinkering with the other members either, and since these programs are written in C++, memory corruption is possible. Ideally, it should be as difficult as possible for one client to crash another. I'm only worried about buggy clients, not malicious ones, so imperfect solutions are allowed.
I can try to stop clients from overwriting by declaring the members in the header they use as const, but that won't prevent memory corruption (buffer overflows, bad casts, etc.) from overwriting. I can insert canaries, but then I have to constantly pay the cost of checking them.
Instead of storing the reference count member directly, I could store a pointer to the actual data in a separate mapped write only page, while keeping the structs in read only mapped pages. This will work, the OS will force my application to crash if I try to write to the pointed to data, but indirect storage can be undesirable when trying to write lock free algorithms, because needing to follow another level of indirection can change whether something can be done atomically.
Is there any way to mark smaller areas of memory such that writing them will cause your app to blow up? Some platforms have hardware watchpoints, and maybe I could activate one of those with inline assembly, but I'd be limited to only 4 at a time on 32-bit x86 and each one could only cover part of the struct because they're limited to 4 bytes. It'd also make my program painful to debug ;)
Edit: I found this rather eye popping paper, but unfortunately it requires using ECC memory and a modified Linux kernel.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我认为不可能像操作系统级别那样使一些位只读。
我刚才想到的一件事是,您可以像您建议的那样将引用计数放在不同的页面中。如果结构体具有通用大小,并且全部位于连续的内存位置中,则可以使用指针算术从结构体指针中定位引用计数,而不是在结构体中使用指针。这可能比为您的用例提供一个指针更好。
I don't think its possible to make a few bits read only like that at the OS level.
One thing that occurred to me just now is that you could put the reference counts in a different page like you suggested. If the structs are a common size, and are all in sequential memory locations you could use pointer arithmetic to locate a reference count from the structures pointer, rather than having a pointer within the structure. This might be better than having a pointer for your use case.
您需要为 SIGSEGV 添加一个从异常中恢复的信号处理程序,但仅限于某些地址。起点可能是 http://www.opengroup.org/onlinepubs /009695399/basedefs/signal.h.html 以及适用于您的操作系统的相应文档。
编辑:我相信您想要的是执行写入并返回(如果写入地址实际上正确),并尾调用前一个异常处理程序(安装异常处理程序时获得的指针)如果你想传播异常。不过我对这些事情没有经验。
You would need to add a signal handler for SIGSEGV which recovers from the exception, but only for certain addresses. A starting point might be http://www.opengroup.org/onlinepubs/009695399/basedefs/signal.h.html and the corresponding documentation for your OS.
Edit: I believe what you want is to perform the write and return if the write address is actually OK, and tail-call the previous exception handler (the pointer you get when you install your exception handler) if you want to propagate the exception. I'm not experienced in these things though.
我从未听说过在小于页面粒度的情况下强制执行只读,因此除非您可以将每个结构放在两个页面上,否则您在这个方向上可能会运气不好。如果您可以负担每个结构两个页面,您可以将引用计数放在其中一个页面上,并使另一个页面只读。
您可以编写 API 而不仅仅是使用标头。强制客户端使用 API 将消除大多数腐败问题。
将数据与引用计数一起保存而不是放在不同的页面上将有助于数据的局部性,从而提高缓存性能。
您需要考虑读者可能会遇到问题并且无法正确更新其引用计数。另外,作者可能无法完成更新。应对这些事情需要额外的检查。您可以将此类检查与 API 结合起来。测量某种完整性检查对性能的影响可能值得尝试。它可能足够快来保存校验和,就像 adler32 一样简单。
I have never heard of enforcing read-only at less than a page granularity, so you might be out of luck in that direction unless you can put each struct on two pages. If you can afford two pages per struct you can put the ref count on one of the pages and make the other read-only.
You could write an API rather than just use headers. Forcing clients to use the API would remove most corruption issues.
Keeping the data with the reference count rather than on a different page will help with locality of data and so improve cache performance.
You need to consider that a reader may have a problem and fail to properly update its ref count. Also that the writer may fail to complete an update. Coping with these things requires extra checks. You can combine such checks with the API. It may be worth experimenting to measure the performance implications of some kind of integrity checking. It may be fast enough to keep a checksum, something as simple as adler32.