Using Valgrind

Introduction

Many programs that use malloc and free have errors. There are several kinds of memory errors. Four of the most common are:

Valgrind is a program that checks C programs for memory errors. The name is from Nordic mythology and is pronounced "Val Grinned" . Using valgrind is easy:

$ cc -g program.c -o prog
$ valgrind ./prog 2> val.out
$ more val.out

Valgrind will run your program checking how your program uses memory and will report any problems to standard error. The notation 2> val.out sends standard error data to the file called val.out. If you prefer to see the output as the program runs, omit that. But the report might run off the top of your screen.


Example 1: A Successful Run of Valgrind

Here is what valgrind shows when the program has no detectable memory errors:

$ valgrind ./lsr1f ../tdir4
==7084== Memcheck, a memory error detector
==7084== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==7084== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==7084== Command: lsr1f ../tdir4
==7084== 
../tdir4:
d4.1/
d4.2/

../tdir4/d4.1:
other2@

../tdir4/d4.2:
other@
==7084== 
==7084== HEAP SUMMARY:
==7084==     in use at exit: 0 bytes in 0 blocks
==7084==   total heap usage: 20 allocs, 20 frees, 100,135 bytes allocated
==7084== 
==7084== All heap blocks were freed -- no leaks are possible
==7084== 
==7084== For counts of detected and suppressed errors, rerun with: -v
==7084== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
$ 

We run valgrind with arguments consisting of the command line for our program. Valgrind signs on, tells you what command it is running, then runs the program, then presents a memory use summary.

In this example, all allocated blocks are freed, so there are no memory leaks. There are also no memory overruns, no double frees, no use of uninitialized data. The ERROR SUMMARY shows there were 0 errors. This is the outcome you want.


Example 2: Detecting a Memory Over Run (Buffer overflow)

Some programs allocate memory but store more data than the allocated space. This type of error can lead to data corruption and/or program crashes. Here is a simple program with this type of error:

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

/* overflow.c : show an overflow of a malloc-ed block	*/
/*		test this with valgrind			*/
/* Q: What change(s) will get rid of the overflow	*/

int main()
{
	char	word[] = "testing";

	char	*p = malloc( strlen(word) );
	strcpy(p, word);
	printf("the word is %s\n", p);
	return 0;
}

Running valgrind on this program produces:

$ valgrind ./overflow 2> v
the word is testing
$ cat v
==15368== Memcheck, a memory error detector
==15368== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==15368== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==15368== Command: ./overflow
==15368== 
==15368== Invalid write of size 1
==15368==    at 0x4C2ECAD: strcpy (vg_replace_strmem.c:510)
==15368==    by 0x4005E7: main (overflow.c:13)
==15368==  Address 0x51e3047 is 0 bytes after a block of size 7 alloc'd
==15368==    at 0x4C2BB7B: malloc (vg_replace_malloc.c:299)
==15368==    by 0x4005D0: main (overflow.c:12)
==15368== 
==15368== Invalid read of size 1
==15368==    at 0x4C2EBA4: strlen (vg_replace_strmem.c:458)
==15368==    by 0x4E84CC6: vfprintf (in /usr/lib64/libc-2.26.so)
==15368==    by 0x4E8B3E5: printf (in /usr/lib64/libc-2.26.so)
==15368==    by 0x4005FD: main (overflow.c:14)
==15368==  Address 0x51e3047 is 0 bytes after a block of size 7 alloc'd
==15368==    at 0x4C2BB7B: malloc (vg_replace_malloc.c:299)
==15368==    by 0x4005D0: main (overflow.c:12)
==15368== 
==15368== 
==15368== HEAP SUMMARY:
==15368==     in use at exit: 7 bytes in 1 blocks
==15368==   total heap usage: 2 allocs, 1 frees, 1,031 bytes allocated
==15368== 
==15368== LEAK SUMMARY:
==15368==    definitely lost: 7 bytes in 1 blocks
==15368==    indirectly lost: 0 bytes in 0 blocks
==15368==      possibly lost: 0 bytes in 0 blocks
==15368==    still reachable: 0 bytes in 0 blocks
==15368==         suppressed: 0 bytes in 0 blocks
==15368== Rerun with --leak-check=full to see details of leaked memory
==15368== 
==15368== For counts of detected and suppressed errors, rerun with: -v
==15368== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)

