Search code examples
cmallocundefined-behaviorcalloc

Memory leaks caused by malloc in C


I'm having a hard time understanding what has gone wrong in my code. My program has a function which reads data from a file 8 bytes at a time.

char *read_64_bit_data_from_file(FILE *file, size_t *number_of_chars_read ){
    char *buffer = malloc(sizeof(char) *  8);
    *number_of_chars_read  = fread(buffer, 1, 8, file);
    printf("%s\n", buffer);
    printf("Length of buffer: %ld\n", strlen(buffer));
    printf("Number of chars read: %ld\n", *number_of_chars_read);
    return buffer;
}

The function is called as below:

...
size_t num = 0;
size_t *number_of_chars_read = #
char *message = calloc(sizeof(char), 8);
do{
    message = read_64_bit_data_from_file(file, number_of_chars_read);
}while(*number_of_chars_read==8);
...

Now, When I compile and run the program I get the following output:

Two road�B�z
Length of buffer: 14
Number of chars read: 8
s divergx<�z
Length of buffer: 14
Number of chars read: 8
ed in a x<�z
Length of buffer: 14
Number of chars read: 8
yellow wx<�z
Length of buffer: 14
Number of chars read: 8
ood, Andx<�z
Length of buffer: 14
Number of chars read: 8
 sorry I
Length of buffer: 8
Number of chars read: 8
 could n
Length of buffer: 8
Number of chars read: 8`

The problem is that the characters written into the buffer is sometimes more than 8, although fread returns the value 8 each time.

Now, If I use calloc instead of malloc for allocating buffer, I get the output as desired. Following is the output in this case:

Two road
Length of buffer: 8
Number of chars read: 8
s diverg
Length of buffer: 8
Number of chars read: 8
ed in a 
Length of buffer: 8
Number of chars read: 8
yellow w
Length of buffer: 8
Number of chars read: 8
ood, And
Length of buffer: 8
Number of chars read: 8
 sorry I
Length of buffer: 8
Number of chars read: 8
 could n
Length of buffer: 8
Number of chars read: 8`

I compiled my program using GCC compiler with -Wall and -Wextra flags. The compiler gave no warnings.

I ran the program through valgrind. Following is the output:

==6051== Invalid write of size 1
==6051==    at 0x10917B: char_as_binary (in 
/home/saksham/Documents/DES/DES)
==6051==    by 0x1091FA: string_to_binary (in 
/home/saksham/Documents/DES/DES)
==6051==    by 0x108EE6: main (in /home/saksham/Documents/DES/DES)
==6051==  Address 0x521ecf7 is 5 bytes after a block of size 2 alloc'd
==6051==    at 0x4C31B25: calloc (in 
/usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==6051==    by 0x1090EE: char_as_binary (in 
/home/saksham/Documents/DES/DES)
==6051==    by 0x1091FA: string_to_binary (in 
/home/saksham/Documents/DES/DES)
==6051==    by 0x108EE6: main (in /home/saksham/Documents/DES/DES)
==6051== 
==6051== Invalid read of size 1
==6051==    at 0x10900A: initial_permutation (in 
/home/saksham/Documents/DES/DES)
==6051==    by 0x108FA9: encrypt (in /home/saksham/Documents/DES/DES)
==6051==    by 0x108EFD: main (in /home/saksham/Documents/DES/DES)
==6051==  Address 0x521ec60 is 0 bytes after a block of size 64 
alloc'd
==6051==    at 0x4C31B25: calloc (in 
/usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==6051==    by 0x1091D3: string_to_binary (in 
/home/saksham/Documents/DES/DES)
==6051==    by 0x108EE6: main (in /home/saksham/Documents/DES/DES)
==6051== 
==6051== 
==6051== HEAP SUMMARY:
==6051==     in use at exit: 2,076 bytes in 172 blocks
==6051==   total heap usage: 186 allocs, 14 frees, 12,972 bytes 
allocated
==6051== 
==6051== LEAK SUMMARY:
==6051==    definitely lost: 2,076 bytes in 172 blocks
==6051==    indirectly lost: 0 bytes in 0 blocks
==6051==      possibly lost: 0 bytes in 0 blocks
==6051==    still reachable: 0 bytes in 0 blocks
==6051==         suppressed: 0 bytes in 0 blocks
==6051== Rerun with --leak-check=full to see details of leaked memory
==6051== 
==6051== For counts of detected and suppressed errors, rerun with: -v
==6051== ERROR SUMMARY: 93 errors from 2 contexts (suppressed: 0 from 
0)

I really do not understand why malloc is behaving as such, and how using calloc fixes it. I believe my program has UB (Undefined behavior).

Please, help me understand what I'm overseeing and where am I going wrong.


Solution

  • In order for the C-string style functions to work (such as strlen), you need to reserve one byte in buffer for a NUL-terminator.

    You are not doing this, and so formally the behaviour of your program is undefined. Your compiler is not picking up on this, but Valgrind is. It's also warning you about the missing free. Consider allocating 9 bytes, set buffer[8] to 0 explicitly, and make sure you don't attempt to read more than 8 bytes into buffer.