Last active
April 10, 2019 16:14
-
-
Save eddieantonio/c6b2688c0b1777347cfac6d01e802e8d to your computer and use it in GitHub Desktop.
nonullfree.c -- throw errors when you call free(NULL)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* nonullfree.c -- throw errors when you call free(NULL) | |
* | |
* BUILDING: | |
* | |
* $ gcc -shared -fPIC nonullfree.c -ldl -o nonullfree.so | |
* | |
* USAGE (Linux): | |
* | |
* $ LD_PRELOAD=./nonullfree.so ./my-program | |
* | |
* | |
* HOW TO USE WITH gdb: | |
* | |
* First, compile YOUR program with -g: | |
* | |
* $ gcc -g my-program.c -o my-program | |
* | |
* Then start gdb like normal: | |
* | |
* $ gdb my-program | |
* | |
* And set the environment variable before running the program: | |
* | |
* (gdb) set environment LD_PRELOAD ./nonullfree.so | |
* (gdb) run | |
* | |
* When a something calls free() with a NULL pointer, you will see this | |
* message: | |
* | |
* Called free() on a NULL pointer! | |
* | |
* Program received signal SIGTRAP, Trace/breakpoint trap. | |
* | |
* In gdb, inspect the stack trace, and find where the actual call to free() | |
* occured: | |
* | |
* (gdb) backtrace | |
* #0 0x00007ffff7840428 in __GI_raise (sig=5) at ../sysdeps/unix/sysv/linux/raise.c:54 | |
* #1 0x00007ffff7bd58c0 in free () from ./nonullfree.so | |
* #2 0x00000000004005f6 in main () at my-program.c:8 | |
* | |
* In this example, frame 0 raised the error -- you may ignore this. Frame 1 is | |
* the "fake" free(), defined in this file. Therefore, frame 2 is the culprit! | |
* Switch to frame 2: | |
* | |
* (gdb) frame 2 | |
* #2 0x00000000004005f6 in main () at my-program.c:8 | |
* 8 free(p); | |
* | |
* Ah, what's the value of `p`? | |
* | |
* (gdb) print p | |
* $1 = (void *) 0x0 | |
* | |
* Bingo! | |
*/ | |
#define _GNU_SOURCE // Required for RTLD_NEXT | |
#include <dlfcn.h> | |
#include <signal.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
/** | |
* This is the TRUE free(void*). | |
* | |
* It is dynamically assigned in on_load(). | |
*/ | |
static void (*libc_free)(void*) = NULL; | |
#define BOLDRED "\033[1;31m" | |
#define RESET "\033[m" | |
/** | |
* Our version of free(void*) that checks for NULL. | |
*/ | |
void free(void *p) { | |
if (p == NULL) { | |
fprintf(stderr, BOLDRED "Called free() on a NULL pointer!" RESET "\n"); | |
raise(SIGTRAP); | |
} | |
libc_free(p); | |
} | |
////////////////////////////////// INTERNAL ////////////////////////////////// | |
__attribute__((constructor)) static | |
void on_load() { | |
// Get the NEXT definition of free(). | |
libc_free = dlsym(RTLD_NEXT, "free"); | |
if (libc_free == NULL) { | |
perror(dlerror()); | |
abort(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment