irrelevant


Static Analysis with Sparse

I use the sparse static analyzer in the projects where I can get away with it. I do my fair share of driver and hardware emulation code, so the ability of sparse to locate endianness bugs and address space mixups is very valuable to me.

To get some good use out of sparse, I typically have a header file called compiler.h with something like this:

#ifdef __CHECKER__
# define __private      __attribute__((noderef))
# define __nocast       __attribute__((nocast))
# define __force        __attribute__((force))
# define __bitwise      __attribute__((bitwise))
#else
# define __private
# define __nocast
# define __force
# define __bitwise
#endif

This is pretty similar to what the kernel does, but tailored for user space code. __CHECKER__ is only set when sparse is the one compiling the code, so whenever we compile with any other compiler, the annotations simply disappear.

I use the __bitwise annotation for types where I basically want to deter the compiler from being clever. Say, for endian types:

typedef uint64_t __bitwise leint64_t;

To any regular compiler, the above just tells it that leint64_t is an uint64_t and it will happily allow me mix those up. But I really do not want to use a little endian type where it is not explicitly expected. Using sparse, the above allows me to detect such mistakes and requires that I explicitly convert from the little endian value to native endianness. Assuming we are on a little endian host, this is pretty trivial:

#define le64_to_cpu(le) ((__force uint64_t)(le))

But, do not write this up yourself, use ccan/endian/endian.h instead. It already has sparse annotations baked right in.

meson'ifying it

As should be apparent to anyone that has read any of my other articles here, this is a meson shop, so we are going to integrate sparse.

We are going to rely on scripts/check_sparse.py from QEMU. It wraps the invocation of sparse to take care of some annoying sparse parameter parsing. I typically use a bunch of ccan code in my projects, so I apply something like this patch to allow me filter out directories.

For sparse, we add a run_target:

sparse = find_program('cgcc', required: false)
if sparse.found()
        run_target('sparse', command: [
                find_program('scripts/check_sparse.py'), 'compile_commands.json',
                sparse.full_path(),
        ])
endif

Now, to run sparse on your code, invoke meson compile:

meson compile -C builddir sparse

That's it! Enjoy!