Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit testing for failed malloc()

What is the best way for unit testing code paths involving a failed malloc()? In most instances, it probably doesn't matter because you're doing something like

thingy *my_thingy = malloc(sizeof(thingy));
if (my_thingy == NULL) {
  fprintf(stderr, "We're so screwed!\n");
  exit(EXIT_FAILURE);
} 

but in some instances you have choices other than dying, because you've allocated some extra stuff for caching or whatever, and you can reclaim that memory.

However, in those instances where you can try to recover from a failed malloc() that you're doing something tricky and error prone in a code path that's pretty unusual, making testing especially important. How do you actually go about doing this?

like image 338
Pillsy Avatar asked Nov 10 '09 21:11

Pillsy


4 Answers

This is a kinda gross, but if you really want unit testing, you could do it with #ifdefs:

thingy *my_thingy = malloc(sizeof(thingy));
#ifdef MALLOC_UNIT_TEST_1
my_thingy = NULL;
#endif
if (my_thingy == NULL) {
  fprintf(stderr, "We're so screwed!\n");
  exit(EXIT_FAILURE);
}

Unfortunately, you'd have to recompile a lot with this solution.

If you're using linux, you could also consider running your code under memory pressure by using ulimit, but be careful.

like image 87
Seth Avatar answered Sep 19 '22 02:09

Seth


I saw a cool solution to this problem which was presented to me by S. Paavolainen. The idea is to override the standard malloc(), which you can do just in the linker, by a custom allocator which

  1. reads the current execution stack of the thread calling malloc()
  2. checks if the stack exists in a database that is stored on hard disk
    1. if the stack does not exist, adds the stack to the database and returns NULL
    2. if the stack did exist already, allocates memory normally and returns

Then you just run your unit test many times: this system automatically enumerates through different control paths to malloc() failure and is much more efficient and reliable than e.g. random testing.

like image 45
Antti Huima Avatar answered Sep 19 '22 02:09

Antti Huima


write your own library that implements malloc by randomly failing or calling the real malloc (either staticly linked or explicitly dlopened)

then LD_PRELOAD it

like image 31
pm100 Avatar answered Sep 22 '22 02:09

pm100


I suggest creating a specific function for your special malloc code that you expect could fail and you could handle gracefully. For example:

void* special_malloc(size_t bytes) {
  void* ptr = malloc(bytes);
  if(ptr == NULL) {
    /* Do something crafty */
  } else {
    return ptr;
  }
}

Then you could unit-test this crafty business in here by passing in some bad values for bytes. You could put this in a separate library and make a mock-library that does behaves special for your testing of the functions which call this one.

like image 31
Dave Avatar answered Sep 22 '22 02:09

Dave