如果在取消点调用信号处理程序会发生什么?

时间:2011-03-23 16:13:39

标签: c linux pthreads posix cancellation

假设应用程序在取消点被阻止,例如read,并且接收到信号并调用信号处理程序。 Glibc / NPTL通过在系统调用期间启用异步取消来实现取消点,因此据我所知,异步取消将在信号处理程序的整个持续时间内保持有效。这当然是非常错误的,因为有很多函数不是异步取消安全的,但是要求从信号处理程序调用是安全的。

这给我留下了两个问题:

  • 我错了还是glibc / NPTL的行为真的被危险地打破了?如果是这样,这种危险行为是否符合要求?
  • 根据POSIX,如果在进程执行取消点的函数时调用信号处理程序,应该会发生什么?

编辑我几乎让自己确信任何作为pthread_cancel潜在目标的线程必须确保永远不会从信号处理程序中调用取消点的函数线程的上下文:

一方面,可以在一个可能被取消的线程中调用并使用任何async-cancel-unsafe函数的任何信号处理程序必须在调用任何取消函数之前禁用取消点。这是因为,从信号中断的代码的角度来看,任何这种取消都等同于异步取消。另一方面,信号处理程序不能禁用取消,除非在调用信号处理程序时将运行的代码仅使用异步信号安全函数,因为pthread_setcancelstate不是异步-signal安全的。

3 个答案:

答案 0 :(得分:4)

回答我自己问题的前半部分:glibc确实展示了我预测的行为。在取消点处阻塞时运行的信号处理程序在异步取消下运行。要查看此效果,只需创建一个调用取消点的线程,该取消点将永久阻塞(或长时间),等待片刻,发送信号,再等一会儿,取消并加入。信号处理程序应该以一种方式摆弄一些易变变量,以便明确表示它在异步终止之前运行了不可预测的时间。

至于POSIX是否允许这种行为,我仍然不是100%肯定。 POSIX声明:

  

每当线程启用了可取消性并且已经以该线程作为目标取消请求,然后线程调用任何作为取消点的函数(例如pthread_testcancel()或read())时,取消请求在函数返回之前应该对它起作用。如果线程已启用可取消性并且在线程作为目标进行取消请求,而线程在取消点处被挂起,则线程应被唤醒并且应取消取消请求。未指定取消请求是否被执行或取消请求是否保持未决状态,并且线程在以下情况下恢复正常执行:

     
      
  • 线程在取消点暂停,并且正在等待的事件发生

  •   
  • 指定的超时已过期

  •   
     在取消请求被采取行动之前

据推测,执行信号处理程序不是“暂停”的情况,因此我倾向于将glibc的行为解释为不符合要求。

答案 1 :(得分:1)

富,

我在做Alex Aliva正在为glibc工作的AC安全文档审核时遇到了这个问题。

我认为GNU C库实现(基于nptl)没有被破坏。虽然在阻塞系统调用(需要是取消点)的情况下启用异步取消是正确的,但这种行为应该仍然符合。

启用异步取消后获取的信号也将导致信号处理程序在启用异步取消的情况下运行。在该处理程序中执行任何不同步取消安全的操作也是危险的。

如果另一个线程以运行线程的信号作为目标调用pthread_cancel,那么这种取消也将立即起作用。这仍然符合“在函数返回之前”的POSIX措辞(在这种情况下,read没有返回,目标线程在信号处理程序中)。

信号的问题在于它导致线程处于两个同时的状态,这两个状态都在取消点中永久存在,并且执行指令。如果取消请求到达,我认为立即采取行动是符合要求的。虽然奥斯汀集团可能会澄清。

glibc实现的问题在于它需要所有信号处理程序,由待取消的线程执行。只调用异步取消安全函数。这是一个非显而易见的要求,不是源于标准,但不会使其不符合要求。

解决信号处理程序脆弱性的潜在解决方案:

  • 不要为阻塞系统调用启用异步取消,而是在取消实现中启用新的IN_SYSCALL位。

  • 当调用pthread_cancel并且目标线程设置了IN_SYSCALL时,然后像通常对async-cancel那样发送SIGCANCEL到线程,但是SIGCANCEL处理程序什么都不做(除了中断系统调用的副作用)

  • 系统调用周围的包装器将查找已发送的取消并在包装器返回之前取消该线程。

虽然在堆栈溢出上发布此内容很有趣,但我不知道其他任何人阅读此内容并且可以在详细信息中回答您的问题。

我认为任何进一步的讨论都应该作为POSIX标准讨论的一部分在Austin Group邮件列表上进行,或者应该在libc-alpha上进行,作为glibc实现讨论的一部分。

答案 2 :(得分:0)

我认为你要找的是两件事的组合:

某些系统调用可能会被信号中断,从而导致返回EINTR错误。这是正常的行为,但是我从来都不清楚如果你正处于read的中间会发生什么 - 从流中读取的内容是什么?也许有人可以对此发表评论以帮助澄清。

不应该像你担心的那样打断不应该中断的系统调用,应该包含在sigprocmask(或线程中的pthread_sigmask)的调用中,以防止它们被中断。重新启用信号后,阻止接收的任何信号都将被传送。但是,与中断一样,如果你阻塞太久,你可能会因为覆盖而错过一些(多次接收相同的信号作为一个未决信号)。