views:

115

answers:

7

Hello,

How do you create a unit test case in c for a buffer overflow that does not cause a memory error such as a segfault?

I.e. Given a simple buffer overflow, such as

int function () {
    int exampleArray[10];
    exampleArray[10] = 5;
    return 0;
}

How do you create a unit test for this code? There is clearly an error, we are writing past the end of an array. However, you can run a function like this without any evidence of the error.

Note: I need to be able to create test cases for when the index to the array is supplied at run time by the user as well as the above simplified case.

In a managed language like Java the code will throw an exception (ArrayIndexOutOfBoundsException) which can be caught. So creating a test case is straightforward (a try-catch block for the exception).

How would such a test be created in c? Can any of the unit testing frameworks for C handle such a situation?

Background Information: I'm trying to do automatic test case generation. I know where the errors are and would like to be able to create a unit test to fail on these bugs.

However I wouldn't have the faintest idea how to create a test case that fails for a buffer overflow bug that doesn't crash the application.

+1  A: 

You can only try to detect accesses to indices right below zero and right above the last element - something like allocate two additional arrays and fill them with marker values, then check the values.

You can't detect all buffer overruns in C easily though. Your code can access element far outside the buffer bounds and for example corrupt heap silently. Or in a multithreaded program one thread can accidentially overwrite the stack of another thread. Memory accesses are unrestricted in C - if the address is mapped into the address space and is writeable your program can write there, you can't easily detect that.

sharptooth
+1  A: 

You do not specify a platform, but the GNU C library has a variable called __malloc_hook which is a function pointer on the form static void *my_malloc_hook(size_t, const void *).

An example: (taken from here)


static void *
my_malloc_hook (size_t size, const void *caller)
{
      void *result;
      /* Restore all old hooks */
      __malloc_hook = old_malloc_hook;
      __free_hook = old_free_hook;
      /* Call recursively */
      result = malloc (size);
      /* Save underlying hooks */
      old_malloc_hook = __malloc_hook;
      old_free_hook = __free_hook;
      /* printf might call malloc, so protect it too. */
      printf ("malloc (%u) returns %p\n", (unsigned int) size, result);
      /* Restore our own hooks */
      __malloc_hook = my_malloc_hook;
      __free_hook = my_free_hook;
      return result;
}

Using this you could allocate a bit more memory than necessary (that you'll need to keep track of somehow) and fill that space with some predefined marker value, returning a pointer some way into the space. Then, of course, you'd have to insert bounds checks at appropriate places in your code, which may not be what you want. Hope this gives some ideas at least.

manneorama
+3  A: 

One way to check is to allocate extra bytes before and after the buffer if you are using heap. But it will be difficult to keep track of every variable. After the function ends you can check if the data in those buffers was modified. You have to create a seperate library to hold these values for you.

Alternatively check this link. Hope it will give you more information on testing for buffer overflow.

EDIT : Some more information :

  1. Its difficult or rather not your job to test for APIs which dont take any input. However if the API takes input which will be manipulated during the course of the execution then you can pass values which can cause overrun.

    void foo()
    {

    char buffer [5];
    
    
     strcpy(buffer, "StackOverflow");
    
    
    // Clearly overflow. Has to be found out in Code reviews or static analysis
    
    
    }
    
Praveen S
This solution will work nicely for all heap related buffer overflows. I don't need to worry about keeping track of every variable as I already know which variables are involved in the buffer overflow (already determined through static analysis of the code).Is there any way to handle the simplest case where the overflowed array is on the stack?Thank you for the assistance :)
tPP
@tpp -That brings us to two questions.1) Is the code reviewed? Because if you are not passing any variable to the function and it fails, you need to hit that point by repeated testing.2) Do you know which variable you want to check for overflow?
Praveen S
1) No the code is not reviewed. 2) I do know the variable I want to check for overflow AND I know the variable does in fact overflow. I'm just not sure how to create a test case if the variable is on the stack. Thanks.
tPP
A: 

If you are running on Intel, you can run the code inside Valgrind. It has an experimental buffer overrun detector, so YMMV. I can't think of an easy way of detecting buffer overruns within C itself without adding lots of cruft to put in and detect guard variables.

JeremyP
+1  A: 

as already pointed out catching every buffer overflow is hard. In my previous project the compiler did not have options like the ones pointed by manneorama. Luckily the entire code base never called malloc directly, the code guidelines (strictly enforced) each malloc call had to call a function which by default used to call malloc (lets name it mymalloc).

So what we did was to create extra buffer size of lets say 4 bytes extra (2 before and 2 after the requested memory. Fill them with bytes you expect the code to not write (and this is where the solution is not sound)

At the top header file define a macro:

#define mymalloc(X) testmalloc(X,__FILE__,__LINE__)

and define a function testmalloc as follows:

void * testmalloc(size_t size, char *filename, int linenum)
{
   void *buff = malloc(size + 4)
   char *bytebuff = (char *) buff
   //bookkeeping, keep record of buff
   bytebuff[0] = 0xBA;
   bytebuff[1] = 0xBA;

   bytebuff[size+2] = 0xBA;
   bytebuff[size+3] = 0xBA;

   return bytebuff[2];
}

(this code is from my memory, I don't have the actual code with me so it might have minor mistakes)

And whenever you have to check for overflows just write a routine which goes through all such buffers and checks for the bytes. __FILE__ and __LINE__ preprocessing macro are to keep track of which line of the program causes overflow.

This doesn't ensure all overflows would be caught as:

  • program might overflow with the exact same byte (0xBA here)
  • program might keep the guard bytes intact but overflows beyond the range

It was easy task for us as highly disciplined code base made this code few minutes of effort and we caught some interesting bugs.

Also this mechanism is limited to heap allocated arrays.

thequark
A: 

You should make sure that all your buffer-writing functions have a defined size of buffer they use, whether it's by a variable or some constant.

For example, if you have

void writeSomeString(char * buf, size_t len);

you can test it respects the buffer size like this:

char testBuf[100];
memset(&testBuf, 42, sizeof(testBuf));
writeSomeString(&testBuf, sizeof(testBuf)-1);
assert(testBuf[sizeof(testBuf)-1] == 42);
che
A: 

The code you have presented is not unit-testable. You need to have control over what you need to test: parameters and return values.

If you really want to illustrate with C what other languages do, imagine functions that do something like:

int setArrayInt(int *array, unsigned int arraySize, unsigned int index, int value)
{
  if (index >= arraySize)
  {
    return -1; /* error */
  }

  array[index] = value;
  return 0; /* success */
}

You would then be able to unit test this function (cxxtest syntax):

/* Function returning failure if accessing out of range index */
TS_ASSERT_EQUALS(-1, setArrrayInt(exampleArray, sizeof(exampleArray), 10, 123));

/* Function returning success if we stay inside its range */
TS_ASSERT_EQUALS(-1, setArrrayInt(exampleArray, sizeof(exampleArray), 9, 123));
/* Our array really gets modified if our call is successful */
TS_ASSERT_EQUALS(123, exampleArray[9]);

Finally, you would use this function to manipulate arrays because you have validated it does that work correctly.

I am not implying you should create such array manipulation functions in your C programs. To catch the kind of mistakes you gave in example, you definitely need static analysis or code instrumentation tools.

Philippe A.