Week 21 - Loadable Kernel Modules (LKM)¶
21.1 Conceptual Core¶
- A loadable kernel module is C code compiled against the running kernel's headers, loaded via
insmod/modprobe, and unloaded viarmmod. It runs at ring 0, with full kernel privileges. There is no safety net: a bug crashes the box. - The legitimate uses for LKMs in 2026 are narrow: device drivers for hardware not supported by mainline, niche filesystem additions, specific tracing or security modules that cannot be expressed as eBPF.
- For most "I want to extend the kernel" needs today, eBPF is the right answer. Pick LKM only when you genuinely cannot express the work in eBPF.
21.2 Mechanical Detail¶
- The skeleton:
- Build via a
Makefile: - Character device skeleton: register a
cdev, definefile_operations(open,release,read,write,unlocked_ioctl), allocate adev_tviaalloc_chrdev_region, create aclassanddeviceso udev creates/dev/<name>. - Locking:
spin_lock(interrupt context),mutex_lock(sleepable),rcu_read_lock(read-mostly). Choosing wrong is a deadlock or a soft-lockup. - Memory:
kmalloc(small, contiguous),vmalloc(large, possibly non-contiguous),kmem_cache_*(slab). Account in/proc/slabinfo. - Signing & secure boot: production hosts with secure boot will reject unsigned modules. Sign with
scripts/sign-fileagainst a MOK (Machine Owner Key).
21.3 Lab-"A Character Device LKM"¶
Write pkv, a simple in-kernel key/value character device:
- /dev/pkv accepts writes of the form key=value\n and reads return the value for the last-written key.
- 100 KV slots, in-kernel hash table.
- Concurrency-correct under multiple writers/readers (use a mutex; pursue an rwlock variant as a stretch).
- ioctl operations for LIST and DELETE.
- KUnit tests in tree.
- Loads/unloads cleanly with no lockdep or KASAN warnings (turn both on in your test kernel).
21.4 Hardening Drill¶
- Read CVE history for
stagingdrivers; identify three classes of bugs that recur (UAF afterkfreeon error path, missing copy_from_user length check, integer overflow). Audit yourpkvfor each.
21.5 Performance Tuning Slice¶
- Run
bpftracewith kprobes on your module's functions; measure per-op latency. Compare against an equivalent userspace KV (e.g., a Unix-socket server).