"Safe" memory allocation utilities (redux)
This is an update/rewrite of my previous post on this.
Typically, I do not really care if my memory allocation fails or not. I just want to assume it will succeed and I intentionally cause it to abort() on failure.
My standard library of allocation routines starts with the xmalloc(). A lot of other libraries and applications does the same.
It is implemented like so
static inline void *xmalloc(size_t sz) { void *mem; if (unlikely(!sz)) return NULL; mem = malloc(sz); if (unlikely(!mem)) backtrace_abort(); return mem; }
The unlikely() is a pretty standard macro by the way.
#define unlikely(cond) __builtin_expect(!!(cond), 0)
backtrace_abort() aborts and if running using glibc, it also prints a helpful backtrace.
#ifdef __GLIBC__ #include <execinfo.h> #endif void backtrace_abort(void) { #ifdef __GLIBC__ void *buf[10]; char **symbols = NULL; int size; size = backtrace(buf, 10); symbols = backtrace_symbols(buf, size); if (symbols) { fprintf(stderr, "fatal error; dumping at most %d stack frames\n", size); for (int i = 0; i < size; i++) fprintf(stderr, "[%d]: %s\n", i, symbols[i]); } free(symbols); #endif /* __GLIBC__ */ abort(); }
There is a zmalloc() as well that just uses calloc(1, sz) instead to zero the memory. However, I rarely use these directly. Instead, I typically allocate memory for quantity of something. When that something is just a number of bytes, I use a set of n-suffixed versions.
static inline void *mallocn(unsigned int n, size_t sz) { if (would_overflow(n, sz)) { fprintf(stderr, "allocation of %d * %zu bytes would overflow\n", n, sz); backtrace_abort(); } return xmalloc(n * sz); } static inline void *zmallocn(unsigned int n, size_t sz) { if (would_overflow(n, sz)) { fprintf(stderr, "allocation of %d * %zu bytes would overflow\n", n, sz); backtrace_abort(); } return zmalloc(n * sz); } static inline void *reallocn(void *mem, unsigned int n, size_t sz) { if (would_overflow(n, sz)) { fprintf(stderr, "allocation of %d * %zu bytes would overflow\n", n, sz); backtrace_abort(); } return realloc(mem, n * sz); }
For these allocation it is crucial that n times sz does not overflow, so they all verify that it does not.
static inline bool would_overflow(unsigned int n, size_t sz) { return n > SIZE_MAX / sz; }
Finally, when the something is a type, I use a set of new macros that are inspired by those in glib. This allows me to do something like void *foos = znew_t(struct foo, 42);.
#define _new_t(t, n, f) \ ((t *) f(n, sizeof(t))) #define new_t(t, n) _new_t(t, n, mallocn) #define znew_t(t, n) _new_t(t, n, zmallocn)
I've already mentioned this in another post, but since it is so useful, let me repeat it here for posterity.
static inline void __do_autofree(void *mem) { void **memp = (void **)mem; free(*memp); } #define __autofree __attribute__((cleanup(__do_autofree)))
Mark your pointers __autofree and watch them be free'ed when going out of scope. Hurrah!