为何在iOS14.2上传入536872413这类大值调用FD_SET会触发崩溃?
FD_SET调用崩溃问题分析
根本原因
- Darwin(iOS)平台下,
fd_set结构默认最大支持的文件描述符数值为FD_SETSIZE=1024,仅允许操作数值小于1024的fd。 - 你传入的
_fd值为536872413,远超过1024的上限:这是因为socket创建失败后未做合法性校验,直接将初始值为INVALID_SOCKET=-1的fd传入,-1强制转换为无符号长整型后会变成超大数值。 - 在
__darwin_fd_set中计算fds_bits数组下标时,超大的fd值得到的下标远远超出fd_set的数组长度,触发栈内存越界访问,最终导致EXC_BAD_ACCESS崩溃。
两次FD_SET调用表现不同的原因
两次调用的fd值相同,但操作的fd_set结构体的栈基地址不同:
- 第554行操作的是
readfds,越界访问的地址刚好落在当前栈帧的合法可写范围内(比如覆盖了栈上其他未使用的内存或局部变量),没有触发内存访问异常,只是静默造成了内存污染。 - 第556行操作的是
exceptfds,基地址不同的情况下,越界后的目标地址刚好落在未分配/不可写的内存区域,直接触发段错误崩溃。
注意:第一次调用实际上已经发生了非法越界,只是没有触发崩溃,属于隐藏的内存错误。
修复方案
- 所有使用socket的逻辑必须先校验fd合法性,等于
INVALID_SOCKET时直接返回错误,禁止进入后续FD_SET逻辑。 - 替换
select+FD_SET的IO多路复用方案,改用poll、kqueue(iOS平台原生支持)等无fd数值上限的接口。 - 如果必须保留select逻辑,调用FD_SET前必须判断fd数值小于
FD_SETSIZE,避免越界。
内容的提问来源于stack exchange,提问作者Berte Colin
相关产品推荐
相关产品推荐

