Skip to content

Instantly share code, notes, and snippets.

@eddieantonio
Last active April 10, 2019 16:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save eddieantonio/c6b2688c0b1777347cfac6d01e802e8d to your computer and use it in GitHub Desktop.
Save eddieantonio/c6b2688c0b1777347cfac6d01e802e8d to your computer and use it in GitHub Desktop.
nonullfree.c -- throw errors when you call free(NULL)
/**
* 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