Valgrind使用asprintf报告内存泄漏

时间:2019-02-20 07:56:59

标签: c linux valgrind

这是一段代码,它基于简单的结构在套接字上写入HTTP响应

void write_response(request *req, response *resp, int socket) {
  char *raw_resp;
  int bytes = 0;

  asprintf(&raw_resp, "HTTP/1.1 %d %s\r\n", resp->code, resp->reason_phrase);

  bytes += strlen(raw_resp);
  for (int i = 0; i < resp->header_count; i++) {
    asprintf(&raw_resp, "%s%s", raw_resp, resp->headers[i]);
    bytes += strlen(resp->headers[i]);
  }

  if (resp->content != NULL) {
    asprintf(&raw_resp, "%s\r\n", raw_resp);
    raw_resp = realloc(raw_resp, bytes + 2 + resp->content->size);
    memcpy(&raw_resp[strlen(raw_resp)], resp->content->data,
           resp->content->size);
    bytes += (resp->content->size + 2);
  }
  write(socket, raw_resp, bytes);
  free(raw_resp);
}

基本上,它首先添加HTTP请求行,然后添加标头,最后添加正文(如果需要)。

但是,valgrind在前两个asprintf上报告了Invalid free() / delete / delete[] / realloc()18 bytes in 1 blocks are definitely lost in loss record 2 of 4(内存泄漏),但是奇怪的是在第三个asprintf上报告了这个错误。

我使用asprintf对吗?

3 个答案:

答案 0 :(得分:5)

这是手册页所说的:

  

函数asprintf()....分配一个字符串...

及以后

  

该指针应该传递给free(3)以便在不再需要时释放分配的存储。

每个新呼叫都会导致新的分配。您似乎没有释放先前分配的字符串。

答案 1 :(得分:3)

您的代码未free asprintf分配的所有字符串。考虑到如何使用asprintf执行动态字符串连接,解决此问题有点麻烦。请注意,您也不处理内存分配失败。您可以使用asprintf返回值来检测并更新bytes,而无需额外的strlen()调用。

/* return the number of bytes written or -1 in case of error */
int write_response(request *req, response *resp, int socket) {
    char *raw_resp, *new_p;
    int bytes;

    bytes = asprintf(&raw_resp, "HTTP/1.1 %d %s\r\n", resp->code, resp->reason_phrase);
    if (bytes < 0)
        return -1;

    for (int i = 0; i < resp->header_count; i++) {
        bytes = asprintf(&new_p, "%s%s", raw_resp, resp->headers[i]);
        free(raw_resp);
        raw_resp = newp;
        if (bytes < 0)
            return -1;
    }

    if (resp->content != NULL) {
        bytes = asprintf(&new_p, "%s\r\n", raw_resp);
        free(raw_resp);
        raw_resp = newp;
        if (bytes < 0)
            return -1;
        new_p = realloc(raw_resp, bytes + resp->content->size);
        if (new_p == NULL) {
            free(raw_resp);
            return -1;
        }
        raw_resp = new_p;
        memcpy(raw_resp + bytes, resp->content->data, resp->content->size);
        bytes += resp->content->size;
    }
    bytes = write(socket, raw_resp, bytes);
    free(raw_resp);
    return bytes;
}

备注:

  • 使用asprintf执行带分配的字符串连接效率低下,只需使用strlenreallocmemcpy

  • asprintf()是非标准的,并非在所有平台上都可用。

  • 除非要求您发出对write的单个调用,否则单独写内容可能会更有效,以避免额外调用realloc()可能会占用大量内存

  • 在初始阶段使用snprintfstrlen计算标头的长度并将标头的空间直接分配为完整大小可能更有效甚至使用本地数组在合理阈值(4K)以下时进行分配。

这是修改后的版本:

int write_response(request *req, response *resp, int socket) {
    char buffer[4096];
    char *raw_resp, *allocated = NULL;
    int bytes, pos;

    bytes = snprintf(NULL, 0, "HTTP/1.1 %d %s\r\n", resp->code, resp->reason_phrase);
    for (int i = 0; i < resp->header_count; i++)
        bytes += strlen(resp->headers[i]);
    if (resp->content != NULL)
        bytes += 2 + resp->content->size;

    /* need an extra byte for `snprintf` null terminator 
       if no headers and no contents */
    if (bytes < sizeof(buffer)) {
        raw_resp = buffer;
    } else {
        raw_resp = allocated = malloc(bytes + 1): 
        if (raw_resp == NULL)
            return -1;
    }
    pos = snprintf(raw_resp, bytes, "HTTP/1.1 %d %s\r\n", resp->code, resp->reason_phrase);
    for (int i = 0; i < resp->header_count; i++) {
        int len = strlen(resp->headers[i]);
        memcpy(raw_resp + pos, resp->headers[i], len);
        pos += len;
    }

    if (resp->content != NULL) {
        raw_resp[pos++] = '\r';
        raw_resp[pos++] = '\n';
        memcpy(raw_resp + pos, resp->content->data, resp->content->size);
        pos += resp->content->size;
    }
    bytes = write(socket, raw_resp, bytes);
    free(allocated);
    return bytes;
}

答案 2 :(得分:1)

您缺少免费活动,除了第一次以外,您不需要 asprintf

可以是:

void write_response(request *req, response *resp, int socket) {
  char * buff;
  int len;

  len = asprintf(&buff, "HTTP/1.1 %d %s\r\n", resp->code, resp->reason_phrase);
  if (len == -1)
    .. error management

  for (int i = 0; i < resp->header_count; i++) {
    size_t hlen = strlen(resp->headers[i]);

    buff = realloc(buff, len + hlen + 1);
    if (buff == NULL)
      ... error management
    strcpy(buff + len, resp->headers[i]);
    len += hlen;
  }

  if (resp->content != NULL) {
    buff = realloc(buff, len + 2 + resp->content->size);
    if (buff == NULL)
      ... error management
    buff[len] = '\r';
    buff[len + 1] = '\n';
    memcpy(len + 2, resp->content->data, resp->content->size);
    len += resp->content->size + 2;
  }
  if (write(socket, buff, len) != len)
    ... error management
  free(buff);
}