Dark Refraction

Mixing glib's memory allocations with clang's code analysis

Glib provides wrappers around common memory allocation functions like malloc and free. Glib's functions have several benefits; there's no undefined behavior when you g_malloc(0), and they make it much easier to swap out different implementations of malloc.

The wrappers do have one large downside - they make it difficult for automated tools to tell what's going on in the program. In particular, I wanted to use clang's code analyzer, scan-build, to find memory leaks in the GIMP.

Essentially, what I needed was for clang to recognize that certain functions were essentially the same as the stdlib variants. I ended up using a series of #defines that simply rewrite parts of the code.

So, I put these #defines into a file:

/* Don't include gmem.h after this, it will screw things up */
#define __G_MEM_H__

#include <stdlib.h>

/* Redefine the glib versions to stdlib versions */
#define g_malloc               malloc
#define g_malloc0(x)           calloc(1,x)
#define g_realloc(x,y)         realloc(x,y)
#define g_free                 free
#define g_try_malloc(x)            malloc(x)
#define g_try_malloc0(x)       calloc(1,x)
#define g_malloc_n(x,y)            malloc((x)*(y))
#define g_malloc0_n(x,y)       calloc(1,(x)*(y))
#define g_realloc_n(x,y,z)     realloc(x,(y)*(z))
#define g_try_malloc_n(x,y)        malloc((x)*(y))
#define g_try_malloc0_n(x,y)   calloc(1,(x)*(y))
#define g_try_realloc_n(x,y,z) realloc(x,(y)*(z))

/* g_new and friends, copied from glib */
#define _G_NEW(struct_type, n_structs, func) \
        ((struct_type *) g_##func##_n ((n_structs), sizeof (struct_type)))
#define _G_RENEW(struct_type, mem, n_structs, func) \
        ((struct_type *) g_##func##_n (mem, (n_structs), sizeof (struct_type)))

#define g_new(struct_type, n_structs)          _G_NEW (struct_type, n_structs, malloc)
#define g_new0(struct_type, n_structs)         _G_NEW (struct_type, n_structs, malloc0)
#define g_renew(struct_type, mem, n_structs)   _G_RENEW (struct_type, mem, n_structs, realloc)
#define g_try_new(struct_type, n_structs)      _G_NEW (struct_type, n_structs, try_malloc)
#define g_try_new0(struct_type, n_structs)     _G_NEW (struct_type, n_structs, try_malloc0)
#define g_try_renew(struct_type, mem, n_structs) _G_RENEW (struct_type, mem, n_structs, try_realloc)

Then, whenever I want to do static analysis, I simply need to export a cflag that includes that header:

export CFLAGS="-include ~/comp/gimp/llvm/define.h"

Then, I run scan-build normally, and it picks up tons of memory allocation bugs. Hooray!

Also note that clang now gives me a ton of false positives for errors relating to malloc (0), which isn't an issue with g_malloc. It's safe to ignore those.

This is a rather hacky way to do it, but it works for my purposes. If your program uses a function pointer with any of the functions other than g_free or g_malloc, this will fail with undefined function errors. I'm sure it's possible to make such programs compile using these headers, but it would be rather messy.

Ideally, I think the compiler could have a set of attributes that specify certain functions allocate or free memory, which could then be used to aid code analysis, and potentially optimization. (I know gcc already has an malloc attribute, but AFAIK scan-build doesn't use it, and no equivalent for free or realloc exists.) Until then, this works for me.