Do Suspect Memory Leak With Your System ? Try Valgrind Tool

September 23, 2013 | By
| Reply More

Valgrind is a collection of command line tools that can be used for debugging and profiling executables in Linux. Memcheck is one of the most popular tool in the Valgrind suite of tools that can be used to detect memory related errors in a program executable. In this article, we will discuss the Memcheck tool through some practical examples.

The Memcheck Tool Examples

Valgrind's Memcheck is a powerful tool that can detect a variety of memory related problems like :

  • A malloc() without any free()
  • A malloc() with more than one free()
  • Uninitialized variables
  • Invalid memory access by a heap pointer
  • etc

Valgrind along with Memcheck tool can be used in the following way :

valgrind --tool=memcheck --leak-check=yes --track-origins=yes [executable-name]

So, just replace [executable-name] with the actual executable name for Valgrind and Memcheck to test and display the errors. Now, lets discuss how Memcheck can be used to detect various memory related problems.

NOTE - Compile all the executables with -g flag (when using gcc) so that Valgrind tool can produce line numbers in its output.

1. Detect Uninitialized variables

Suppose your program contains one (or more) uninitialized variables.

For example :

#include<stdio.h>
#include<stdlib.h>

int main(void)
{
int a;
if(a)
a = a+1;
return 0;
}

Now, when you run Valgrind's Memcheck tool on this code, it will result in the following output:

$ valgrind --tool=memcheck --leak-check=yes --track-origins=yes ./executable1
==2897== Memcheck, a memory error detector
==2897== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==2897== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==2897== Command: ./executable1
==2897==
==2897== Conditional jump or move depends on uninitialised value(s)
==2897== at 0x4004F4: main (valgrind_unini_var.c:7)
==2897== Uninitialised value was created by a stack allocation
==2897== at 0x4004EC: main (valgrind_unini_var.c:5)

==2897==
==2897==
==2897== HEAP SUMMARY:
==2897== in use at exit: 0 bytes in 0 blocks
==2897== total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==2897==
==2897== All heap blocks were freed -- no leaks are possible
==2897==
==2897== For counts of detected and suppressed errors, rerun with: -v
==2897== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 2 from 2)

So you can see that Valgrind produced errors (highlighted in bold) related to uninitialised variable 'a'.

2. Detect memory leaks

Suppose your program contains no free() corresponding to a malloc(). This would obviously result in memory leaks. Memcheck tool can be used to easily find these kind of leaks.

Here is an example of code :

#include<stdio.h>
#include<stdlib.h>

int main(void)
{
char *ptr = (char*)malloc(10);

return 0;
}

Here is the output when Valgrind's Memcheck tool was used to run the executable compiled out of the code above :

$ valgrind --tool=memcheck --leak-check=yes --track-origins=yes ./executable2
==2934== Memcheck, a memory error detector
==2934== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==2934== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==2934== Command: ./executable2
==2934==
==2934==
==2934== HEAP SUMMARY:
==2934== in use at exit: 10 bytes in 1 blocks
==2934== total heap usage: 1 allocs, 0 frees, 10 bytes allocated
==2934==
==2934== 10 bytes in 1 blocks are definitely lost in loss record 1 of 1
==2934== at 0x4C2CD7B: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2934== by 0x40053D: main (valgrind_no_free.c:6)
==2934==
==2934== LEAK SUMMARY:
==2934== definitely lost: 10 bytes in 1 blocks
==2934== indirectly lost: 0 bytes in 0 blocks
==2934== possibly lost: 0 bytes in 0 blocks
==2934== still reachable: 0 bytes in 0 blocks
==2934== suppressed: 0 bytes in 0 blocks
==2934==
==2934== For counts of detected and suppressed errors, rerun with: -v
==2934== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 2 from 2)

So you can see that the memory leak was easily detected.

3. Detect double free

Sometimes, programmers make this silly mistake of freeing the same memory twice. Valgrind's Memcheck tool can be used to detect these types of problems too.

Here is an example code containing double free :

#include<stdio.h>
#include<stdlib.h>

int main(void)
{
char *ptr = (char*)malloc(10);
free(ptr);
free(ptr);

return 0;
}

And here is the output of Valgrind's Memcheck tool for the code above :

