glibc/NPTL/Linux健壮互斥体中的竞争条件?

glibc/NPTL/Linux健壮互斥体中的竞争条件?,c,linux,pthreads,C,Linux,Pthreads,在2010年对这个问题的评论中,jilles声称: glibc强大的互斥锁速度如此之快是因为glibc走了危险的捷径。当内核将互斥体标记为“将导致EOwnerded”时,不能保证互斥体仍然存在。如果互斥体被破坏,内存被一个内存映射文件替换,该文件恰好在正确的位置包含最后一个拥有线程的ID,并且最后一个拥有线程在写入锁字后(但在从其拥有的互斥体列表中完全删除互斥体之前)终止,则该文件已损坏。Solaris和即将推出的BSD9健壮互斥锁速度较慢,因为它们不想冒这个风险 我无法理解这种说法,因为销毁互

在2010年对这个问题的评论中,jilles声称:

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

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


我之所以这么问,我之所以感兴趣,是因为这与我自己基于同一个Linux健壮互斥原语构建的实现的正确性有关。

我认为我发现了竞争,这确实非常丑陋。事情是这样的:

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

  • 将其放入线程的健壮列表头的“挂起”槽中
  • 将其从当前线程持有的健壮互斥体的链接列表中删除
  • 解锁互斥锁
  • 清除线程的健壮列表头的“挂起”插槽
  • 问题在于,在步骤3和步骤4之间,同一进程中的另一个线程可以获得互斥锁,然后解锁它,并且(正确地)认为自己是互斥锁的最终用户,销毁并释放/映射它。在此之后,如果进程中的任何线程创建了文件、设备或共享内存的共享映射,并且恰好为其分配了相同的地址,并且该位置的值恰好与仍处于解锁步骤3和步骤4之间的线程的pid匹配,则会出现这样的情况,即,如果进程被终止,内核将通过设置它认为是互斥锁所有者id的32位整数的高位来损坏映射文件

    解决方案是在上述第2步和第4步之间对mmap/munmap进行全局锁定,与我在回答此问题时所述的屏障问题的解决方案完全相同:


    FreeBSD pthread开发人员David Xu对竞赛的描述:

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


    正如该消息中还提到的,如果具有不同权限的线程访问一个通用的健壮互斥锁,则会更“有趣”。由于拥有的健壮互斥体列表的节点位于互斥体本身中,因此具有低权限的线程可能会损坏高权限线程的列表。这很容易被利用,使高权限线程崩溃,在极少数情况下,这可能会导致高权限线程的内存损坏。显然,Linux的健壮互斥体只设计用于具有相同权限的线程。通过将健壮列表完全置于线程内存中而不是链表中,可以很容易地避免这种情况。

    omg,错误名称在ITI中已经过时了。似乎旧的基于VMA的方法至少有一些问题。但是,如果我读得正确,列表将保存在用户空间内存中-那么,如果该内存已损坏,您该怎么办?虽然这可能只是破坏共享内存的一个特例。是的,我看到如果进程运行异常并对其进行重击,列表甚至互斥对象内容都可能被破坏。这就是正在描述的问题吗?当访问互斥对象的进程调用了未定义的行为时,我并不担心确保正确的行为;我只是担心在使用健壮互斥时可能会出现一些竞争条件。我想我自己已经解决了这个问题,但我很乐意将奖金奖励给任何有兴趣提供更多关于这个主题的信息的人。感谢你抓住了在不取消映射的情况下将内存用于其他用途的问题。这应该通过在
    pthread\u mutex\u destroy
    中进行某种同步来避免。关于最后一句话,使用数组而不是链表的“简单”解决方案实际上并不容易。您没有用于阵列的存储空间。存储需求随着线程持有的健壮互斥体的数量线性增长,在锁定时获得存储而不需要额外分配(可能会失败)的唯一方法是将其放入互斥体本身。我同意这是不幸的,但似乎没有其他办法。大多数内核空间解决方案还需要在锁时分配,这可能会失败,导致锁毫无用处……为了避免在内核中花费太多资源,Linux限制了链表的长度。因此,如果您锁定了太多健壮的互斥锁,那么除了进程终止时的解锁之外,其他一切都可以工作。在这种情况下,失败
    pthread\u mutex\u lock
    似乎更有意义。此外,鉴于必须已经检查健壮互斥体上的
    pthread\u mutex\u lock
    的返回值(对于[EOWNERDEAD]和[ENOTRECOVERABLE]),我认为由于内存不足等原因导致此操作失败是可以接受的。此外,假设您的程序可以恢复受保护对象的状态,
    EOWNERDEAD
    是您可以处理的条件
    ENOMEM
    或类似情况不是您可以处理的条件;如果程序无法获得等待它的锁或块,则程序无法继续。请记住,
    pthread\u mutex\u lock
    可以从取消处理程序调用,其中需要将互斥锁锁定为下一级取消处理程序的不变量…已接受,我可能也会奖励这个答案。我想我在解释这件事上做得更好