Intermediate
Kernel Internals
Linux 6.x
Understanding Linux Kernel Space Stacks — A Complete Guide for Kernel and Driver Developers
When you write a Linux kernel module or a device driver, every function call you make consumes space on the kernel-mode stack. Unlike user-space programs that can grow their stack dynamically, the kernel-mode stack is fixed in size and very small. Misunderstanding this can crash your system or cause silent data corruption.
This tutorial is part of the free Linux kernel programming course at EmbeddedPathashala. By the end of this article, you will have a solid mental model of how kernel space manages stacks for every thread on the system.
What You Will Learn
- What the kernel space portion of the Virtual Address Space (VAS) looks like
- How many stacks exist on a running Linux system and why
- The difference between kernel-mode stacks and user-mode stacks
- How
struct task_structties to every thread on the system - Why kernel-mode stacks are small and what risks that creates
- The per-CPU interrupt stack and why it exists on x86 and ARM64
- How to use
CONFIG_FRAME_WARNto catch stack overflow risks at compile time - Stack overflow prevention best practices for kernel and driver developers
Prerequisites
- Basic understanding of Linux processes and threads
- Familiarity with Virtual Address Space (VAS) concepts — user space vs kernel space
- Some experience writing or reading Linux kernel module code is helpful but not required
How the Linux Kernel Organises Its Virtual Address Space
Every process running on a Linux system has its own Virtual Address Space (VAS). This VAS is divided into two regions: user space (where your application code runs) and kernel space (where the kernel itself resides). Kernel space is shared across all processes — the same kernel code and kernel data structures are mapped into the top portion of every process’s address space.
Within kernel space, the kernel must track and manage every thread that is currently alive on the system — both user-mode threads (threads belonging to user-space applications) and kernel threads (threads created entirely within the kernel itself, such as kworker, ksoftirqd, or kthreadd). Each of these threads requires dedicated resources inside kernel space, and the two most important ones are:
- A task structure —
struct task_struct— which stores all metadata about the thread - A kernel-mode stack — a small, fixed-size region of kernel memory used whenever that thread executes kernel code
| User Process P1 (main thread) |
User Process P2 (main + thrd2 + thrd3) |
Kernel Thread kthrd1 | Kernel Thread kthrd2 |
|---|---|---|---|
|
task_struct
(metadata for main) Kernel-mode
Stack (8KB on 64-bit) |
task_struct (main)
task_struct (thrd2)
task_struct (thrd3)
K-stack (main)
K-stack (thrd2)
K-stack (thrd3)
|
task_struct
(kernel thread) Kernel-mode
Stack (only stack it has) |
task_struct
(kernel thread) Kernel-mode
Stack (only stack it has) |
| User threads — each has BOTH a user-mode stack AND a kernel-mode stack | Kernel threads — ONLY have a kernel-mode stack (no user-space mapping) | ||
How Many Stacks Does a Running Linux System Have?
This is a question that surprises most people when they first encounter it. Let’s reason through it carefully because this is fundamental to understanding how the Linux kernel manages every thread on a live system.
Consider a snapshot of a running system where:
- There are 1,053 user-mode threads (threads belonging to user-space processes)
- There are 181 kernel threads (threads that live entirely in kernel space)
This gives a total of 1,234 threads on the system. How many stacks does this require?
| Thread Type | Count | User-mode Stack | Kernel-mode Stack | Total Stacks |
|---|---|---|---|---|
| User-mode threads | 1,053 | 1,053 ✓ | 1,053 ✓ | 2,106 |
| Kernel threads | 181 | None (no user mapping) | 181 ✓ | 181 |
| Grand Total Stacks on System → | 2,287 | |||
The key insight here is that every user-mode thread carries two stacks: one in user space (for executing user-space code) and one in kernel space (for executing kernel code on behalf of that thread — for example, during a system call). Kernel threads, on the other hand, live entirely in kernel space and have no user-space mapping at all, so they only get a single kernel-mode stack.
The task_struct — The Kernel’s Control Block for Every Thread
For every thread alive on the system — whether it is a user thread or a kernel thread — the Linux kernel maintains a data structure called struct task_struct. This is essentially the kernel’s “control block” for a thread. It stores everything the kernel needs to know about that thread: its state, its priority, its open file descriptors, its memory mappings, its signal handlers, and much more.
From our example system snapshot:
- There are 1,234 task structures in kernel memory — one per thread
- 1,053 of these represent user threads
- 181 of these represent kernel threads
task_struct of the currently executing task is accessible inside kernel code via the current macro. For example, current->pid gives you the PID of the currently running thread. This is one of the most frequently used macros in kernel and driver development.
What is a Kernel-Mode Stack and How Does it Work?
Every time a user-mode thread enters kernel space — through a system call, a hardware interrupt, a page fault, or any other exception — the CPU switches from using the thread’s user-mode stack to its dedicated kernel-mode stack. This is a fundamental mechanism of the x86 and ARM64 privilege level switch.
Key Properties of the Kernel-Mode Stack in Linux 6.x
| Property | 32-bit Linux | 64-bit Linux (x86_64, ARM64) |
|---|---|---|
| Size | 2 pages (typically 8 KB) | 4 pages (typically 16 KB) |
| Allocation time | At thread creation — during kernel_clone() (previously _do_fork() in older kernels) |
|
| Growth direction | Downward — toward lower virtual addresses (same as user-mode stacks) | |
| Size at runtime | Fixed — cannot grow dynamically | |
| Stack Pointer register | ESP (32-bit) | RSP on x86_64 / SP on ARM64 |
kernel_clone() rather than _do_fork(). The function _do_fork() was removed and its functionality merged into kernel_clone() in Linux 5.10. The kernel-mode stack is still allocated at thread creation time — this behaviour has not changed.
How a Stack Frame is Built on the Kernel Stack
Each time a function is called inside the kernel, the CPU sets up a stack frame on the kernel-mode stack. A stack frame contains:
- The return address (so the CPU knows where to jump back after the function returns)
- Saved registers (caller-saved or callee-saved, depending on the ABI)
- Local variables declared inside that function
- Any arguments passed on the stack (architecture-dependent)
Stack frame of sys_read() entry point
vfs_read()
ext4_file_read_iter()
Why is the Kernel-Mode Stack So Small? The Risks for Driver Developers
The kernel-mode stack is intentionally kept small. This is a deliberate design decision with important reasons:
- There can be thousands of kernel-mode stacks alive at any moment (one per thread). If each were large, the total memory footprint would be enormous.
- Kernel code is expected to be lean and efficient — deep recursion or large local arrays on the kernel stack are design mistakes, not features.
- A small stack encourages kernel developers to allocate large buffers on the heap (using
kmalloc()orvmalloc()) instead of the stack.
The kernel-mode stack is only 16 KB on 64-bit systems. Never declare large local arrays or buffers on the stack in kernel code. Use dynamic allocation (
kmalloc()) for anything beyond a few hundred bytes. Also, avoid deep recursion in kernel code — each recursive call adds another stack frame and can exhaust the stack silently, leading to a kernel panic or memory corruption.
Catching Stack Overflow Risk at Compile Time: CONFIG_FRAME_WARN
The Linux kernel build system includes a configurable option called CONFIG_FRAME_WARN that instructs GCC to emit a warning whenever a single function’s stack frame exceeds a specified size. This is an important safety net for kernel and driver developers.
# In your kernel .config or via menuconfig:
# Kernel hacking → Compile-time checks and compiler options → Warn for stack frames larger than (bytes)
CONFIG_FRAME_WARN=1024 # Warn if any stack frame exceeds 1024 bytes
When enabled, GCC will produce a build-time warning like:
drivers/mydriver/mydriver.c:128:1: warning: the frame size of 1280 bytes is larger than 1024 bytes [-Wframe-larger-than=]
CONFIG_FRAME_WARN enabled and treat any such warning as a bug that must be fixed. Refactor large stack frames to use kmalloc()-allocated memory instead.
What to Do When You Need Large Buffers in Kernel Code
/* ❌ Wrong — large buffer on kernel stack (dangerous!) */
static int my_driver_read(struct file *f, char __user *buf, size_t count, loff_t *pos)
{
char tmp[4096]; /* 4 KB on a 16 KB stack — very risky */
/* ... */
}
/* ✅ Correct — dynamically allocated on the kernel heap */
static int my_driver_read(struct file *f, char __user *buf, size_t count, loff_t *pos)
{
char *tmp;
tmp = kmalloc(4096, GFP_KERNEL);
if (!tmp)
return -ENOMEM;
/* ... use tmp ... */
kfree(tmp);
return 0;
}
The Per-CPU Interrupt Stack — A Third Type of Stack in the Linux Kernel
Beyond user-mode stacks and kernel-mode stacks, modern Linux on architectures like x86_64 and ARM64 also maintains a per-CPU interrupt stack. This is a separate, dedicated stack used specifically when a hardware interrupt is being handled.
Why Does the Interrupt Stack Exist?
When a hardware interrupt fires — say, from a network card or a disk controller — the CPU’s interrupt mechanism takes control away from whatever thread was running and begins executing the interrupt service routine (ISR). If this ISR were to use the interrupted thread’s kernel-mode stack, it would put additional pressure on an already small 16 KB stack. In deeply nested interrupt scenarios, this could easily overflow.
The solution: each CPU has its own dedicated interrupt stack that is separate from any thread’s kernel-mode stack. When an interrupt fires, the CPU switches to this per-CPU interrupt stack for the duration of the interrupt handling.
(separate, dedicated)
(separate, dedicated)
Complete Summary: Stacks and Task Structures on a Linux System
| Resource | User Thread | Kernel Thread |
|---|---|---|
struct task_struct |
✓ One per thread (in kernel memory) | ✓ One per thread (in kernel memory) |
| User-mode stack | ✓ Dynamic, in user space | ✗ None — no user-space mapping |
| Kernel-mode stack | ✓ Fixed, 16 KB on 64-bit | ✓ Fixed, 16 KB on 64-bit |
| Per-CPU interrupt stack | Shared per CPU (not per thread) — used during hardware interrupt handling | |
Best Practices for Linux Kernel and Driver Stack Management
- Never allocate large local arrays on the kernel stack. Use
kmalloc()orvmalloc()for buffers larger than a few hundred bytes. - Avoid deep recursion in kernel code. Convert recursive algorithms to iterative ones if they will be called in kernel context.
- Enable
CONFIG_FRAME_WARNin your kernel build configuration during development and fix all warnings before shipping a driver. - Use
CONFIG_KASAN(Kernel Address Sanitizer) during development to detect stack overflows and other memory errors early. - Keep interrupt handlers short. If heavy processing is needed, defer it using workqueues or tasklets — which get their own kernel stacks — rather than doing it directly in the interrupt handler.
- Check
/proc/<pid>/stackduring debugging to see the current kernel-mode stack of any thread (requires root and appropriate kernel configuration).
Common Mistakes Made by Kernel and Driver Developers
Interrupt handlers and softirq handlers run on the per-CPU interrupt stack. Allocating large structures here is especially dangerous because the interrupt stack is typically the same size or smaller than the kernel-mode stack.
When you pass a large structure by value to a kernel function, a copy is placed on the kernel stack. Pass pointers instead and use dynamic allocation for the structure itself.
User-mode stacks can grow dynamically (the kernel will allocate more pages via demand paging when a guard page is hit). The kernel-mode stack cannot grow — hitting its limit causes a kernel panic or silent corruption. There is no guard page mechanism for the kernel-mode stack in the same way.
Key Takeaways
- Every thread (user or kernel) has exactly one
struct task_structin kernel memory. - Every user-mode thread has two stacks: a user-mode stack and a kernel-mode stack.
- Kernel threads have only one stack: a kernel-mode stack.
- Kernel-mode stacks are fixed in size: 8 KB on 32-bit systems, 16 KB on 64-bit systems.
- They are allocated at thread creation time via
kernel_clone()in Linux 6.x. - Modern Linux on x86_64 and ARM64 uses a per-CPU interrupt stack to isolate interrupt handling from thread stacks.
- Use
CONFIG_FRAME_WARNto catch stack frame size issues at compile time. - Always use
kmalloc()for large buffers — never the kernel stack.
Conclusion
Understanding how Linux manages kernel space stacks is foundational knowledge for anyone pursuing Linux kernel programming or Linux device driver development. The kernel-mode stack underpins every system call, every interrupt handler, and every line of code in your kernel module. Its small, fixed size is both a constraint and a discipline that pushes kernel developers toward clean, efficient code.
In the next article in this free Linux kernel development course, we will look at how to actually view and inspect both the kernel-mode and user-mode stacks of any running thread using the /proc filesystem and modern eBPF-based tools.
Frequently Asked Questions (FAQ)
On a 64-bit system (x86_64 or ARM64) running Linux 6.x, the kernel-mode stack is 4 pages × 4 KB = 16 KB per thread. On 32-bit systems it is 2 pages × 4 KB = 8 KB.
No. Kernel threads have no user-space mapping and therefore no user-mode stack. They execute entirely in kernel space and only have a kernel-mode stack.
A kernel stack overflow typically leads to a kernel panic, a system hang, or silent memory corruption — all of which are catastrophic. Unlike user-mode stack overflows (which generate a segfault the OS can catch), kernel stack overflows have no safe recovery mechanism.
The kernel-mode stack is allocated when the thread is created, inside kernel_clone() (Linux 5.10 and later). In older kernels this was done inside _do_fork(). Once allocated, the stack size does not change.
CONFIG_FRAME_WARN is a kernel build option that instructs GCC to emit a compile-time warning if any function’s stack frame exceeds a specified size (e.g., 1024 bytes). This helps kernel and driver developers catch potential stack overflow risks before they cause runtime problems.
On architectures like x86_64 and ARM64, each CPU core has a dedicated interrupt stack that is used only when handling hardware interrupts. This prevents the interrupt handler from consuming space on the interrupted thread’s already small kernel-mode stack.
kmalloc() allocates memory from the kernel’s heap (slab allocator), which is much larger than the 16 KB kernel stack. Use kmalloc(GFP_KERNEL) for buffers that would otherwise be too large for the stack. Always check for allocation failure and free with kfree().
Yes. The Linux kernel exposes the kernel-mode call stack of any thread via /proc/<pid>/stack. This requires root access. On modern kernels, this is restricted to root by default to prevent information leakage. We cover this in the next tutorial in this series.
The task_struct contains (or points to) the kernel-mode stack through its stack field, which points to the base of the thread’s kernel stack. The stack itself is a separate allocation — task_struct is the metadata, the stack is the execution resource.
On a production server with thousands of threads (which is common), you can have thousands of kernel-mode stacks. Each consumes 16 KB of kernel memory. A server with 5,000 threads would have up to 80 MB of memory consumed by kernel stacks alone — which is why keeping them small is important.
References and Further Reading
- Linux Kernel Documentation: x86 Kernel Stacks
- Linux Kernel Core API Documentation
- struct task_struct definition — Elixir Linux Cross-Reference
Continue the Free Linux Kernel Programming Course
EmbeddedPathashala offers 100% free, in-depth courses on Linux kernel internals, Linux device drivers, and embedded systems programming.
Next: Viewing Kernel Stacks via /proc → ← Back to Course Index