Functions returning strings in C - Memory considerations

Today, I read a small and simple article on functions returning strings in C.
It considers couple of coding practices that can lead to memory leaks or buffer overruns.

You can find the article here.

In rest of this post, I reproduce much of this stuff, just as an exercise for myself. This way, I hope to remember the points mentioned in it.

If we want a function to return a string, it has to first allocate local memory and give its location to calling function. Once the function returns, what happens to this local memory? This memory stays and it is the responsibility of calling function to free this memory.


#define MAX_LEN 100

char * getString()
{
char *str = malloc(MAX_LEN);
strcpy(str, "Memory Leak!");

return str;
}


int main()
{

printf("%s \n", getString());
return 0;
}



In above code, we are not clearing the memory allocated to string str. This is the cause of memory leak which Valgrind points below.



# gcc -g test.c -o test
# valgrind --tool=memcheck --leak-check=yes test

==5407== Memcheck, a memory error detector for x86-linux.
==5407== Copyright (C) 2002-2005, and GNU GPL'd, by Julian Seward et al.
==5407== Using valgrind-2.4.0, a program supervision framework for x86-linux.
==5407== Copyright (C) 2000-2005, and GNU GPL'd, by Julian Seward et al.
==5407== For more details, rerun with: -v
==5407==
Memory Leak!
==5407==
==5407== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 13 from 1)
==5407== malloc/free: in use at exit: 100 bytes in 1 blocks.
==5407== malloc/free: 1 allocs, 0 frees, 100 bytes allocated.
==5407== For counts of detected errors, rerun with: -v
==5407== searching for pointers to 1 not-freed blocks.
==5407== checked 57344 bytes.
==5407==
==5407== 100 bytes in 1 blocks are definitely lost in loss record 1 of 1
==5407== at 0x1B9025B8: malloc (in /.nfsroot/usr/lib/valgrind/vgpreload_memcheck.so)
==5407== by 0x80483D5: getString (test.c:9)
==5407== by 0x8048405: main (test.c:17)
==5407==
==5407== LEAK SUMMARY:
==5407== definitely lost: 100 bytes in 1 blocks.
==5407== possibly lost: 0 bytes in 0 blocks.
==5407== still reachable: 0 bytes in 0 blocks.
==5407== suppressed: 0 bytes in 0 blocks.
==5407== Reachable blocks (those to which a pointer was found) are not shown.
==5407== To see them, rerun with: --show-reachable=yes



To prevent the leak, calling function can allocate the memory. Callee can fit the string in to it and return.


#define MAX_LEN 100

void getString (char *str)
{
strcpy(str, "Buffer Overrun!");
}

int main()
{
char *str = malloc(MAX_LEN);

getString(str);
printf("%s \n", str);
free(str);

return 0;
}



Output from Valgrind.


# valgrind --tool=memcheck --leak-check=yes a.out
==5465== Memcheck, a memory error detector for x86-linux.
==5465== Copyright (C) 2002-2005, and GNU GPL'd, by Julian Seward et al.
==5465== Using valgrind-2.4.0, a program supervision framework for x86-linux.
==5465== Copyright (C) 2000-2005, and GNU GPL'd, by Julian Seward et al.
==5465== For more details, rerun with: -v
==5465==
Buffer Overrun!
==5465==
==5465== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 13 from 1)
==5465== malloc/free: in use at exit: 0 bytes in 0 blocks.
==5465== malloc/free: 1 allocs, 1 frees, 100 bytes allocated.
==5465== For counts of detected errors, rerun with: -v
==5465== No malloc'd blocks -- no leaks are possible.


Now, it is possible that the memory allocated to str is less than the size of the copied string. There is no size checks and its possible to overrun the allocated memory.

Making necessary checks.



#define MAX_LEN 100
#define TRUE 1
#define FALSE 0

int getString (char *str, size_t SIZE)
{
if (SIZE <= sizeof("Hello World!"))
{
return FALSE;
}

strcpy(str, "Hello World!");

return TRUE;
}

int main()
{
char *str = malloc(MAX_LEN);

if(!getString(str, MAX_LEN))
{
/* print an error message */
}

printf("%s \n", str);
free(str);

return 0;
}


Output from valgrind



# valgrind --tool=memcheck --leak-check=yes a.out
==5476== Memcheck, a memory error detector for x86-linux.
==5476== Copyright (C) 2002-2005, and GNU GPL'd, by Julian Seward et al.
==5476== Using valgrind-2.4.0, a program supervision framework for x86-linux.
==5476== Copyright (C) 2000-2005, and GNU GPL'd, by Julian Seward et al.
==5476== For more details, rerun with: -v
==5476==
Hello World!
==5476==
==5476== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 13 from 1)
==5476== malloc/free: in use at exit: 0 bytes in 0 blocks.
==5476== malloc/free: 1 allocs, 1 frees, 100 bytes allocated.
==5476== For counts of detected errors, rerun with: -v
==5476== No malloc'd blocks -- no leaks are possible.

Labels: , , ,