C CheckPointer
Memory Safety Checker Example

The C CheckPointer Memory Safety checker tool finds difficult pointer errors. This page shows a buggy C program, and its vanilla execution and mysterious crash after compilation, and its execution and problem diagnosis using the C CheckPointer tool.

A Buggy C Program

This artificial program allocates, updates, and deallocates various bits of storage, and prints its progress as it goes. It does a very bad thing... somewhere. Can you spot the problem?


/* buggy.c */

extern void *malloc(unsigned int n);
extern void free(void *);
extern int printf(const char *, ...);

struct X {
  int *a;
  int *b;
};

struct X foo(struct X p) {
        struct X *x = &p;
        printf("deref a on cpy passed\n");
        int pa = (*x->a);
        printf("deref b on cpy passed\n");
        int pb = (*x->b);
        if (pb<333) 
           { printf("free b\n");
             free(x->b);
           }
        return p;
}

int main() {
        printf("alloc x\n");
        struct X *x = malloc(sizeof(struct X));
        printf("alloc/assign a\n");
        x->a = malloc(sizeof(int));
        *x->a = 111 ;
        printf("alloc/assign b\n");
        x->b = malloc(sizeof(int));
        *x->b = 222 ;
        printf("deref a\n");
        int za = *(x->a);
        printf("deref b\n");
        int zb = *(x->b);
        printf("alloc y\n");
        struct X *y = malloc(sizeof(struct X));
        printf("assign y\n");
        *y = *x;
        printf("deref a\n");
        int ra = *(y->a);
        printf("deref b\n");
        int rb = *(y->b);
        printf("after deref: ra = %d, rb=%d \n",ra,rb);
        printf("rederef a\n");
        za = *(x->a);
        printf("rederef b\n");
        zb = *(x->b);
        struct X xxx = *x;
        struct X yyy = xxx;
        struct X *zzz = &yyy;
        printf("deref a on copy\n");
        za = *(zzz->a);
        printf("deref b on copy\n");
        zb = *(zzz->b);
        struct X r;
        r = foo(yyy);
        struct X *rr = &r;
        printf("deref a on return\n");
        ra = *(rr->a);
        printf("store through b on return\n");
        *(rr->b)=333;
        printf("deref b after update\n");
        rb = *(rr->b);
        printf("loop...");
        int i;
        for (i=0;i<1000000000;i++) { }
        printf("\nafter deref on return: ra = %d, rb =%d, done\n",ra,rb);
        printf("exit\n");
}

A traditional Execution

Here we compile the program with gcc and execute it, getting a crash. See remarks following the program execution.


C:\Example>gcc-4 -g -I.. buggy.c
C:\Example>a.exe
alloc x
alloc/assign a
alloc/assign b
deref a
deref b
alloc y
assign y
deref a
deref b
after deref: ra = 111, rb=222
rederef a
rederef b
deref a on copy
deref b on copy
deref a on cpy passed
deref b on cpy passed
free b
deref a on return
store through b on return
deref b after update
loop...
after deref on return: ra = 111, rb =333, done
exit
      2 [sig] a 9128 open_stackdumpfile: Dumping stack trace to a.exe.stackdump
C:\Example>

The program runs apparantly perfectly, computing final values to fill the struct rr, computes for a long time (loop...), prints the apparantly correct values ra and rb stored in the struct, does an exit, ... and then it dies. There is clearly a problem, but where is the origin of the problem? How would you go about debugging this? See the diagnosis using the C CheckPointer tool.

Pointer errors cause Undefined Behavior

In this particular execution, we get a "nice" crash reported by GCC's runtime, so at least it is obvious there is a problem. However, what the C standard says about errors with pointers is that they produce undefined behavior. What this means is the program can do anything including:

  • crashing loudly (as for this example),
  • producing garbage for output,
  • having arbitrary side effects,
  • producing the correct answer and exit cleanly,
  • producing any of the above behaviors nondeterministically on any specific run
Nontheless, program execution in the presence of a pointer error means the production of an untrusted result. Checkpointer catches any pointer errors, before they cause undefined behavior. Anything it diagnoses needs to be fixed if the program is claimed to well-behaved.

Execution using CheckPointer

Because the problem is so mysterious, we suspect a memory error. We first instrument the program using the CheckPointer tool, which generates an instrumented version of the program (the -D parameter causes the instrumented program to abort after reporting the first safety error encountered). We then compile and run that program:


C:\Example>run ../CheckPointer buggy.c
*** Processing file: buggy.c -> buggy.out.c
C:Example>gcc -DCHECK_POINTER_TERMINATE_ON_ERROR buggy.out.c check_pointer*.c
C:Example> a.exe
alloc x
alloc/assign a
alloc/assign b
deref a
deref b
alloc y
assign y
deref a
deref b
after deref: ra = 111, rb=222
rederef a
rederef b
deref a on copy
deref b on copy
deref a on cpy passed
deref b on cpy passed
free b
deref a on return
store through b on return
*** Error: Dereference of dangling pointer.
    in function: main, line: 64, file C:/Example/buggy.c
assertion "0" failed: file "check_pointer.c", line 544, function: check_pointer_print_error
Aborted (core dumped)
C:\Example>

Notice the program stops with a detected problem before the long-running loop even starts. The CheckPointer tool has diagnosed an erroneous write to an invalid location (a "wild store") in line 64 in the original program; this is the first line of code where an error can be diagnosed as real. One might argue the mistake is the free operation at line 20, but by itself that operation is not erroneous, so the error cannot be diagnosed there. That free operation is relatively easy to find with a debugger once it is known that line 64 is erroneous; whether line 20 or line 64 is modified to fix the program is determined by the purpose of the program.

The C CheckPointer tool can easily find this kind of error. With programs with tens of thousands of lines, this is extremely difficult to do with a debugger.

Semantic Designs also provides a variety of other tools.

For more information: info@semanticdesigns.com    Follow us at Twitter: @SemanticDesigns

C CheckPointer
Memory Safety Example