Linux FIFO客户端服务器程序malloc断言错误修复咨询
问题
我正在编写一款基于Linux FIFO的客户端与服务器通信程序,功能为客户端发送消息后,服务器将消息打印到控制台。消息格式设计为[客户端PID/消息长度:消息内容],例如客户端PID为4172时,消息为[4172/17:Hey, how are you?],服务器解析后仅打印消息内容,以此实现动态内存分配避免内存浪费。
程序首次运行正常,客户端与服务器可正常通信并打印消息,但再次发送消息时触发如下错误:
server.out: malloc.c:2617: sysmalloc: Assertion (old_top == initial_t… …op (av) && old_size == 0) || ((unsigned long) (old_size) >= MINSIZE && prev_inuse (old_top) && ((unsigned long) old_end & (pagesize - 1)) == 0)' failed. Aborted(core dumped).
服务器相关代码如下:
// delimCheckBuffer是大小为1的数组 void readUntilColon(int fd, char* buffer, size_t index) { while(read(fd, delimCheckBuffer, 1) > 0) { if(delimCheckBuffer[0] == ':') { buffer[index] = ':'; break; } buffer[index] = delimCheckBuffer[0]; ++index; } } char* messageReconstruct(int fdOfFIFO) { char* message = calloc(15, sizeof(char)); //考虑了: / [ ]这4个符号,假设PID和消息长度最多分别为7位和4位 long NumOfCharsInMessage; size_t index = 0; readUntilColon(fdOfFIFO, message, 1); NumOfCharsInMessage = findNumOfCharsInMessage(message); free(message); message = calloc(NumOfCharsInMessage, sizeof(char)); while(read(fdOfFIFO, delimCheckBuffer, 1) > 0) { if(delimCheckBuffer[0] == ']') break; message[index] = delimCheckBuffer[0]; ++index; } return message; }
使用Valgrind调试,提示readUntilColon()函数中buffer[index] = delimCheckBuffer[0];存在1字节的无效写入,但无法理解问题所在,希望有人能解释原因并提供解决方案。
问题分析与解决方案
原因分析
- 缓冲区越界写入:
- 初始分配的
message缓冲区是15字节(calloc自动初始化0),但readUntilColon传入的起始index是1,后续循环中index持续递增。当解析的PID+长度部分总长度超过13字节时(从index=1开始,buffer[14]是第15个字节),就会写入到缓冲区之外,直接破坏堆内存结构。首次运行可能刚好没触发越界,但第二次发送时,被破坏的堆内存会导致后续内存分配操作触发malloc的断言检查。 - 比如7位PID+1个
/+4位长度+1个:,总长度是13,写到index=13时是合法的,但如果PID/长度更长,就会写到buffer[14]之后,造成越界。
- 初始分配的
- 字符串未终止:
readUntilColon没有为缓冲区添加字符串终止符\0,后续findNumOfCharsInMessage处理时可能读取到内存垃圾,导致计算出的消息长度错误,进而引发后续内存分配和写入的问题。
修复方案
方案1:修复缓冲区越界与字符串终止问题
修改readUntilColon函数,增加缓冲区长度限制,确保写入不越界,同时保证字符串始终以\0结尾:
// 添加bufferLen参数,限制写入范围 void readUntilColon(int fd, char* buffer, size_t index, size_t bufferLen) { while(read(fd, delimCheckBuffer, 1) > 0) { // 预留1字节给终止符,避免越界 if (index >= bufferLen - 1) { break; } if(delimCheckBuffer[0] == ':') { buffer[index] = ':'; index++; buffer[index] = '\0'; break; } buffer[index] = delimCheckBuffer[0]; index++; } // 确保字符串始终终止 buffer[index] = '\0'; } char* messageReconstruct(int fdOfFIFO) { const size_t INIT_BUFFER_SIZE = 15; char* message = calloc(INIT_BUFFER_SIZE, sizeof(char)); long NumOfCharsInMessage; size_t index = 0; readUntilColon(fdOfFIFO, message, 1, INIT_BUFFER_SIZE); NumOfCharsInMessage = findNumOfCharsInMessage(message); free(message); // 为字符串终止符额外分配1字节 message = calloc(NumOfCharsInMessage + 1, sizeof(char)); if (!message) return NULL; index = 0; while(read(fdOfFIFO, delimCheckBuffer, 1) > 0) { if(delimCheckBuffer[0] == ']') break; // 检查是否超过消息长度 if (index >= NumOfCharsInMessage) { break; } message[index] = delimCheckBuffer[0]; index++; } message[index] = '\0'; return message; }
方案2:优化解析逻辑,跳过中间缓冲区
直接读取消息格式中的长度部分,无需先分配固定缓冲区,从根源避免越界问题:
#include <ctype.h> char* messageReconstruct(int fdOfFIFO) { char ch; long msg_len = 0; // 跳过'['和PID部分,直到读到'/' while(read(fdOfFIFO, &ch, 1) > 0 && ch != '/'); // 读取消息长度数字 while(read(fdOfFIFO, &ch, 1) > 0 && isdigit(ch)) { msg_len = msg_len * 10 + (ch - '0'); } // 跳过':',处理格式错误 if (ch != ':') { return NULL; } // 分配足够内存(包含终止符) char* message = calloc(msg_len + 1, sizeof(char)); if (!message) return NULL; // 读取消息内容,直到']'或达到指定长度 size_t idx = 0; while(read(fdOfFIFO, &ch, 1) > 0 && ch != ']' && idx < msg_len) { message[idx++] = ch; } message[idx] = '\0'; return message; }
关键注意事项
- 所有字符串操作必须确保以
\0结尾,否则后续字符串处理函数会读取内存垃圾。 - 动态分配内存时,要为字符串终止符额外分配1字节空间。
- 读取FIFO时需处理格式异常情况,避免无限循环或越界。
内容的提问来源于stack exchange,提问作者mathInferno
相关产品推荐
相关产品推荐

