How to Find Pointer Abuse in C

A_Hicken-75x75

By Arthur Hicken

June 7, 2012

3  min read

Pointers are both the strength and Achilles heel of programming in C and C++. While pointers allow you to be very creative and flexible in how you approach solving a particular problem, it is very easy to unwittingly introduce defects into your code. Problems with pointers are among the most difficult encountered by C programmers. A generation ago brute-force techniques like inserting print statements into the code were often the best strategy for trying to find these problems.

Today memory error detection tools like Insure++ can detect pointer-related problems automatically as the code is detected, which saves a lot of time and headache. Insure++ finds problems in the following categories:

  • Operations on NULL pointers.Operations on uninitialized pointers.
  • Operations on pointers that don’t actually point to valid data.
  • Operations which try to compare or otherwise relate pointers that don’t point at the same data object.
  • Function calls through function pointers that don’t actually point to functions.
  • Many other causes of possible undefined behavior or implementation-defined behavior.

Insure++ uses a state-of-the-art code parser, along with hundreds of heuristics, to analyze the application code, during which it reports on several possible static violations. While analyzing the code, it writes a new source code file, with appropriate instrumentation inserted in “trouble spots” (e.g. pointer dereference, scope exit, etc.), and the resulting source file is automatically compiled and all resulting object code files are linked into a new executable program.

Example

Below is the code for a “Hello world” program that uses dynamic memory allocation:

    
/*
 * File: hello.c
 */
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[]) {
    char *string, *string_so_far;
    int i, length;     length = 0;
    for(i=0; i<argc; i++) {
        length += strlen(argv[i])+1;
        string = malloc(length+1);
 
        /*  * Copy the string built so far. */
        if(string_so_far != (char *)0)
            strcpy(string, string_so_far);
        else *string = '\0';
        strcat(string, argv[i]);
        if(i < argc-1) strcat(string, " ");
        string_so_far = string;
    }
    printf("You entered: %s\n", string_so_far);
    return (0);
}
    

The basic idea of this program is that we keep track of the current string size in the variable length. As each new argument is processed, we add its length to the length variable and allocate a block of memory of the new size. Notice that the code is careful to include the final NULL character when computing the string length (line 14) and also the space between strings. Both of these are easy mistakes to make. It’s an interesting exercise to see how quickly you can find such an error with a memory error detection tool like Parasoft Insure++.

The code either copies the argument to the buffer or appends it, depending on whether or not this is the first pass round the loop. Finally, the pointer string_so_far points to the new longer string.

If you compile and run this program under Insure++, you’ll see “uninitialized pointer” errors reported for the code “strcpy(string, string_so_far)”. This is because the variable string_so_far hasn’t been set to anything before the first trip through the argument loop. In a small code sample like this such a problem is obvious, but even if the error is buried in a heap of hundreds of thousands of lines of code and much more subtle than the above error, Insure++ will find it every time.

Insure++ Reports

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.

To try Parasoft Insure++, please visit https://www.parasoft.com/products/parasoft-insure/insure-request-a-demo/.

Photo Credit: oskay

A_Hicken-75x75

By Arthur Hicken

Arthur has been involved in software security and test automation at Parasoft for over 25 years, helping research new methods and techniques (including 5 patents) while helping clients improve their software practices.

Get the latest software testing news and resources delivered to your inbox.