skip to Main Content

I am trying to create C-based HTML client. I`m quite new to C memory leaks debugging, so please see such (simplified) code of HTML response parser:

//=============
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int str_index_of(const char *a, char *b) {
    char *offset = (char *) strstr(a, b);
    return offset - a;
}

char *get_until(char *haystack, char *until) {
    int offset = str_index_of(haystack, until);
    return strndup(haystack, offset);
}
char *str_replace(const char *search, const char *replace, const char *subject) {
    size_t search_size = strlen(search);
    size_t replace_size = strlen(replace);
    size_t subject_size = strlen(subject);

    // Count the number of occurrences of search in subject
    size_t count = 0;
    const char *p = subject;
    while ((p = strstr(p, search)) != NULL) {
        count++;
        p += search_size;
    }

    // Allocate memory for the new string
    size_t new_size = subject_size + count * (replace_size - search_size);
    char *new_subject = (char *) calloc(new_size + 1, sizeof(char));

    // Replace search with replace in subject
    const char *old = subject;
    char *new_p = new_subject;
    while ((p = strstr(old, search)) != NULL) {
        size_t old_size = p - old;
        memcpy(new_p, old, old_size);
        new_p += old_size;
        memcpy(new_p, replace, replace_size);
        new_p += replace_size;
        old = p + search_size;
    }
    strcpy(new_p, old);

    return new_subject;
}

void getStatusCode(char *response, char *status_code) {
    char *status_line = get_until(response, "rn");
    status_line = str_replace("HTTP/1.1 ", "", status_line);
    char *code = strndup(status_line, 4);
    code = str_replace(" ", "", code);
    status_code = strcpy(status_code, code);

    free(code);
    free(status_line);
}

void mockRequest(char *status_code) {
    char *res = "HTTP/1.1 200 OKn"
                "Server: nginxn"
                "Date: Fri, 07 Apr 2023 18:44:22 GMTn"
                "Content-Type: application/json; charset=utf-8n"
                "Connection: closen"
                "Access-Control-Allow-Origin: *n"
                "n"
                "73n"
                "some-body-content-heren"
                "0";
    char *status_line = NULL;
    getStatusCode(res, status_code);
    free(status_line);
}



int main() {
    char status_code[4];
    mockRequest(status_code);
    printf("STATUS: %s", status_code);
    return 0;
}

//=============

I use address sanitizer. The sanitizer reports:

==8055==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 190 byte(s) in 1 object(s) allocated from:
    #0 0x7f31ccabc808 in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cc:144
    #1 0x7f31cca25243 in __interceptor_strndup ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:379
    #2 0x55d0abad935c in get_until /home/user/CLionProjects/untitled/main.c:12
    #3 0x55d0abad94e0 in getStatusCode /home/user/CLionProjects/untitled/main.c:48
    #4 0x55d0abad959d in mockRequest /home/user/CLionProjects/untitled/main.c:70
    #5 0x55d0abad963f in main /home/user/CLionProjects/untitled/main.c:78
    #6 0x7f31cc7e1082 in __libc_start_main ../csu/libc-start.c:308

Direct leak of 5 byte(s) in 1 object(s) allocated from:
    #0 0x7f31ccabc808 in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cc:144
    #1 0x7f31cca25243 in __interceptor_strndup ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:379
    #2 0x55d0abad9513 in getStatusCode /home/user/CLionProjects/untitled/main.c:50
    #3 0x55d0abad959d in mockRequest /home/user/CLionProjects/untitled/main.c:70
    #4 0x55d0abad963f in main /home/user/CLionProjects/untitled/main.c:78
    #5 0x7f31cc7e1082 in __libc_start_main ../csu/libc-start.c:308

SUMMARY: AddressSanitizer: 195 byte(s) leaked in 2 allocation(s).

The leaks are when calling strndup, if I understand well.
I completely have no ideas about where the leaks may be located.
Your advice?

2

Answers


  1. void getStatusCode(char *response, char *status_code) {
        char *status_line = get_until(response, "rn");
        status_line = str_replace("HTTP/1.1 ", "", status_line);
        char *code = strndup(status_line, 4);
        code = str_replace(" ", "", code);
        status_code = strcpy(status_code, code);
    
        free(code);
        free(status_line);
    }
    

    Notice that the variable code stores the result of strndup but in the very next line code is reassigned to the result of str_replace. The memory allocated by strndup is never freed, and cannot be, because there is no longer a pointer to it.

    The same issue exists with status_line.

    To avoid this in the future, ensure that every memory allocation maintains a pointer to it that can be freed.

    char *a = memory_allocating_function();
    char *b = other_memory_allocating_function(a);
    
    // ...
    
    free(a);
    free(b);
    
    Login or Signup to reply.
  2. status_line = str_replace("HTTP/1.1 ", "", status_line);
    

    leaks the original status_line, because str_replace() allocates new memory for the copy with the replacement. You need to assign the result to a new variable, so you can free the original.

    void getStatusCode(char *response, char *status_code) {
        char *status_line = get_until(response, "rn");
        char *clean_status_line = str_replace("HTTP/1.1 ", "", status_line);
        free(status_line);
    
        char *code = strndup(clean_status_line, 4);
        free(clean_status_line);
    
        char *clean_code = str_replace(" ", "", code);
        free(code);
    
        strcpy(status_code, clean_code);
        free(clean_code);
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search