How Tech - Systems Programming

How Tech - Systems Programming

Setting Up Automated Static Analysis Tools for Kernel Code Integrity

Jun 19, 2026
∙ Paid

A kernel bug doesn’t stay contained. When ring 0 code dereferences a bad pointer, the panic takes every process on the machine with it. When a driver writes through a stale pointer to a page that got recycled, you corrupt memory belonging to an entirely different process. There’s no MMU boundary between kernel subsystems, no segfault to stop the bleeding. One unvalidated pointer propagates until the machine reboots or—worse—continues running with corrupted state that eventually shows up as a production incident three weeks later.

Code review catches intent problems. Static analysis catches structural ones: the kind where the logic looks plausible but the type system is telling you something is wrong at a level human reviewers miss after the fifth consecutive hour of staring at patches.


Sparse: The Kernel’s Own Type Checker

Sparse isn’t a general-purpose linter. Linus Torvalds wrote it specifically to enforce the kernel’s pointer annotation system. Run make C=2 in any kernel tree and sparse checks every file that gets compiled. The integration is that tight.

The core insight is that C’s type system treats all pointers as equivalent. Kernel code operates across several distinct address spaces—kernel virtual address space, user virtual address space, MMIO, RCU-protected regions—and the compiler won’t tell you when you mix them up. Sparse adds a parallel type layer on top through annotations: __user, __iomem, __rcu, __percpu. These live in include/linux/compiler.h and activate only when __CHECKER__ is defined.

#ifdef __CHECKER__
# define __user   __attribute__((noderef, address_space(1)))
# define __iomem  __attribute__((noderef, address_space(2)))
# define __rcu    __attribute__((noderef, address_space(4)))
#else
# define __user
# define __iomem
# define __rcu
#endif

The noderef attribute tells sparse that direct dereference is illegal. address_space(1) marks it user-space. When you write return *uptr on a const u32 __user *uptr, sparse stops you:

buggy.c:31: error: dereference of noderef expression
buggy.c:47: error: incorrect type in argument 1 (different address spaces)
   expected char const *kname
   got char [noderef] <asn:1>*uname

That second error is the one that causes CVEs. A function that expects a kernel pointer receives a user pointer, dereferences it in kernel context, and you’ve got a kernel read to an attacker-controlled address. The correct path is copy_from_user(), which validates the address against the process’s VMA table before touching it.


User's avatar

Continue reading this post for free, courtesy of Systems.

Or purchase a paid subscription.
© 2026 Sumedh S · Privacy ∙ Terms ∙ Collection notice
Start your SubstackGet the app
Substack is the home for great culture