This program has three errors. There are two instance of over running the allocated space, once in the write to memory at line 13 in strcpy and once in the read from memory in strlen called by printf at line 14. In addition to those two memory over runs, the local variable p is deallocated when main exits, thereby losing any reference to the allocated block pointed to by p.

The changes to eliminate these three problems are:


Example 3: A Memory Leak

A memory leak is a very common error in C programs. The program uses malloc to allocate a block of memory, stores the address of that block in a variable. Later in the program, that variable is over-written with a new address or the variable is deallocated when a function completes. In both cases, the address of the block is lost. Without the address, the program cannot free() the block.

In a long-running program, particularly in server programs, these lost blocks accumulate until the program has allocated a lot of memory but is not using most of that memory. The program can use up all the memory on the system, causing the system to slow down and sometimes freeze up.

Here is a simple program that has a memory leak:

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

/* leak.c : program that allocates memory and loses addresses	*/
/*	    use valgrind to find memory leaks			*/
/* Q: What change(s) will get rid of the leak?			*/

#define	WORDLEN	200

int main()
{
	char	*p;
	char	w[WORDLEN];
	
	while ( fgets(w, WORDLEN, stdin) != NULL )
	{
		p = malloc(1 + strlen(w));	/* allocate memory	*/
		if ( p == NULL )		/* overwrites prev addr	*/
			exit(1);
		strcpy(p, w);			/* load data		*/
		printf("word is %s", p);	/* print data		*/
	}
	return 0;
}

Running valgrind on this program produces:

$ cc -Wall -g leak.c -o leak
$ valgrind ./leak 
==31865== Memcheck, a memory error detector
==31865== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==31865== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==31865== Command: ./leak
==31865== 
abc
word is abc
def
word is def
==31865== 
==31865== HEAP SUMMARY:
==31865==     in use at exit: 10 bytes in 2 blocks
==31865==   total heap usage: 4 allocs, 2 frees, 2,058 bytes allocated
==31865== 
==31865== LEAK SUMMARY:
==31865==    definitely lost: 10 bytes in 2 blocks
==31865==    indirectly lost: 0 bytes in 0 blocks
==31865==      possibly lost: 0 bytes in 0 blocks
==31865==    still reachable: 0 bytes in 0 blocks
==31865==         suppressed: 0 bytes in 0 blocks
==31865== Rerun with --leak-check=full to see details of leaked memory
==31865== 
==31865== For counts of detected and suppressed errors, rerun with: -v
==31865== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
$

This program does not crash, because there is no memory corruption. Instead, the program allocates memory with malloc, stores the address of the block in p, uses that memory, then allocates a new block, storing the address of this new block in p replacing (and losing) the previous value. When the function ends, the local pointer variable p is deallocated (as all local variables are) thereby losing that value. With the addresses of these two memory blocks overwritten and deallocated, the program cannot call free to release the memory.

Valgrind suggests using --leak-check=full for more details. Here is the report:

 $ valgrind --leak-check=full ./leak
==15497== Memcheck, a memory error detector
==15497== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==15497== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==15497== Command: ./leak
==15497== 
abc
word is abc
def
word is def
==15497== 
==15497== HEAP SUMMARY:
==15497==     in use at exit: 10 bytes in 2 blocks
==15497==   total heap usage: 4 allocs, 2 frees, 2,058 bytes allocated
==15497== 
==15497== 10 bytes in 2 blocks are definitely lost in loss record 1 of 1
==15497==    at 0x4C2BB7B: malloc (vg_replace_malloc.c:299)
==15497==    by 0x40069E: main (leak.c:18)
==15497== 
==15497== LEAK SUMMARY:
==15497==    definitely lost: 10 bytes in 2 blocks
==15497==    indirectly lost: 0 bytes in 0 blocks
==15497==      possibly lost: 0 bytes in 0 blocks
==15497==    still reachable: 0 bytes in 0 blocks
==15497==         suppressed: 0 bytes in 0 blocks
==15497== 
==15497== For counts of detected and suppressed errors, rerun with: -v
==15497== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
$

This report shows that the error happens at line 10 in the program.

If the program runs for a long time and processes a lot of data, the number of lost memory blocks will increase, requiring more and more memory from the system.

There are several solutions to this error. One is to free the memory after using it before storing a new address in p. Another solution is to use realloc to change the size of the block to match the next input string. In any case, the function should free the block before ending.


