CVE-2021-47274 - Linux Kernel Memory Corruption in Tracing Subsystem (A Deep Dive)
The stability and security of the Linux kernel is crucial for millions of servers and devices worldwide. In early 2021, a kernel vulnerability was discovered—eventually assigned as CVE-2021-47274—that caused severe memory corruption and hard-to-debug system crashes in production environments. This post explains, in practical and accessible terms, how the bug was introduced, the kernel panic symptoms, the technical root cause, and how it was fixed.
What Was CVE-2021-47274?
CVE-2021-47274 is a critical memory corruption vulnerability in the Linux kernel's tracing subsystem. Specifically, the bug was caused by an incorrect length check when handling trace buffer entries, leading to out-of-bounds writes. Such memory errors can destabilize the kernel, introduce subtle data corruption, or even allow for local privilege escalation in some scenarios.
Real-World Signs: Kernel Crashes in Action
Production environments hit by this bug saw unpredictable crashes, general protection faults, and kernel panics, often with cryptic crash logs like this (edited for clarity):
Call Trace:
general protection fault: 000 [#1] SMP PTI
CPU: 17 PID: 26996 Comm: python Kdump: loaded Tainted:G
RIP: 001:kmem_cache_alloc+x90/x190
RSP: 0018:ffffb16faa597df8 EFLAGS: 00010286
...
Call Trace:
anon_vma_clone+x5d/x170
__split_vma+x91/x1a
do_munmap+x2c6/x320
vm_munmap+x54/x70
__x64_sys_munmap+x22/x30
do_syscall_64+x5b/x1b
entry_SYSCALL_64_after_hwframe+x44/xa9
RIP: 0033:x7f45d6e61e27
Crucially, the issue would often be triggered by heavy tracing or dynamic instrumentation workloads, like those using kprobes or ftrace.
How Was the Bug Tracked Down?
James Wang and others reproduced the bug reliably on Linux 4.19 LTS. A detailed backtrace with debug tools showed clear evidence of out-of-bounds memory writes:
[ 86.775200] BUG: Out-of-bounds write at addr xffff88aefe8b700
[ 86.792145] page_fault+x1e/x30
[ 86.795576] strncpy_from_unsafe+x66/xb
[ 86.799789] fetch_memory_string+x25/x40
[ 86.804002] fetch_deref_string+x51/x60
[ 86.808134] kprobe_trace_func+x32d/x3a
...
The failures appeared during tracing operations, particularly with code paths related to storing trace filter data.
The Root Cause: A Length Check Gone Wrong
Previous patches (notably b220c049d519) attempted to avoid buffer overflows by adding length checks when allocating space for new trace entries:
if (len > TRACE_BUF_SIZE)
return -EINVAL;
entry = kmem_cache_alloc(trace_entry_cache, GFP_KERNEL);
However, the check was incomplete. It didn't account for an extra element in the data structure (specifically, entry->array[]), which stores the actual length of the trace data—meaning the buffer could still be overrun by precisely the size of one element. As a result, the kernel could write past the allocated memory chunk, possibly clobbering unrelated structures.
Incorrect Length Check (the buggy case)
if (len > TRACE_BUF_SIZE)
return -EINVAL;
entry = kmem_cache_alloc(trace_entry_cache, GFP_KERNEL);
// writes len+1 entries into entry->array[]
Corrected Length Check (the fix)
// Fix: Reserve room for array[], which holds the length.
if (len + 1 > TRACE_BUF_SIZE)
return -EINVAL;
entry = kmem_cache_alloc(trace_entry_cache, GFP_KERNEL);
Because array[] is always filled, the correct check is len + 1 > size, not just len > size.
While no public exploit code is known, the security impact is serious
- Out-of-bounds Write: Writing past the end of a kernel buffer can corrupt neighboring kernel structures, leading to unpredictable behaviors, denial of service, or potentially code execution with kernel privileges.
- Trigger via Tracing: Local users with the ability to use ftrace or kprobes can likely hit this bug by crafting long enough filter strings, or by triggering massive trace data events.
- Easy to Abuse in Debug or Cloud Environments: Systems with active tracing, debugging, or observability agents are especially at risk.
> Note: The classic demonstration would involve writing a userspace program that triggers long trace events, causing the kernel to crash or misbehave—an avenue for DoS, privilege escalation, or even persistence mechanisms for attackers.
The Official Fix
The problem was patched in mainline and LTS kernels. The essential fix (commit link) simply includes the extra length slot in the check:
- if (len > TRACE_BUF_SIZE)
+ if (len + 1 > TRACE_BUF_SIZE)
return -EINVAL;
All modern distributions have now integrated the patch. If you run *any* kernel with version 4.19.x, make sure you have the latest security updates applied (preferably after June 2021).
References and Further Reading
- CVE-2021-47274 entry at NVD
- Upstream kernel commit b220c049d519 ("tracing: Check length before giving out the filter buffer")
- Upstream kernel commit with the correct length check fix
- Linux Kernel Mailing List - Original Patch Discussion
Conclusion
CVE-2021-47274 highlights how even a small off-by-one error in a complex kernel subsystem can bring down mission-critical servers or open the door to attacks. If you use Linux 4.x or any kernel with tracing features, double-check for the fix. In complex systems, staying on top of LTS kernel updates is your best defense.
Stay safe, keep your systems patched, and keep learning!
*If you want to keep up with more kernel vulnerabilities in an understandable way, follow this blog or watch the Kernel Newbies and LKML mailing lists for updates.*
Timeline
Published on: 05/21/2024 15:15:15 UTC
Last modified on: 07/03/2024 01:37:32 UTC