Go back to blog listing

Finding Dynamic Memory Errors with Memory Error Detection Tools

Using dynamically allocated memory properly is a tricky issue. In many cases, programs continue to run well after a programming error causes serious memory corruption; sometimes they don’t crash at all. In this post we're looking at how dynamic analysis can detect memory corruption problems common in C code.

A common mistake is trying to reuse a pointer after it has already been released. Consider lines 22-26 in the following program that de-allocates memory blocks before allocating the larger ones:

 

1:  /*
2:   * File: hello4.c
3:   */
4:  #include <stdlib.h>
5:  #include <string.h>
6:
7:  main(argc, argv)
8:    int argc;
9:    char *argv[];
10:   {
11:     char *string, *string_so_far;
12:     int i, length;
13:  
14:     length = 0;
15:  
16:     for(i=0; i<argc; i++) {
17:       length += strlen(argv[i])+1;
18:       string = malloc(length+1);
19:       /*
20:       * Copy the string built so far.
21:       */
22:       if(string_so_far != (char *)0) {
23:         free(string_so_far);
24:         strcpy(string, string_so_far);
25:       }
26:       else *string = '\0';
27:
28:       strcat(string, argv[i]);
29:       if(i < argc-1) strcat(string, " ");
30:       string_so_far = string;
31:     }
32:     printf("You entered: %s\n", string_so_far);
33:     return (0);
34:   }

Dangling Pointers

If you run this code through memory error detection tool such as  Parasoft Insure++, you’ll get an error message about a “dangling pointer” at line 24. In this case, the block pointed to by string_so_far is freed at line 23 and then used in the next line.

Wikipedia defines a dangling pointer as "pointers that do not point to a valid object of the appropriate type. These are special cases of memory safety violations. More generally, dangling references and wild references are references that do not resolve to a valid destination." These are a class of errors related to "deleting an object from memory explicitly or by destroying the stack frame on return does not alter associated pointers. The pointer still points to the same location in memory even though it may now be used for other purposes." This is a common problem that often goes unnoticed because programs with these errors often compile with no warnings and can sometime execute correctly.

In the example above, the memory buffer reference by pointer string_so_far is explicitly freed and then subsequently dereferenced. Dynamic analysis detects these errors which can go unnoticed during testing and even during normal operation of the application until the wrong piece of memory is incorrectly accessed.

What Other Dynamic Memory Problems Can Memory Error Detection Tools Find?

In addition to this type of dangling pointer dynamic memory problem, memory error detection tool such as Parasoft Insure++ also detect the following errors:

  • Reading from or writing to “dangling pointers."
  • Passing “dangling pointers” as arguments to functions or returning them from functions.
  • Freeing the same memory block multiple times.
  • Attempting to free statically allocated memory.
  • Freeing stack memory (local variables).
  • Passing a pointer to free that doesn’t point to the beginning of a memory block.
  • Calls to free with NULL or uninitialized pointers.
  • Passing nonsensical arguments or arguments of the wrong data type to malloc, calloc, realloc or free.

Compile-time analysis

Insure++ detects errors at compile-time as well as runtime. Compile-time errors detected include:

  • Cast of pointer loses precision
  • Mismatch in format specification
  • Mismatch in argument type
  • Code is not evaluated, has no effect, or is unreachable
  • Undefined identifier
  • Variable declared, but never used
  • Returning pointer to local variable
  • Function returns inconsistent value
  • Unused variables

Runtime Reporting

The user then executes this program as part of use-case testing, just as you would use the original program, and Insure++ reports any problems found. Insure++ reports include detailed information, including: about the type of bug, source file and line number, actual source code line contents, expression that caused the problem, with reports including:

  • The type of bug, (e.g. EXPR_UNRELATED_PTRCMP)
  • The source file and line number, (e.g. foo.cc:42)
  • The actual source code line contents, (e.g. “while (p < g) {”)
  • The expression which caused the problem, (e.g. “p < g”)
  • Information about all pointers and memory blocks involved in the bug:
    • The pointer values
    • The memory blocks pointed to, (if any), and any offset
    • The block allocation information:
      • Stack trace if dynamically allocated.
      • Block declaration location, (source file and line number), if allocated on the stack or globally.
      • Stack trace of deallocation of the block, if applicable.
    • The stack trace showing how the program got to the bug location.

Image credit: xithorian

 

Stay up to date