You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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字节的无效写入,但无法理解问题所在,希望有人能解释原因并提供解决方案。


问题分析与解决方案

原因分析

  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]之后,造成越界。
  2. 字符串未终止:
    • 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 03:24:59