【问题标题】:Should my library handle SIGSEGV on bad pointer input?我的库应该在错误的指针输入上处理 SIGSEGV 吗?
【发布时间】:2012-02-17 03:45:36
【问题描述】:

我正在编写一个以 FILE * 指针作为输入的小型库。

如果我立即检查这个 FILE * 指针并发现它导致了段错误,那么处理信号、设置 errno 并优雅退出是否更正确;还是什么都不做,使用调用者安装的信号处理程序(如果有的话)?

流行的智慧似乎是“图书馆不应该导致崩溃”。但我的想法是,既然这个特定的信号肯定是调用者的错,那么我不应该试图向他隐藏那个信息。他可能安装了自己的处理程序,以自己的方式对问题做出反应。可以使用 errno 检索相同的信息,但设置 SIGSEGV 的默认处置是有充分理由的,并且通过强制调用者处理他的错误,或通过崩溃并保护他免受进一步损害,向上传递信号尊重这一理念.

您是否同意这种分析,或者您认为在这种情况下处理 SIGSEGV 有什么令人信服的理由?

【问题讨论】:

  • AFAIK 标准 C 库不处理内部崩溃,为什么你的库应该?但是我想这取决于你打算用你的库做什么。 (对 FILE * 的检查如何导致 SIGSEV 顺便说一句?只是好奇)

标签: c error-handling shared-libraries signals errno


【解决方案1】:

接管处理程序不是图书馆业务,除非明确要求,否则我会说这有点冒犯他们。为了最大限度地减少崩溃,库可能会在一定程度上验证他们的输入。除此之外:垃圾进——垃圾出。

【讨论】:

    【解决方案2】:

    流行的智慧似乎是“图书馆不应该导致崩溃。”

    我不知道你从哪里得到的——如果他们传递了一个无效的指针,你应该崩溃。任何图书馆都会。

    【讨论】:

    • 有趣的是,虽然您的陈述对于大多数函数都是正确的,但也有一些例外。例如,write 和 getline 将分别以 EBADF 和 EINVAL 退出。
    • 抱歉,这是写入的 EFAULT,尽管它也会检查 EBADF。
    • @User123abc,你确定你在谈论任何无效指针,而不仅仅是空指针?
    【解决方案3】:

    我认为检查NULL 指针的特殊情况是合理的。但除此之外,如果他们通过垃圾,他们就违反了函数的合同,他们会崩溃。

    【讨论】:

      【解决方案4】:

      这是一个主观问题,可能不适合 SO,但我会提出我的意见:

      这样想:如果你有一个函数接受一个以 nul 终止的char * 字符串并且被记录为这样,并且调用者传递了一个没有 nul 终止符的字符串,你是否应该捕捉到信号并拍打调用者在手腕上?还是应该让它崩溃,让糟糕的程序员使用你的 API 修复他/她的代码?

      如果您的代码采用FILE * 指针,并且您的文档说“传递任何打开的FILE *”,并且它们传递了一个关闭或无效的FILE * 对象,那么它们就违反了合同。检查这种情况会减慢正确使用您的库的人的代码以适应不使用您的库的人,而让它崩溃将使阅读文档并编写好的代码的人尽可能快地保持代码。

      您是否希望传递无效FILE * 指针的人检查并正确处理错误?还是他们更可能盲目地继续,导致稍后再次崩溃,在这种情况下处理此崩溃可能只是掩饰错误?

      【讨论】:

      • 此外,没有理由期望它甚至可以捕获传递无效FILE * 的情况。它完全取决于系统的实现和内部行为。无效的FILE * 很容易看起来“有效”,但却做了一些完全虚假的事情,甚至会破坏您的库代码并阻止其检查工作(在没有内存保护的系统上)。
      【解决方案5】:

      内核 如果你给它们一个错误的指针,它们不应该崩溃,但库可能应该。这并不意味着您不应该进行错误检查。一个好的程序在遇到不合理的坏数据时会立即死亡。我宁愿使用 assert(f != NULL) 调用 bail 库,而不是仅仅滚动并最终取消引用 NULL 指针。

      【讨论】:

        【解决方案6】:

        对不起,那些说库应该崩溃的人只是懒惰(也许是考虑到时间,以及开发工作)。库是函数的集合。库代码不应该“只是崩溃”,就像软件中的其他功能应该“只是崩溃”一样。

        当然,如果通常会涉及多种语言或(相对)外来语言功能(如异常),库可能会在如何跨 API 边界传递错误方面存在一些问题,但没有什么 TOO 有什么特别之处那。实际上,这只是编写库的一部分负担,而不是应用程序内代码。

        除非您确实无法证明开销是合理的,否则系统之间的每个接口都应实施健全性检查,或者更好的是,按合同设计,以防止安全问题和错误。

        有很多方法可以解决这个问题,您可能应该做的事情,按照优先顺序,是以下之一:

        1. 在库中使用支持异常(或者更好的是,按合同设计)的语言,并在合同上抛出异常或允许合同失败。

        2. 提供错误处理信号/槽或挂钩/回调机制,并调用任何已注册的处理程序。要求在初始化库时,至少注册一个错误处理程序。

        3. 支持在每个可能因任何原因而失败的函数中返回一些错误代码。但这是从 C(相对于 C++)时代开始的旧的、相对疯狂的做事方式。

        4. 设置一些全局“发生错误标志”,并允许在调用前清除该标志。这也是旧的,并且完全很疯狂,主要是因为它将错误状态维护负担转移给调用者,并且在线程方面不安全。

        【讨论】:

          猜你喜欢
          • 2019-05-01
          • 1970-01-01
          • 2013-10-05
          • 1970-01-01
          • 2011-12-01
          • 2023-04-01
          • 1970-01-01
          • 2019-10-13
          • 2016-09-05
          相关资源
          最近更新 更多