Example 4: Double Free -- heap memory corruption<

Another memory error is freeing a block of memory that has already been freed or freeing a block of memory that was not allocated with malloc. In either case, free() function will do some internal book keeping to manage storage in the heap. Free() does not check if the address passed points to a valid, allocated block. Doing those book keeping pointer-moving operations with invalid addresses will corrupt the heap data structures. This memory corruption can produce crashes later in the program.

The following program frees a block twice:

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

/* doublefree.c : show free()ing a block twice		*/
/*		  test this with valgrind		*/
/* Q: What change(s) will get rid of the error?		*/

void show_word(char *);

int main()
{
	char	word[] = "testing";

	char	*p = malloc( 1+strlen(word) );	/* allocate	*/
	strcpy(p, word);			/* store data	*/
	show_word(p);				/* show data	*/
	free(p);				/* free space	*/
	return 0;
}
void show_word(char *w)
{
	printf("the word is %s\n", w);		/* print word	*/
	free(w);				/* deallocate	*/
}

When this program is run with valgrind, the result is:

$ cc -Wall -g doublefree.c -o doublefree
$ valgrind ./doublefree
==3587== Memcheck, a memory error detector
==3587== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==3587== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==3587== Command: ./doublefree
==3587== 
the word is testing
==3587== Invalid free() / delete / delete[] / realloc()
==3587==    at 0x4C2CD28: free (vg_replace_malloc.c:530)
==3587==    by 0x400653: main (doublefree.c:18)
==3587==  Address 0x51e3040 is 0 bytes inside a block of size 8 free'd
==3587==    at 0x4C2CD28: free (vg_replace_malloc.c:530)
==3587==    by 0x400688: show_word (doublefree.c:24)
==3587==    by 0x400647: main (doublefree.c:17)
==3587==  Block was alloc'd at
==3587==    at 0x4C2BB7B: malloc (vg_replace_malloc.c:299)
==3587==    by 0x400624: main (doublefree.c:15)
==3587== 
==3587== 
==3587== HEAP SUMMARY:
==3587==     in use at exit: 0 bytes in 0 blocks
==3587==   total heap usage: 2 allocs, 3 frees, 1,032 bytes allocated
==3587== 
==3587== All heap blocks were freed -- no leaks are possible
==3587== 
==3587== For counts of detected and suppressed errors, rerun with: -v
==3587== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
$ 

The report shows an 'Invalid free()' at main line 18. The report shows that the block was earlier freed at line 24 after being originall allocated at line 15. The HEAP SUMMARY shows there were 2 allocs and 3 frees. Valgrind will also report calls to free with addresses that were not returned from malloc.


Example 5: Using Uninitialized Memory

Local variables and heap memory are not initialized when defined and allocated. If a program uses these variables before assigning them values, the program will produce unpredicatable results. The compiler often catches these errors. Valgrind will report them as the program is running. Here is an example:

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

/* uninit.c : code that uses uninitialized memory		*/
/*		use valgrind to detect these bugs		*/
/* Q: What change(s) will get rid of this problem?		*/

int main()
{
	int	x;
	char	*p;

	printf("x is %d\n", x);
	printf("p points to %s\n", p);
	return 0;
}

The compiler catches the errors, and so does valgrind:

$ cc -Wall -g uninit.c -o uninit
uninit.c:m In function 'mainm':
uninit.c:13:2:warning: 'm' is used uninitialized in this function [-Wuninitialized]
  printf("x is %d\n", x);
  ^~~~~~~~~~~~~~~~~~~~~~
uninit.c:14:2:warning: 'p' is used uninitialized in this function [-Wuninitialized]
  printf("p points to %s\n", p);
  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
