Valgrind invalid read/write when appending a character to a C string

I'm trying to parse through a string (size ranging from an article to a whole book), and build a temporary linked list to store each individual words, separated by any whitespace characters. I wrote my own charAppend() function since I don't think I can use strcat() and pass in char* str and char c as arguments. Eventually my goal is to organize this linked list of words into a hash table, and when I tried to output the hash table content in the format of <word>:<number of occurrences> with printf I got a seg fault. The arguments to printf seem to be ok on gdb, but when I ran valgrind I got a lot of Invalid write of size 1 and Invalid read of size 1 in my charAppend() function. I don't understand what's going on here since stepping through using gdb didn't give me much info.

my charAppend() function:

//str is my current word, and c is the character I'm trying to append 
char* charAppend(char* str, char c) { 
    int length = 0;
    if (str != NULL) {
        length = strlen(str);
        str[length] = c;
        str = realloc(str, length+1);
        str[length+1] = '\0';
    }
    else {
        str = malloc(2); //1 for c, 1 for \0
        str[0] = c;
        str[1] = '\0';
    }
    return str;
}

And here are some of the error messages:

line 191 is str[length+1] = '\0';

% valgrind --leak-check=full --track-origins=yes test_wc wc-small.txt
==21794== Memcheck, a memory error detector
==21794== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==21794== Using Valgrind-3.12.0.SVN and LibVEX; rerun with -h for copyright info
==21794== Command: test_wc wc-small.txt
==21794== 
==21794== Invalid write of size 1
==21794==    at 0x109528: charAppend (wc.c:191)
==21794==    by 0x108EB5: wc_init (wc.c:47)
==21794==    by 0x108DAB: main (test_wc.c:47)
==21794==  Address 0x54db1e2 is 0 bytes after a block of size 2 alloc'd
==21794==    at 0x4C2DDCF: realloc (vg_replace_malloc.c:785)
==21794==    by 0x109513: charAppend (wc.c:190)
==21794==    by 0x108EB5: wc_init (wc.c:47)
==21794==    by 0x108DAB: main (test_wc.c:47)
==21794== 
==21794== Invalid read of size 1
==21794==    at 0x4C2EDB4: strlen (vg_replace_strmem.c:454)
==21794==    by 0x1094E5: charAppend (wc.c:188)
==21794==    by 0x108EB5: wc_init (wc.c:47)
==21794==    by 0x108DAB: main (test_wc.c:47)
==21794==  Address 0x54db1e2 is 0 bytes after a block of size 2 alloc'd
==21794==    at 0x4C2DDCF: realloc (vg_replace_malloc.c:785)
==21794==    by 0x109513: charAppend (wc.c:190)
==21794==    by 0x108EB5: wc_init (wc.c:47)
==21794==    by 0x108DAB: main (test_wc.c:47)

There are at least 10 more of such error messages, some Conditional jump or move depends on uninitialised value(s) as a result of line 190, str = realloc(str, length+1); and a few that might be a consequence of these when I'm trying to print the hash table content, which I don't even understand.

==21794== Invalid read of size 8
==21794==    at 0x1092CE: wc_output (wc.c:124)
==21794==    by 0x108DD1: main (test_wc.c:51)
==21794==  Address 0xffefffe20 is on thread 1's stack
==21794==  1680 bytes below stack pointer
==21794== 
==21794== Invalid read of size 8
==21794==    at 0x1092EA: wc_output (wc.c:125)
==21794==    by 0x108DD1: main (test_wc.c:51)
==21794==  Address 0xffefffe20 is on thread 1's stack
==21794==  1680 bytes below stack pointer

My guess is that I don't have a correct understanding of how C strings (especially modifying them) work (It's been 2 years since I took an intro level C course), but any help on what went or might be wrong, and suggestions on how to debug these would be appreciated!

I can post more code if that helps, but I think charAppend() is the primary suspect.

1 answer

  • answered 2018-09-21 17:10 4386427

    There are multiple mistakes in this part:

        length = strlen(str);
        str[length] = c;
        str = realloc(str, length+1);
        str[length+1] = '\0';
    

    1) strlen does not give you the amount of memory allocated. It gives the amount minus 1. This is because strlen does not include the string termination. So your realloc is wrong.

    2) Never realloc directly into the target pointer. realloc may return NULL

    So try like:

        length = strlen(str);
        char * tmp = realloc(str, length+2); // note +2
        if (tmp == NULL) exit(1);            // bad error
        str = tmp;
        str[length] = c;
        str[length+1] = '\0';