基于epoll的IPv4 C服务器接收多条客户端消息时栈溢出排查
栈溢出问题定位分析
大概率的问题方向
stack-buffer-overflow本质是栈上缓冲区被写入超出其分配大小的数据,结合你单条消息正常、多条触发的情况,重点查以下几点:
1. TCP粘包未处理,导致单次读取超栈缓存容量
TCP是流式协议,客户端连续发多条消息时,操作系统可能把它们合并成一个TCP段发送。如果你的代码里接收缓冲区是按单条消息大小(1字节类型+32字节内容+1字节换行=34字节)在栈上分配的,且没有检查实际读取长度是否超过缓冲区大小,一旦收到粘包(比如两条消息共68字节),直接写入栈缓存就会越界。
比如这种写法肯定会出问题:
char recv_buf[34]; ssize_t read_len = read(client_fd, recv_buf, sizeof(recv_buf)); // 直接处理recv_buf,没管read_len是否大于34
2. 解析状态未重置,后续消息处理越界
如果你的消息解析逻辑依赖栈上的状态变量(比如当前已读取的字节数、解析偏移),处理完一条消息后没把这些变量重置,下一条消息会基于之前的偏移继续写入,直接超出栈缓存范围。
比如:
// 栈上的偏移变量,处理完消息没重置 int current_pos = 0; while ((read_len = read(client_fd, recv_buf + current_pos, sizeof(recv_buf)-current_pos)) > 0) { current_pos += read_len; // 解析出一条消息后,没把current_pos清零,继续累加 }
3. 响应构造时栈缓存不足
服务器返回的响应包含原类型、uint32_t IP、uint16_t端口和原消息,总大小是1+4+2+34=41字节。如果响应缓冲区是栈上分配的,且代码里错误地写入了超出这个大小的数据(比如原消息部分没截断、拼接时多写了字节),多次处理后也会触发栈溢出。
排查实操步骤
- 先核对栈缓冲区的定义:接收和响应的栈缓存是否留足余量?或者干脆改用堆内存(
malloc/free)分配,避免栈大小的限制。 - 加强制边界检查:每次
read()后必须判断读取长度是否超过缓冲区剩余空间,超过的话要么处理部分数据,要么扩容。 - 检查状态变量重置:每条消息处理完成后,所有栈上的偏移、计数等状态必须重置为初始值。
- 用调试工具定位:开启编译器栈保护(
gcc -g -fstack-protector-all),然后用gdb跑,触发错误后用bt命令看调用栈,直接找到溢出的代码行:gcc -g -fstack-protector-all your_server_code.c -o server gdb ./server run # 触发错误后输入bt查看调用栈
内容的提问来源于stack exchange,提问作者Gyro
相关产品推荐
相关产品推荐