$ valgrind ./uninit
==5422== Memcheck, a memory error detector
==5422== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==5422== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==5422== Command: ./uninit
==5422== 
==5422== Conditional jump or move depends on uninitialised value(s)
==5422==    at 0x4E8425F: vfprintf (in /usr/lib64/libc-2.26.so)
==5422==    by 0x4E8B3E5: printf (in /usr/lib64/libc-2.26.so)
==5422==    by 0x4004E2: main (uninit.c:13)
==5422== 
==5422== Use of uninitialised value of size 8
==5422==    at 0x4E803B1: _itoa_word (in /usr/lib64/libc-2.26.so)
==5422==    by 0x4E8394D: vfprintf (in /usr/lib64/libc-2.26.so)
==5422==    by 0x4E8B3E5: printf (in /usr/lib64/libc-2.26.so)
==5422==    by 0x4004E2: main (uninit.c:13)
==5422== 
==5422== Conditional jump or move depends on uninitialised value(s)
==5422==    at 0x4E803BB: _itoa_word (in /usr/lib64/libc-2.26.so)
==5422==    by 0x4E8394D: vfprintf (in /usr/lib64/libc-2.26.so)
==5422==    by 0x4E8B3E5: printf (in /usr/lib64/libc-2.26.so)
==5422==    by 0x4004E2: main (uninit.c:13)
==5422== 
==5422== Conditional jump or move depends on uninitialised value(s)
==5422==    at 0x4E83A05: vfprintf (in /usr/lib64/libc-2.26.so)
==5422==    by 0x4E8B3E5: printf (in /usr/lib64/libc-2.26.so)
==5422==    by 0x4004E2: main (uninit.c:13)
==5422== 
==5422== Conditional jump or move depends on uninitialised value(s)
==5422==    at 0x4E84401: vfprintf (in /usr/lib64/libc-2.26.so)
==5422==    by 0x4E8B3E5: printf (in /usr/lib64/libc-2.26.so)
==5422==    by 0x4004E2: main (uninit.c:13)
==5422== 
x is 0
==5422== Conditional jump or move depends on uninitialised value(s)
==5422==    at 0x4E83410: vfprintf (in /usr/lib64/libc-2.26.so)
==5422==    by 0x4E8B3E5: printf (in /usr/lib64/libc-2.26.so)
==5422==    by 0x4004F8: main (uninit.c:14)
==5422== 
==5422== Use of uninitialised value of size 8
==5422==    at 0x4C2EB92: strlen (vg_replace_strmem.c:458)
==5422==    by 0x4E84CC6: vfprintf (in /usr/lib64/libc-2.26.so)
==5422==    by 0x4E8B3E5: printf (in /usr/lib64/libc-2.26.so)
==5422==    by 0x4004F8: main (uninit.c:14)
==5422== 
==5422== Use of uninitialised value of size 8
==5422==    at 0x4C2EBA4: strlen (vg_replace_strmem.c:458)
==5422==    by 0x4E84CC6: vfprintf (in /usr/lib64/libc-2.26.so)
==5422==    by 0x4E8B3E5: printf (in /usr/lib64/libc-2.26.so)
==5422==    by 0x4004F8: main (uninit.c:14)
==5422== 
==5422== Conditional jump or move depends on uninitialised value(s)
==5422==    at 0x4EADDE0: _IO_file_xsputn@@GLIBC_2.2.5 (in /usr/lib64/libc-2.26.so)
==5422==    by 0x4E84594: vfprintf (in /usr/lib64/libc-2.26.so)
==5422==    by 0x4E8B3E5: printf (in /usr/lib64/libc-2.26.so)
==5422==    by 0x4004F8: main (uninit.c:14)
==5422== 
==5422== Use of uninitialised value of size 8
==5422==    at 0x4EADDE6: _IO_file_xsputn@@GLIBC_2.2.5 (in /usr/lib64/libc-2.26.so)
==5422==    by 0x4E84594: vfprintf (in /usr/lib64/libc-2.26.so)
==5422==    by 0x4E8B3E5: printf (in /usr/lib64/libc-2.26.so)
==5422==    by 0x4004F8: main (uninit.c:14)
==5422== 
==5422== Conditional jump or move depends on uninitialised value(s)
==5422==    at 0x4EADDFE: _IO_file_xsputn@@GLIBC_2.2.5 (in /usr/lib64/libc-2.26.so)
==5422==    by 0x4E84594: vfprintf (in /usr/lib64/libc-2.26.so)
==5422==    by 0x4E8B3E5: printf (in /usr/lib64/libc-2.26.so)
==5422==    by 0x4004F8: main (uninit.c:14)
==5422== 
==5422== Conditional jump or move depends on uninitialised value(s)
==5422==    at 0x4C32FE3: is_overlap (vg_replace_strmem.c:137)
==5422==    by 0x4C32FE3: mempcpy (vg_replace_strmem.c:1524)
==5422==    by 0x4EADD23: _IO_file_xsputn@@GLIBC_2.2.5 (in /usr/lib64/libc-2.26.so)
==5422==    by 0x4E84594: vfprintf (in /usr/lib64/libc-2.26.so)
==5422==    by 0x4E8B3E5: printf (in /usr/lib64/libc-2.26.so)
==5422==    by 0x4004F8: main (uninit.c:14)
==5422== 
==5422== Conditional jump or move depends on uninitialised value(s)
==5422==    at 0x4C32FE9: is_overlap (vg_replace_strmem.c:140)
==5422==    by 0x4C32FE9: mempcpy (vg_replace_strmem.c:1524)
==5422==    by 0x4EADD23: _IO_file_xsputn@@GLIBC_2.2.5 (in /usr/lib64/libc-2.26.so)
==5422==    by 0x4E84594: vfprintf (in /usr/lib64/libc-2.26.so)
==5422==    by 0x4E8B3E5: printf (in /usr/lib64/libc-2.26.so)
==5422==    by 0x4004F8: main (uninit.c:14)
==5422== 
==5422== Conditional jump or move depends on uninitialised value(s)
==5422==    at 0x4C32FF8: mempcpy (vg_replace_strmem.c:1524)
==5422==    by 0x4EADD23: _IO_file_xsputn@@GLIBC_2.2.5 (in /usr/lib64/libc-2.26.so)
==5422==    by 0x4E84594: vfprintf (in /usr/lib64/libc-2.26.so)
==5422==    by 0x4E8B3E5: printf (in /usr/lib64/libc-2.26.so)
==5422==    by 0x4004F8: main (uninit.c:14)
==5422== 
==5422== Conditional jump or move depends on uninitialised value(s)
==5422==    at 0x4C33086: mempcpy (vg_replace_strmem.c:1524)
==5422==    by 0x4EADD23: _IO_file_xsputn@@GLIBC_2.2.5 (in /usr/lib64/libc-2.26.so)
==5422==    by 0x4E84594: vfprintf (in /usr/lib64/libc-2.26.so)
==5422==    by 0x4E8B3E5: printf (in /usr/lib64/libc-2.26.so)
==5422==    by 0x4004F8: main (uninit.c:14)
==5422== 
==5422== Conditional jump or move depends on uninitialised value(s)
==5422==    at 0x4C3304D: mempcpy (vg_replace_strmem.c:1524)
==5422==    by 0x4EADD23: _IO_file_xsputn@@GLIBC_2.2.5 (in /usr/lib64/libc-2.26.so)
==5422==    by 0x4E84594: vfprintf (in /usr/lib64/libc-2.26.so)
==5422==    by 0x4E8B3E5: printf (in /usr/lib64/libc-2.26.so)
==5422==    by 0x4004F8: main (uninit.c:14)
==5422== 
==5422== Use of uninitialised value of size 8
==5422==    at 0x4C33058: mempcpy (vg_replace_strmem.c:1524)
==5422==    by 0x4EADD23: _IO_file_xsputn@@GLIBC_2.2.5 (in /usr/lib64/libc-2.26.so)
==5422==    by 0x4E84594: vfprintf (in /usr/lib64/libc-2.26.so)
==5422==    by 0x4E8B3E5: printf (in /usr/lib64/libc-2.26.so)
==5422==    by 0x4004F8: main (uninit.c:14)
==5422== 
p points to 
==5422== 
==5422== HEAP SUMMARY:
==5422==     in use at exit: 0 bytes in 0 blocks
==5422==   total heap usage: 1 allocs, 1 frees, 1,024 bytes allocated
==5422== 
==5422== All heap blocks were freed -- no leaks are possible
==5422== 
==5422== For counts of detected and suppressed errors, rerun with: -v
==5422== Use --track-origins=yes to see where uninitialised values come from
==5422== ERROR SUMMARY: 17 errors from 17 contexts (suppressed: 0 from 0)
$

Valgrind finds every place where these uninitialized variables are used. As the report shows, library functions call other functions, all of which are being passed and are using an uninitialized value.


Other Memory Errors

Valgrind checks for other types of memory errors. The manual page for valgrind contains a lot of information. Many flags allow you to ask for more details. There are several tutorials available on the Web.

The four types of memory errors shown here do not always cause obvious problems. The problems may occur later, or after running the program for a long time, or only with certain input. Checking your program with valgrind is an important step in making sure your program is as secure and well-designed as possible.