Search code examples
cpointerssplitc-stringsrealloc

Invalid write,read when allocating memory in c


I have this function split_text that takes in a char array text and attempts to split it into sub-arrays using the token as the split value, res is an array of pointers to each of the sub-strings

int split_text(char * text, char * token, char ** res)
{
    int x =0;
    char * tmp = strtok(text , token);
    while (tmp != NULL)
    {
        res[x] = malloc(1024);
        strcpy(res[x], tmp);
        x++;
        *res = realloc(*res, sizeof(char*)*(x+1));
        tmp = strtok(NULL , token);
    }
    return x;
}

int main()
{
    char text[] = "Hello world this is elliot";

    char ** ptr = (char**)malloc(sizeof(char*));

    int x = split_text(text, " ", ptr);

    
    for (int i =0; i< x; i++)
        printf("%s\n", ptr[i]);
    
    return 0;
}

when compiling this code it works fine as in it displays the sub-strings each on a new line but when ran with valgrind, I get many Invalid write of size 8 and Invalid read of size 8

Here is the full valgrind output

==74210== Memcheck, a memory error detector
==74210== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==74210== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info
==74210== Command: ./a.out
==74210== 
==74210== Invalid write of size 8
==74210==    at 0x1091D0: split_text (split.c:23)
==74210==    by 0x1092B2: main (split.c:38)
==74210==  Address 0x4a3f048 is 0 bytes after a block of size 8 alloc'd
==74210==    at 0x483F7B5: malloc (vg_replace_malloc.c:381)
==74210==    by 0x109294: main (split.c:36)
==74210== 
==74210== Invalid read of size 8
==74210==    at 0x1091E7: split_text (split.c:24)
==74210==    by 0x1092B2: main (split.c:38)
==74210==  Address 0x4a3f048 is 0 bytes after a block of size 8 alloc'd
==74210==    at 0x483F7B5: malloc (vg_replace_malloc.c:381)
==74210==    by 0x109294: main (split.c:36)
==74210== 
Hello
==74210== Invalid read of size 8
==74210==    at 0x1092D3: main (split.c:42)
==74210==  Address 0x4a3f048 is 0 bytes after a block of size 8 alloc'd
==74210==    at 0x483F7B5: malloc (vg_replace_malloc.c:381)
==74210==    by 0x109294: main (split.c:36)
==74210== 
world
this
is
elliot
==74210== 
==74210== HEAP SUMMARY:
==74210==     in use at exit: 4,152 bytes in 6 blocks
==74210==   total heap usage: 12 allocs, 6 frees, 6,312 bytes allocated
==74210== 
==74210== LEAK SUMMARY:
==74210==    definitely lost: 4,104 bytes in 5 blocks
==74210==    indirectly lost: 48 bytes in 1 blocks
==74210==      possibly lost: 0 bytes in 0 blocks
==74210==    still reachable: 0 bytes in 0 blocks
==74210==         suppressed: 0 bytes in 0 blocks
==74210== Rerun with --leak-check=full to see details of leaked memory
==74210== 
==74210== ERROR SUMMARY: 12 errors from 3 contexts (suppressed: 0 from 0)
==74210== 
==74210== 4 errors in context 1 of 3:
==74210== Invalid read of size 8
==74210==    at 0x1092D3: main (split.c:42)
==74210==  Address 0x4a3f048 is 0 bytes after a block of size 8 alloc'd
==74210==    at 0x483F7B5: malloc (vg_replace_malloc.c:381)
==74210==    by 0x109294: main (split.c:36)
==74210== 
==74210== 
==74210== 4 errors in context 2 of 3:
==74210== Invalid read of size 8
==74210==    at 0x1091E7: split_text (split.c:24)
==74210==    by 0x1092B2: main (split.c:38)
==74210==  Address 0x4a3f048 is 0 bytes after a block of size 8 alloc'd
==74210==    at 0x483F7B5: malloc (vg_replace_malloc.c:381)
==74210==    by 0x109294: main (split.c:36)
==74210== 
==74210== 
==74210== 4 errors in context 3 of 3:
==74210== Invalid write of size 8
==74210==    at 0x1091D0: split_text (split.c:23)
==74210==    by 0x1092B2: main (split.c:38)
==74210==  Address 0x4a3f048 is 0 bytes after a block of size 8 alloc'd
==74210==    at 0x483F7B5: malloc (vg_replace_malloc.c:381)
==74210==    by 0x109294: main (split.c:36)
==74210== 
==74210== ERROR SUMMARY: 12 errors from 3 contexts (suppressed: 0 from 0)

I know that it is reading and writing from invalid memory places but it still outputs the split-ed text as sub-strings

Why do I get these errors and how do I fix them?


Solution

  • At least this statement

    *res = realloc(*res, sizeof(char*)*(x+1))
    

    that is equivalent to

    res[0] = realloc(res[0], sizeof(char*)*(x+1))
    

    does not make a sense.

    It always reallocates the first element of the array of pointers pointed to by the pointer res.

    As a result the array pointed to by the pointer res has only one element allocated in main.

    So this call strcpy

    strcpy(res[x], tmp);
    

    invokes undefined behavior at least when x is not equal to 0.

    Pay attention to that if you want to change the pointer ptr declared in main within the function split_text then you need to pass it to the function by reference. That is the third parameter of the function should be declared like

    int split_text(char * text, char * token, char *** res);
    

    As for the function definition then you need to allocate memory for a pointer for each new element and also to allocate a character array pointed to by the pointer where a substring will be copied.