Test Malloc Failure with Macros in C

Last updated on December 8, 2022 pm

Test Malloc Failure with Macros in C

We already know that if malloc() failed, it will return NULL, so almost every C programmer would write the following snippet in their code:

1
2
3
4
5
char *string = (char *)malloc(10 * sizeof(char));
if (string == NULL) {
return;
}
// do something ...

However, if we want to do something else if the reture value is NULL for memory allocation? It is easy to put some code into it (i.e., line 3 of above snippet), but the real problem is, how can we test this part?

To make sure the robustness of our program, every part of the code should be massively tested. But it is almost impossible for us to run out of memory manually, we need some technique to emulate allocation failure.

And here is the way.

Handle malloc()

Code

Take a quick look at the code and I will explain them later.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdlib.h>

int set_null = 0;

void *my_malloc(size_t n) {
set_null += 1;
if (set_null == 497) {
return NULL;
}
return malloc(n);
}

#define malloc(x) my_malloc(x)

#include "file_to_test.h"

#undef malloc

int main(void) {
function_to_test();
return 0;
}

Explanation

Firstly, we need some basic knowledge of preprocessor and macro in c.

Don’t worry, in this context, they could be simply recognized as some kind of replacement. The essence is to replace the standard malloc function to a function defined by us, say my_malloc, so that we can make it return NULL if we want.

So in line 13, we specify that we want to replace the standard malloc() with our my_malloc(), and the parameter x is just a placeholder to tell compiler that we only have one argument to pass to the macro.

And secondly, we include our source code into our file. Note that we do not ‘close’ our macro (i.e., undef), so every time malloc() occurs in file_to_test.h, what actually called is our my_malloc() function.

Next step, we undefine malloc, which means give control to real malloc function. So for every code after this line, when compiler encounter a malloc(), it calls the genuine one.

Note that we define our my_maclloc() function before the macro, so in line 10, real malloc() is called.

Also, we need a way to decide when should our fake malloc should return NULL. We can have a global variable to track it, and when it fullfills some condition, function returns NULL.

At this point, you may have know that there are almost countless ways to do this. We can take modulo of it, use random function and so on. Here I simply check it’s value, and we can fake a allocation failure at anywhere we want.

Handle realloc()

Similar to malloc(), realloc can be replace with our own function, but it’s a little bit trickier.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int set_null = 0;

void *my_realloc(void *ptr, size_t n) {
set_null += 1;
if (set_null == 223) {
return NULL;
}
return realloc(ptr, n);
}

#define realloc(a, b) my_realloc(a, b)

#include "file_to_test.c"

#undef realloc

Note that the common usage of realloc is ptr_a = realloc(ptr_b, new_size); If this reallocation failed, ptr_a is set to NULL, but ptr_b is remaining unchanged.

1
void *realloc(void *ptr, size_t new_size);

Accroding to the documentation of realloc, original memory is never changed.

If there is not enough memory, the old memory block is not freed and null pointer is returned.

So in our simulation, ptr_a also remains unchanged.

1
2
3
4
5
6
7
8
9
10
int set_null = 0;

void *my_realloc(void *ptr, size_t n) {
set_null += 1;
if (set_null == 223) {
free(ptr); // don't do this!
return NULL;
}
return realloc(ptr, n);
}

Otherwise, you may pass your test but still get a memory leak when realloc failure occurs.

Remark on realloc()

Additionally, never do something like ptr_a = realloc(ptr_a, new_size); In this case, if real reallocation occurs, ptr_a would be set to NULL, whereas the memory location pointed by ptr_a is still there. The compiler is not intelligent enough to free(ptr_a) before assignment. Thus, we lose the access to original memory, and eventually we will receive a memory leak.

So always do like this:

1
2
3
4
5
temp = realloc(ptr_a, new_size);
if (temp == NULL) {
// handle realloc failure
}
ptr_a = temp;

References

  1. How to handle realloc when it fails due to memory? - Stack Overflow
  2. How to test malloc failure in C? - Stack Overflow
  3. Does realloc free the former buffer if it fails? - Stack Overflow
  4. c - How to handle realloc when it fails due to memory? - Stack Overflow
  5. Replacing text macros - cppreference.com
  6. Macros in C | Types and Examples - Scaler Topics
  7. The C Preprocessor
  8. #undef directive (C/C++) | Microsoft Learn
  9. C Preprocessor and Macros
  10. realloc - cppreference.com

Test Malloc Failure with Macros in C
https://lingkang.dev/2022/12/07/test-malloc-failure/
Author
Lingkang
Posted on
December 7, 2022
Licensed under