调用 FD_ISSET 时未定义的行为

问题描述 投票:0回答:1

我有一个带有 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。

c undefined-behavior posix-select
1个回答
1
投票

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

如果出现这种情况,您应该始终检查以确保在调用 FD_SET 之前fd < FD_SETSIZE

。比如:
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_SET 或 FD_ISSET 一起使用
    

© www.soinside.com 2019 - 2024. All rights reserved.