Linux Kernel Debug Configuration: Building a Debug Kernel for Safe Module Development
A complete guide to setting up a Linux 6.x debug kernel so you can catch bugs early — covering memory sanitizers, lock validators, ftrace, and the right development workflow.
🎓 What You Will Learn
- Why a debug kernel is essential for Linux kernel module development
- How the Linux 6.x kernel debug configuration system works
- Which CONFIG_* options to enable and exactly what each one does
- How memory sanitizers like KASAN catch kernel bugs at runtime
- How
lockdepdetects deadlocks and lock ordering violations - How to use ftrace to trace kernel function calls
- The correct two-kernel development workflow used by professional kernel engineers
make menuconfig and understand what a loadable kernel module (LKM) is. Basic C programming knowledge is assumed.
Why You Need a Debug Kernel for Linux Module Development
When you write a Linux kernel module, a bug does not just crash your program — it can panic the entire system, corrupt memory silently, or cause a deadlock that freezes everything. This is completely different from writing user-space applications where a segfault only kills your process.
In this free Linux kernel development course, we follow the same professional practice that Linux kernel maintainers use: always test your kernel module code on a debug kernel first. A debug kernel is a specially configured Linux kernel that has extra instrumentation turned on. These extra checks cost some performance, but they will catch bugs during development that would never show up on a production kernel — bugs that might only appear under very specific race conditions or memory access patterns.
The Linux 6.x kernel has a rich set of debug configuration options that have improved significantly compared to older versions like 5.4. This tutorial walks through what each group of options does, how to enable them in make menuconfig, and how to structure your workflow so you never ship buggy kernel code.
How Linux Kernel Debug Configuration Works
The Linux kernel’s entire build system is driven by a configuration mechanism called Kconfig. Each kernel feature or option is controlled by a CONFIG_* variable that can be set to y (built-in), m (module), or n (disabled). Debug options are almost always either y or n.
You interact with these options through the make menuconfig terminal UI. The debug-related options are grouped under the Kernel Hacking sub-menu. Here is an overview of how the configuration flows from your choices to the compiled kernel:
Developer runsmake menuconfig |
→ | Kernel Hacking Sub-menu |
→ | CONFIG_* options set in .config |
→ | Debug Kernel Compiled with Extra Checks |
When you enable a debug option, the kernel build system compiles additional code paths into the kernel image. These code paths perform extra checking at runtime — for example, checking every memory allocation boundary or validating every lock acquisition order. This is why a debug kernel is larger and slower than a production kernel.
| Step | Menu Path | What You Find Here |
|---|---|---|
| 1 | make menuconfig top level |
Root kernel configuration menu |
| 2 | Kernel hacking → | All kernel debug and tracing options |
| 3 | Kernel hacking → Memory Debugging | KASAN, SLUB_DEBUG, heap corruption detection |
| 4 | Kernel hacking → Lock Debugging | lockdep, lock statistics, atomic sleep checks |
| 5 | Kernel hacking → Tracers | ftrace, function graph tracer, IRQ off tracer |
Essential Debug Config Options for Linux 6.x Kernel Module Development
Let us go through each group of debug options. These are the options every serious Linux kernel developer enables during development. In Linux 6.x, some of these have improved significantly — for example, KASAN now supports both x86_64 and ARM64 architectures.
gdb, crash, or any debugger tool against the kernel. Without this, stack traces in kernel oops messages are much less useful. Always enable this during development.debugfs virtual filesystem, typically mounted at /sys/kernel/debug/. Many kernel subsystems and drivers expose diagnostic information through debugfs. It is also used by ftrace internally. You will often need this enabled to access real-time kernel debug data from user space.Memory Debugging Options — The Most Important Group
Memory bugs are the most dangerous and hardest-to-find class of kernel bugs. A use-after-free bug or a heap buffer overflow in a kernel module can corrupt memory that is used by a completely unrelated part of the kernel, causing a crash much later with no obvious connection to your code. The memory debugging options below catch these bugs immediately when they happen.
kmalloc() comes from here.| Every 8 bytes of kernel memory mapped to 1 byte of shadow memory | ||||
| Kernel Memory Byte 0-7 |
Kernel Memory Byte 8-15 |
Kernel Memory Byte 16-23 |
Redzones (invalid) |
Free’d region (poisoned) |
| ↓ | ↓ | ↓ | ↓ | ↓ |
| Shadow: 0x00 (valid) |
Shadow: 0x00 (valid) |
Shadow: 0x03 (partial) |
Shadow: 0xF1 (redzone) |
Shadow: 0xFD (freed) |
| On any access to poisoned shadow regions, KASAN immediately reports a bug with full stack trace | ||||
Lock Debugging Options — Catch Deadlocks Before They Happen
Locking bugs are another major category of kernel bugs. A deadlock can freeze your entire system with no error message and no obvious cause. The lock debugging options below make the kernel actively verify that lock acquisitions always follow a consistent order, catching potential deadlocks the first time a suspicious pattern is detected — even if the actual deadlock has not occurred yet.
CONFIG_LOCK_STAT and several other related options./proc/lock_stat. This is useful for performance optimization once your module is functionally correct.kmalloc() with GFP_KERNEL, or copy_from_user()), the kernel can deadlock or panic. This option makes the kernel emit a warning the moment it detects such a pattern.| Thread A | Thread B | lockdep Detection |
|---|---|---|
| Acquires Lock 1 ✓ | Acquires Lock 2 ✓ | Records: A holds L1, B holds L2 |
| Tries to acquire Lock 2 → BLOCKS |
Tries to acquire Lock 1 → BLOCKS |
⚠ lockdep reports circular dependency BEFORE deadlock! |
Tracing Options — See Exactly What Your Kernel Is Doing
BUG() and WARN() macros print the exact source file name and line number where they were triggered, in addition to the stack trace. Without this, you only get the function name. With it, you get the precise line of code that detected the bug.Additional Safety Options
-fno-omit-frame-pointer, which adds a small overhead but greatly improves debuggability.Quick Reference: All Debug Options at a Glance
| CONFIG Option | Category | What It Catches | Performance Impact |
|---|---|---|---|
CONFIG_DEBUG_INFO |
General | Enables readable stack traces and GDB support | Larger kernel image |
CONFIG_SLUB_DEBUG |
Memory | Heap overflows, double-free, use-after-free | Moderate (~10-15%) |
CONFIG_KASAN |
Memory | All memory safety violations immediately | High (~2x), uses ~1/8 RAM |
CONFIG_DEBUG_MEMORY_INIT |
Memory | Use of uninitialized memory | Low |
CONFIG_PROVE_LOCKING |
Locking | Potential deadlocks, lock ordering violations | Moderate |
CONFIG_DEBUG_ATOMIC_SLEEP |
Locking | Sleeping in atomic/spinlock context | Very Low |
CONFIG_FTRACE |
Tracing | Function call tracing and profiling | Low when inactive |
CONFIG_UBSAN |
Safety | Integer overflow, invalid shifts, misaligned access | Low-Moderate |
CONFIG_SCHED_STACK_END_CHECK |
Safety | Kernel stack overflow | Very Low |
CONFIG_BUG_ON_DATA_CORRUPTION |
Safety | Corrupted kernel data structures | Very Low |
Enabling Debug Options: Step-by-Step Commands for Linux 6.x
Open your Linux 6.x kernel source directory and run:
# Navigate to your kernel source directory
cd ~/linux-6.x
# Open the configuration menu
make menuconfig
Inside menuconfig, navigate to Kernel hacking and enable the options listed above. Alternatively, you can set them directly in your .config file. Here is a shell snippet that uses scripts/config to enable the most important ones automatically:
#!/bin/bash
# Enable essential debug options for Linux 6.x kernel development
# Run this inside your kernel source directory
./scripts/config --enable CONFIG_DEBUG_INFO
./scripts/config --enable CONFIG_DEBUG_FS
./scripts/config --enable CONFIG_MAGIC_SYSRQ
./scripts/config --enable CONFIG_DEBUG_KERNEL
./scripts/config --enable CONFIG_SLUB_DEBUG
./scripts/config --enable CONFIG_DEBUG_MEMORY_INIT
./scripts/config --enable CONFIG_KASAN
./scripts/config --enable CONFIG_PROVE_LOCKING
./scripts/config --enable CONFIG_LOCK_STAT
./scripts/config --enable CONFIG_DEBUG_ATOMIC_SLEEP
./scripts/config --enable CONFIG_STACKTRACE
./scripts/config --enable CONFIG_DEBUG_BUGVERBOSE
./scripts/config --enable CONFIG_FTRACE
./scripts/config --enable CONFIG_BUG_ON_DATA_CORRUPTION
./scripts/config --enable CONFIG_SCHED_STACK_END_CHECK
./scripts/config --enable CONFIG_UBSAN
./scripts/config --enable CONFIG_EARLY_PRINTK
./scripts/config --enable CONFIG_UNWINDER_FRAME_POINTER
# Regenerate .config to resolve any new dependencies
make olddefconfig
echo "Debug config applied. Now run: make -j$(nproc)"
CONFIG_DEBUG_INFO has been split into several sub-options (CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT, CONFIG_DEBUG_INFO_DWARF5, etc.). If scripts/config --enable CONFIG_DEBUG_INFO does not work on your exact kernel version, run make menuconfig and manually select the debug info format under Kernel hacking → Compile-time checks and compiler options → Debug information.
The Professional Two-Kernel Development Workflow
Having a debug kernel is not just about enabling options — it is about using the right kernel at the right time. Professional Linux kernel engineers and embedded Linux developers always maintain at least two kernel builds:
|
🛠
Debug Kernel
✓ KASAN enabled
✓ lockdep active ✓ SLUB_DEBUG on ✓ ftrace ready ✓ Full debug symbols ✗ Slower performance ✗ Higher RAM usage Use during development & testing
|
⇄ |
🚀
Production Kernel
✗ KASAN disabled
✗ lockdep off ✗ SLUB_DEBUG off ✗ Minimal tracing ✗ Stripped symbols ✓ Maximum performance ✓ Minimal RAM overhead Use for final validation & deployment
|
The workflow is:
- Write your kernel module code.
- Build and load it on your debug kernel. Run your tests. Let KASAN, lockdep, and UBSAN catch any bugs.
- Fix any bugs reported. Repeat until the module runs cleanly on the debug kernel.
- Build and load it on your production kernel. Run the same tests to confirm behavior under real-world conditions.
- Only declare the module ready for deployment after passing both stages.
What Changed from Linux 5.x to Linux 6.x for Debug Configuration
| Feature | Linux 5.4 (old reference) | Linux 6.x (current) |
|---|---|---|
| KASAN architecture support | x86_64 only officially | x86_64, ARM64, RISC-V, and more |
| KASAN mode | Classic shadow memory only | Classic + Hardware Tag-Based KASAN (ARM64) |
| Debug info format | Single CONFIG_DEBUG_INFO option | Multiple sub-options (DWARF4, DWARF5, split) |
| Stack unwinder | ORC default, frame pointer optional | ORC improved; frame pointer still available |
| lockdep scalability | Performance issues with many locks | Improved hash tables, better scalability |
| UBSAN checks | Basic set | Extended: local-bounds, pointer-overflow added |
Common Mistakes When Setting Up a Debug Kernel
WARN_ON() message, it means the kernel detected a suspicious condition. Do not ignore these — treat them as bugs to fix, even if the system keeps running.
CONFIG_DEBUG_ATOMIC_SLEEP catches. kmalloc(GFP_KERNEL) may sleep; if you call it while holding a spinlock, the kernel can deadlock. Use GFP_ATOMIC inside spinlock-protected sections.
Best Practices for Debug Kernel Configuration in Professional Kernel Development
- Keep a dedicated
.configfile for your debug kernel and check it into version control so your team uses consistent debug settings. - Use
scripts/configto script your configuration changes rather than manually editing.config— it is safer and reproducible. - After enabling new debug options, run
make olddefconfigto resolve any new dependency options that need to be set. - If you are working on embedded hardware with limited RAM, consider enabling KASAN only on your x86_64 development VM and doing ARM-specific testing without it.
- Enable
CONFIG_FTRACEeven if you are not actively tracing — it is lightweight when not in use, and having it available lets you quickly trace any function when something unexpected happens. - Run your module under stress conditions (concurrent access, memory pressure, rapid load/unload cycles) on the debug kernel to expose race conditions that do not show up under normal usage.
🌟 Key Takeaways
- A debug kernel is a Linux kernel built with extra runtime checking enabled — it is slower but catches bugs immediately instead of silently corrupting state.
- The essential debug groups are: basic info (
DEBUG_INFO,DEBUG_FS), memory safety (KASAN,SLUB_DEBUG), lock validation (lockdep,DEBUG_ATOMIC_SLEEP), and tracing (ftrace). - KASAN is the most powerful tool — it catches memory safety bugs the moment they happen using shadow memory instrumentation.
- lockdep validates lock ordering across the entire kernel and catches potential deadlocks before they actually freeze your system.
- The correct professional workflow is: develop and test on a debug kernel first, then validate on a production kernel before deployment.
- Linux 6.x has improved KASAN support (now includes ARM64), better UBSAN checks, and improved lockdep scalability compared to Linux 5.x.
Frequently Asked Questions
debugfs, typically at /sys/kernel/debug/tracing/. To start a basic function trace, run: echo function > /sys/kernel/debug/tracing/current_tracer and then read /sys/kernel/debug/tracing/trace. The trace-cmd user-space tool makes this much easier — it is the recommended interface for everyday ftrace use.CONFIG_DEBUG_SHIRQ tests that your interrupt handler correctly handles being called when the interrupt line is being shared with another device. When enabled, the kernel will deliberately call your IRQ handler an extra time during free_irq() to verify the handler correctly handles spurious invocations. Useful if you are writing interrupt-driven device drivers.slub_debug can be set as a kernel boot parameter without recompiling. For serious kernel module development, rebuilding with proper debug options is the right approach.References and Further Reading
- Linux Kernel Documentation: The Kernel Address Sanitizer (KASAN)
- Linux Kernel Documentation: Runtime Locking Correctness Validator (lockdep)
- Linux Kernel Documentation: ftrace — Function Tracer
- Linux Kernel Documentation: Undefined Behavior Sanitizer (UBSAN)
Continue Your Free Linux Kernel Development Journey
EmbeddedPathashala offers a completely free Linux kernel programming course covering everything from writing your first kernel module to advanced device drivers.
View Full Course Index Subscribe on YouTube