Valgrind Example

A simple program that contains nine different tests, each of which shows an example of an error that valgrind can catch, is shown below.

/*
 * This program has various memory related problems that provide a good way
 * to show off the various abilities of valgrind.  To run it:
 *
 *   valgrind <optional valgrind options> ./valgrind-tests <test number>
 *
 * where <test number> is between 1 and 9, inclusive.  Suggested
 * valgrind options to run with are
 *
 *   --logfile=valgrind.output --num-callers=6 --leak-check=yes
 */

#include <cassert>
#include <iostream>

using namespace std;

void
test_1()
{
  // This test provides an example of using uninitialised memory
  int i;
  printf("%d\n", i);               // Error, i hasn't been initialized

  int * num = (int*)malloc(sizeof(int));
  cout << *num << endl;            // Error, *num hasn't been initialized
  free(num);
}

void
test_2()
{
  // This test provides an example of reading/writing memory after it
  // has been free'd
  int * i = new int;
  delete i;
  *i = 4;                          // Error, i was already freed 
}

void
test_3()
{
  // This test provides an example of reading/writing off the end of
  // malloc'd blocks
  int * i = (int*)malloc(sizeof(int)*10);
  i[10] = 13;                      // Error, wrote past the end of the block
  cout << i[-1] << endl;           // Error, read from before start of the block
  free(i);
}

void
test_4()
{
  // This test provides an example of reading/writing inappropriate
  // areas on the stack.  Note that valgrind only catches errors below
  // the stack (so in this example, we have to pass a negative index
  // to ptr or valgrind won't catch the problem)
  int i;
  int * ptr = &i;
  ptr[-8] = 7;                     // Error, writing to a bad location on stack
  i = ptr[-15];                    // Error, reading from a bad stack location
}

void
test_5()
{
  // This test provides an example of memory leaks -- where pointers
  // to malloc'd blocks are not freed
  int    * i = new int;
  static double * j = new double;
  i = NULL;
  // Note that neither i or j were freed here, although j being static means
  // that it will be considered still reachable instead of definitely lost
}

void
test_6()
{
  // This test provides an example of mismatched use of
  // malloc/new/new [] vs free/delete/delete []
  int * i = new int;
  free(i);                         // Error, new/free mismatch
  double * j = new double[50];
  delete j;                        // Error, new[],delete mismatch 
}

void
test_7()
{
  // This test provides an example of overlapping src and dst
  // pointers in memcpy() and related functions
  char big_buf[1000];
  char * ptr_1 = &big_buf[0];
  char * ptr_2 = &big_buf[400];
  memcpy(ptr_1, ptr_2, 500);       // Error, dst region overlaps src region
}

void
test_8()
{
  // This test provides an example of doubly freed memory
  int * i = new int;
  delete i;
  delete i;                        // Error, i delete'd twice
}

void
test_9()
{
  // This test provides an example of passing unaddressable bytes to a
  // system call.  Note that the file descriptors for standard input
  // (stdin) and standard output (stdout) are 0 and 1 respectively,
  // which is used in the read(2) and write(2) system calls (see the
  // respective man pages for more information).
  char * buf = new char[50];
  printf("Please type a bunch of characters and hit enter.\n");
  read(0, buf, 1000);              // Error, read data overflows buffer
  write(1, buf, 1000);             // Error, data comes from past end of buffer
  delete[] buf;
}

int
main(int argc, char**argv)
{
  if (argc!=2) {
    cerr << "Syntax:" << endl;
    cerr << "  " << argv[0] << " <test-number>" << endl;
    return -1;
  }
  int test_number = atoi(argv[1]);

  switch (test_number) {
    case 1: test_1(); break;
    case 2: test_2(); break;
    case 3: test_3(); break;
    case 4: test_4(); break;
    case 5: test_5(); break;
    case 6: test_6(); break;
    case 7: test_7(); break;
    case 8: test_8(); break;
    case 9: test_9(); break;
    default: cout << "No test or invalid test specified (only 1--9 are valid)."
                  << endl;
             return -1;
  }

  return 0;
}