【问题标题】:Undefined behavior when calling FD_ISSET调用 FD_ISSET 时未定义的行为
【发布时间】:2021-09-24 13:39:40
【问题描述】:

我正在尝试诊断一个带有 Heisenbug 的程序。使用 gdb 和 Ghidra 的组合,我已经能够追踪到特定部分的崩溃。这是我的代码的要点:

FD_ZERO(&readfds);
FD_SET(sock1, &readfds);
max_fd = sock1;

if ( some_condition ) {
    FD_SET(sock2, &readfds);
    if ( sock2 > max_fd ) {
        max_fd = sock2;
    }
}

if ( select(max_fd+1, &readfds, NULL, NULL, &timer) == -1 ) {
    goto error;
}

if ( FD_ISSET(sock1, &readfds) ) {
    ...
}

if ( FD_ISSET(sock2, &readfds) ) {
    ...
}

我已经能够将崩溃范围缩小到最后一个 FD_ISSET 宏的扩展。具体来说,它调用 __fdelt_chk 最终导致我的 shell 报告

*** buffer overflow detected ***: terminated

但是,如果我将代码更改为

bool using_sock2 = false;

...

if ( some_condition ) {
    using_sock2 = true;
    ...
}

...

if ( using_sock2 && FD_ISSET(sock2, &readfds) ) {
    ...
}

问题消失了。

显然,我调用了某种未定义的行为。但是,我查看了手册页,没有看到任何似乎相关的警告/要求。究竟是什么导致了这次崩溃?

编辑:在 gdb 或 valgrind 下运行程序会使错误消失。我无法找到崩溃源的唯一方法是正常运行程序,然后从另一个终端附加 gdb。

【问题讨论】:

  • 请提供 MRE (minimal reproducible example) 以证明不当行为。
  • 另外,尝试通过 valgrind 运行您的代码。如果你对内存管理不善,它会告诉你在哪里。
  • 要么 sock2 无效,要么 readfds 无效。 sock2 的检查正在关闭。或者 readfds 超出范围或(如果在堆上分配)被释放。仅用于测试 - 将 sock2 和 readfds 声明为静态,看看会发生什么。
  • 啊,@moi 是对的。我设法用 strace 附加,发现 close 被调用 sock2,然后 sock2 被设置为 -1。由于“由错字引起”而投票结束(足够接近)。

标签: c select undefined-behavior


【解决方案1】:

使用 fd_set/FD_SET/FD_ISSET 需要注意的一点是这些集合是固定大小的——fd_set 中只有足够的空间用于 FD_SETSIZE 文件描述符。在 Linux 上(你没有说你正在使用什么操作系统)FD_SETSIZE 是 1024,它与 1024 文件描述符的默认 ulimit 匹配,所以你不会看到问题,除非你已经为你的进程提高了 ulimit(1024 是只是一个软限制——硬限制实际上要大得多)。

如果可能是这种情况,您应该始终检查以确保fd < FD_SETSIZE调用 FD_SET。比如:

FD_ZERO(&readfds);
if (sock1 >= FD_SETSIZE) {
    error("too many file descriptors!");
    abort(); }
FD_SET(sock1, &readfds);
max_fd = sock1;

if ( some_condition ) {
    if (sock2 >= FD_SETSIZE) {
        error("too many file descriptors!");
        abort(); }
    FD_SET(sock2, &readfds);
    if ( sock2 > max_fd ) {
        max_fd = sock2;
    }
}

您可能还想确保您的文件描述符都不是其他无效值(例如可能来自某些早期系统调用中的错误的 -1),因为这同样会导致越界访问如果您尝试将其与 FD_SET 或 FD_ISSET 一起使用,则在 fd_set 中

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-10-30
    • 2019-08-27
    • 2018-08-21
    • 2013-04-19
    • 1970-01-01
    • 2012-11-25
    • 1970-01-01
    相关资源
    最近更新 更多