$ valgrind --tool=memcheck --leak-check=yes --track-origins=yes ./executable3
==2961== Memcheck, a memory error detector
==2961== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==2961== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==2961== Command: ./executable3
==2961==
==2961== Invalid free() / delete / delete[] / realloc()
==2961== at 0x4C2BA6C: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2961== by 0x4005A9: main (valgrind_double_free.c:8)
==2961== Address 0x51fc040 is 0 bytes inside a block of size 10 free'd
==2961== at 0x4C2BA6C: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2961== by 0x40059D: main (valgrind_double_free.c:7)
==2961==
==2961==
==2961== HEAP SUMMARY:
==2961== in use at exit: 0 bytes in 0 blocks
==2961== total heap usage: 1 allocs, 2 frees, 10 bytes allocated
==2961==
==2961== All heap blocks were freed -- no leaks are possible
==2961==
==2961== For counts of detected and suppressed errors, rerun with: -v
==2961== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 2 from 2)

So you can see that the second free() was successfully identified (highlighted in bold) as invalid function call.

4. Detect Operations On Dangling Pointers

Dangling pointers are those which point to a memory that is already freed. Valgrind's Memcheck can easily detect these type of issues.

Here is an example code:

#include<stdio.h>
#include<stdlib.h>

int main(void)
{
char *ptr = (char*)malloc(10);
free(ptr);

ptr[3] = 'a';

return 0;
}

So you can see that the code above tries to access the memory pointed by 'ptr' even after it is freed. Here is how Memcheck detects this bug :

$ valgrind --tool=memcheck --leak-check=yes --track-origins=yes ./executable4
==3393== Memcheck, a memory error detector
==3393== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==3393== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==3393== Command: ./executable4
==3393==
==3393== Invalid write of size 1
==3393== at 0x4005A6: main (valgrind_no_null.c:9)
==3393== Address 0x51fc043 is 3 bytes inside a block of size 10 free'd
==3393== at 0x4C2BA6C: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3393== by 0x40059D: main (valgrind_no_null.c:7)
==3393==
==3393==
==3393== HEAP SUMMARY:
==3393== in use at exit: 0 bytes in 0 blocks
==3393== total heap usage: 1 allocs, 1 frees, 10 bytes allocated
==3393==
==3393== All heap blocks were freed -- no leaks are possible
==3393==
==3393== For counts of detected and suppressed errors, rerun with: -v
==3393== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 2 from 2)

So you can see that the tool detected the write operation on the dangling pointer and labelled it as invalid.

5. Detect Invalid Memory Access

Sometimes, code contains a bug where-in a pointer tries to access out of bound heap memory location. Memcheck can easily detect these kind of memory related bugs.

Here is an example :

#include<stdio.h>
#include<stdlib.h>

int main(void)
{
char *ptr = (char*)malloc(10);
ptr[11] = 'z';

return 0;
}

In the code above, you can see that the pointer 'ptr' is trying to access a memory location that is out of its bounds. Here is how memcheck detects this problem :

$ valgrind --tool=memcheck --leak-check=yes --track-origins=yes ./executable5
==3538== Memcheck, a memory error detector
==3538== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==3538== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==3538== Command: ./executable5
==3538==
==3538== Invalid write of size 1
==3538== at 0x40059A: main (valgrind_inv_mem.c:7)
==3538== Address 0x51fc04b is 1 bytes after a block of size 10 alloc'd
==3538== at 0x4C2CD7B: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3538== by 0x40058D: main (valgrind_inv_mem.c:6)
==3538==
==3538==
==3538== HEAP SUMMARY:
==3538== in use at exit: 0 bytes in 0 blocks
==3538== total heap usage: 1 allocs, 1 frees, 10 bytes allocated
==3538==
==3538== All heap blocks were freed -- no leaks are possible
==3538==
==3538== For counts of detected and suppressed errors, rerun with: -v
==3538== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 2 from 2)

So you can see that memcheck easily detected (highlighted in bold) the invalid memory access.

The Memcheck Tool Limitations

Here are some of the known limitations of using Valgrind :

  • It slows down the overall processing of your program.
  • Cannot detect buffer overflows in case of stack variables.
  • Consumes a lot of memory.

To know more about Valgrind's Memcheck tool, checkout its user manual.

NOTE - Want to learn more about pointers in C, read our article on C Pointer Fundamentals Explained With Examples.

Filed Under : OPEN SOURCE TOOLS

Free Linux Ebook to Download

Leave a Reply

Commenting Policy:
Promotion of your products ? Comment gets deleted.
All comments are subject to moderation.