About Letting Your Program Die in Peace

requirement: medium knowledge of C/C++ or similar languages under MS Windows.

One of the problems of writing a problem are the coding errors. There are many types of errors but the worst ones are those that trigger a Chernobyl reactor like effect.

Something wrong occurs during the program execution, and instead of dying peacefully, it loses any control multiplying the magnitude of the event.

Although there isn’t one universal solution, we can adopt strategies to mitigate the problems of this kind.

The Case

One common case is when one or more local variables are poisoned by a buffer overrun.
How can we prevent this?

For instance, let’s look at this sample function:

int myFN (int val)
{
    int max;
    int i;
    char buffer[8];

    max = val + 8;
    for (i = 0; i < max; i++)
    {
        buffer[i] = 0;
    }
    return max;
}

It has a serious problem when val is greater than 7.
In this case the function doesn’t generate any exception, but it never returns because the buffer overrun poisons the value of ‘i’ variable to 0.

Before planning anything we need to see how it happens.

It starts in the thread stack.
It is the place in which a thread stores many vital data including functions return addresses, register values, and for C/C++ compiler (and even Delphi) it is the place where function parameters and local variables are placed backward (from a high memory address to a lower one).

So for C/C++ compilers, anytime myFN() is called the thread stores the parameters, the return address, the previous context pointer, and get a new one.

...FF
[function params]
[myFN return address]
[previous context area info]
[myFN new context area
  - int max
  - int i
  - BYTE buffer[]
]
...00

In this new context area, all myFN() local variables are stored.

As you can see, ‘i’ is placed immediately after ‘buffer’, and this is the reason by which the function can never return at all.

To walk around the problem, you have to declare myFN() local variables in this way:

char buffer[8];
int max;
int i;

This will protect ‘i’ & ‘max’ value from any buffer overrun.
It will likely corrupt the return address too, generating an exception when it will retrieve and use.

MyFN() will poorly accomplish its task, but, at least, it will not hang the thread.

Conclusion

A walkaround about the local variables accidental corruption can be declaring the local variables by types in the following order:

arrays
data structure containing arrays
data structure
simple variables (int, short, …)
pointers

This should help to have a safer memory corruption processing with almost no effort.