16

jilles 在对 2010 年Unix 崩溃时自动释放互斥锁问题的评论中声称:

glibc 强大的互斥锁是如此之快,因为 glibc 采用了危险的捷径。当内核将其标记为“将导致 EOWNERDEAD”时,不能保证互斥锁仍然存在。如果互斥锁被销毁并且内存被内存映射文件替换,该文件恰好在正确的位置包含最后拥有线程的 ID,并且最后拥有线程在写入锁定字之后终止(但在从其列表中完全删除互斥锁之前)拥有的互斥体),文件已损坏。Solaris 和即将成为 FreeBSD9 的健壮互斥体较慢,因为它们不想冒这个风险。

我无法理解这种说法,因为销毁互斥锁​​是不合法的,除非它被解锁(因此不在任何线程的健壮列表中)。我也找不到任何搜索此类错误/问题的参考资料。这种说法是完全错误的吗?

我问并且我感兴趣的原因是,这与我自己基于相同 Linux 健壮互斥原语构建的实现的正确性有关。

4

2 回答 2

8

我想我找到了比赛,而且确实很丑陋。它是这样的:

线程 A 持有健壮的互斥体并将其解锁。基本程序是:

  1. 把它放在线程健壮列表头的“待定”槽中。
  2. 从当前线程持有的健壮互斥锁链表中删除它。
  3. 解锁互斥锁。
  4. 清除线程健壮列表标题的“待处理”槽。

问题在于,在第 3 步和第 4 步之间,同一进程中的另一个线程可以获得互斥锁,然后将其解锁,并且(正确地)相信自己是互斥锁的最终用户,销毁并释放/munmap 它。之后,如果进程中的任何线程创建了文件、设备或共享内存的共享映射,并且它恰好被分配了相同的地址,并且该位置的值恰好与仍在步骤之间的线程的 pid 匹配在解锁的第 3 和第 4 步中,如果进程被杀死,内核将通过设置它认为是互斥体所有者 ID 的 32 位整数的高位来破坏映射文件。

解决方案是在上面的第 2 步和第 4 步之间对 mmap/munmap 进行全局锁定,这与我对这个问题的回答中描述的障碍问题的解决方案完全相同:

可以在 Linux 上实现正确的故障安全进程共享屏障吗?

于 2012-08-17T18:34:37.700 回答
6

FreeBSD pthread 开发者 David Xu 对比赛的描述:http: //lists.freebsd.org/pipermail/svn-src-user/2010-November/003668.html

我不认为比赛严格要求 munmap/mmap 循环。这块共享内存也可能有不同的用途。这不常见但有效。

正如该消息中还提到的,如果具有不同特权的线程访问一个通用的健壮互斥锁,则会出现更多“乐趣”。因为拥有的健壮互斥锁列表的节点位于互斥锁本身中,所以具有低权限的线程可能会破坏高权限线程的列表。这很容易被利用来使高权限线程崩溃,并且在极少数情况下,这可能会导致高权限线程的内存被破坏。显然,Linux 强大的互斥锁仅设计用于具有相同权限的线程。这可以通过使健壮列表成为完全在线程内存中的数组而不是链表来轻松避免。

于 2012-08-17T22:45:24.210 回答