2

背景

我正在开发一个程序,其中一个线程从主线程中分离出来,解析电子邮件日志并按 QID 组织每个条目,QID 是每条消息唯一的 ID。

问题

该程序一直在工作,直到我重新组织它,而不是首先将整个日志文件加载到内存中,而是在读取日志文件时开始解析每一行。该程序将在下面显示的代码片段中进行 SIGABRT(稍微简化但演示了相同的问题)。

调试结果

gdb 将声称它是(取决于运行)realloc/free 无效大小或 realloc/free 损坏内存,而 valgrind 始终声称一个非空终止数组导致运行溢出并损坏堆(我认为这更有可能是案例但找不到问题)。(如果您希望我分享 valgrind 或 gdb 的原始输出,请告诉我)。

void *parseMessageAndMatch(void *params) {
    // get params passed in from parent thread
    parse_params *rp = (parse_params *)params;

    // len0: length of log dump for current QID
    // len1: length of log dump after current buffer is appended
    int len0, len1;
    // tmp char pointer for log dump
    char *tmp;

    // 1. increase/allocate memory for log dump and append line buffer
    // get length of current log dump (0 if NULL)
    // dump is of type char* and contains the log lines pertaining to a certain QID
    len0 = (!msgs.msgs[rp->index].dump) ? 0 : strlen(msgs.msgs[rp->index].dump);
    // get length of log dump after current line buffer is appended
    // rp->len contains the strlen of the line buffer
    len1 = len0 + rp->len + 1;

    // allocate space for tmp log dump
    // lock becaus msgs is a global variable and there is more than one thread
    pthread_mutex_lock(rp->mutex);
    if ((tmp = realloc(msgs.msgs[rp->index].dump, sizeof(*tmp)*len1)) == NULL) {
        printf("Cannot allocate memory for log dump.\n");
        exit(-1);
    }
    // update pointer
    msgs.msgs[rp->index].dump = tmp;
    // set tmp to null
    tmp = NULL;
    // if original buffer was empty, zero it out for safety
    if (len0 == 0)
        bzero(msgs.msgs[rp->index].dump, sizeof(char)*len1);

    // rp->line is malloc'ed then bzero'ed and strcpy'ed to contain the log line read from file
    strcat(msgs.msgs[rp->index].dump, rp->line);
    msgs.msgs[rp->index].dump[len1-1] = '\0'; // add null terminator
    pthread_mutex_unlock(rp->mutex);

    // more parsing code is here but was commented out during my debug process
    // so I have omitted it from this code snippet

    // free line buffer
    free(rp->line);
    // set to null to prevent double free
    rp->line = NULL;
    // free params
    free(rp);
    // prevent double free
    rp = NULL;
}

编辑

在下面的答案中进行了更正。

4

1 回答 1

2
msgs.msgs[rp->index].dump[len1] = '\0'; // add null terminator

realloc(msgs.msgs[rp->index].dump, sizeof(*tmp)*len1)

是越界写入。最后一个有效索引是len1 - 1


修复后代码仍在中止,可能是

// rp->line is malloc'ed then bzero'ed and strcpy'ed to contain the log line read from file
strcat(msgs.msgs[rp->index].dump, rp->line);

写在分配strcpyrp->line缓冲区之外。我可以在函数中看到的唯一其他候选人是

msgs.msgs[rp->index].dump

如果这不是 0 终止的,则strlen可以在分配的缓冲区外运行,strcat然后可以再次在分配的内存之外写入。后者可以通过设置来避免

msgs.msgs[rp->index].dump[len0] = 0;

在 realloc 之后和strcat.


释放后设置rpNULL

free(rp);
// prevent double free
rp = NULL;

是没有意义的,因为rp是一个局部变量,然后函数立即返回。传入的指针params仍然保存调用者中的旧地址(严格来说,它的值变得不确定,但实际上,位不会改变)。这开启了调用者认为指针仍然指向有效内存的可能性。

于 2013-04-04T20:14:06.180 回答