7

我正在阅读 DJB 的“Qmail 1.0 十年后对安全性的一些思考”,他列出了移动文件描述符的这个函数:

int fd_move(到,从)
诠释为;
来自;
{
  如果(到 == 从)返回 0;
  if (fd_copy(to,from) == -1) 返回 -1;
  关闭(从);
  返回0;
}

我突然想到这段代码没有检查 close 的返回值,所以我阅读了 close(2) 的手册页,似乎它可能会失败EINTR,在这种情况下,适当的行为似乎是再次调用 close以同样的论点。

由于这段代码是由在 C 和 UNIX 方面比我有更多经验的人编写的,并且在 qmail 中十多年来一直保持不变,我认为一定有一些我遗漏的细微差别使得这段代码正确。谁能向我解释这种细微差别?

4

4 回答 4

2

我有两个答案:

  1. 他试图提出一个关于分解通用代码的观点,并且为了简洁和清晰,这些示例通常会省略错误检查。
  2. close(2) 可能会返回 EINTER,但在实践中会这样做,如果是这样,你会合理地做什么?重试一次?重试直到成功?如果你得到 EIO 怎么办?这可能意味着几乎任何事情,所以除了记录它并继续前进之外,你真的没有合理的追索权。如果您在 EIO 之后重试,您可能会得到 EBADF,然后呢?假设描述符已关闭并继续前进?

每个系统调用都可以返回 EINTR,尤其是像 read(2) 这样阻塞等待慢人的系统调用。这是一个更有可能的情况,一个好的“从终端获取输入”例程确实会检查这一点。这也意味着 write(2) 可能会失败,即使在写入日志文件时也是如此。您是尝试记录记录器生成的错误还是应该放弃?

于 2010-04-03T06:30:50.803 回答
1

当一个文件描述符被复制时,就像它在fd_copyordup2函数中一样,您最终会得到多个文件描述符引用同一事物(即struct file在内核中相同)。关闭其中一个只会减少其引用计数。除非是最后一次关闭,否则不会对基础对象执行任何操作。因此,诸如EINTR和之类的条件EIO是不可能的。

于 2010-04-03T17:11:12.207 回答
0

另一种可能性是,他的函数仅在一个应用程序(或一个应用程序的一部分)中使用,该应用程序已经做了一些事情来确保调用不会被信号中断。如果您不打算对信号做任何重要的事情,那么您不必对它们做出响应,并且将它们全部屏蔽掉可能是有意义的,而不是将每个阻塞系统调用包装在 EINTR 重试中。当然除了会杀死你的那些,所以如果你通过退出来处理它,那么 SIGKILL 和经常 SIGPIPE 以及 SIGSEGV 和类似的致命错误在任何情况下都不会传递到正确的用户空间应用程序。

无论如何,如果他所说的只是安全性,那么他很可能不必重试close。如果关闭 EIO 失败,那么他将无法重试,这将是永久失败。因此,成功的程序的正确性是不必要的close。很可能他的程序的正确性也没有必要close在 EINTR 上重试。

通常你希望你的程序尽最大努力成功,这意味着在 EINTR 上重试。但这是与安全性不同的问题。如果您的程序被设计为使某些功能因任何原因而失败不是安全漏洞,那么特别是它恰好因 EINTR 而不是由于永久原因而失败的事实就不是缺陷。众所周知,DJB 相当固执己见,所以如果他证明了他不需要重试的某种理由,因此我不会感到惊讶,因此即使这样做会让他的程序成功在当前可能失败的某些情况下刷新手柄(例如kill在关键时刻由用户明确发送无害信号)。

编辑:在我看来,在某些条件下重试 EINTR 本身可能是一个安全漏洞。它为该部分代码引入了一种新行为:它可以无限循环以响应信号泛滥,以前它会尝试close然后返回。我不确定这是否会导致 qmail 出现任何问题(毕竟,close它本身并不能保证它会在多长时间内返回)。但是,如果在一次尝试后放弃确实使代码更易于分析,那么它可能是一个明智的举措。或不。

您可能认为重试可以防止 DoS 缺陷,其中信号会导致虚假故障。但是重试允许另一个(更困难的)DoS 缺陷,其中信号泛滥会导致无限期的停顿。就二进制“这个应用程序可以 DoSed 吗?”而言,这是 DJB 在编写 qmail 和 djbdns 时感兴趣的绝对安全问题,没有区别。如果某件事可以发生一次,那么通常这意味着它可以发生多次。

于 2010-04-03T08:53:16.777 回答
0

EINTR没有您明确要求,只有破损的 unices 才会返回。signal()启用可重启系统调用(“BSD 风格”)的合理语义。在具有 sysv 语义(中断信号)的系统上构建程序时,您应该始终将调用替换为signal()调用,您可以根据它是否不存在bsd_signal()来定义自己。sigaction()

进一步值得注意的是,除非您安装了信号处理程序,否则系统不会在收到信号返回。EINTR如果保留默认操作,或者如果信号设置为无操作,则系统调用不可能被中断。

于 2010-07-26T12:40:03.217 回答