In early 2021, engineers working on the Linux kernel discovered a tricky bug on ARM devices, now tracked as CVE-2021-46910. The vulnerability affects how per-CPU virtual memory is set up for temporary mappings (using kmap_local()), especially when extra debugging checks are enabled. While the bug has now been fixed, understanding what went wrong is essential for anyone dealing with embedded or ARM-based Linux systems.
Background: What Went Wrong?
The kmap_local() function offers a way for the kernel to temporarily map high memory pages into a process's address space. It uses a system known as the "fixmap region." For ARM, if you turned on debugging with CONFIG_DEBUG_KMAP_LOCAL, the number of per-CPU slots doubled—half for actual use, half as "guard regions" to help catch bugs.
But here’s the catch: on systems with lots of CPUs (like NR_CPUS=32), this region _grew too large_. The fixmap region started growing downward in memory, bumping into areas used by other critical kernel systems, such as the Device Tree or EFI tables.
That caused the kernel to _misinterpret_ normal memory blocks as fixmap table entries—a recipe for chaos! When that happened, the kernel would crash with impossible-to-debug errors during early boot, especially on ARM devices using EFI.
Here’s what you might see on a failed boot
ftrace: allocating 45548 entries in 89 pages
8<--- cut here ---
Unable to handle kernel paging request at virtual address fc6006f
pgd = (ptrval)
[fc6006f] *pgd=80000040207003, *pmd=00000000
Internal error: Oops: a06 [#1] SMP ARM
...
PC is at cpu_ca15_set_pte_ext+x24/x30
LR is at __set_fixmap+xe4/x118
...
Translation: the kernel tried to access a memory region that wasn’t valid, all because its bookkeeping had gone haywire.
The problematic logic allocating fixmap slots looked something like this (simplified for clarity)
#ifdef CONFIG_DEBUG_KMAP_LOCAL
#define KM_LOCAL_PER_CPU_FIXMAP_SLOTS (KM_LOCAL_FIXMAP_SLOTS * 2)
#else
#define KM_LOCAL_PER_CPU_FIXMAP_SLOTS KM_LOCAL_FIXMAP_SLOTS
#endif
#define TOTAL_FIXMAP_SLOTS (KM_LOCAL_PER_CPU_FIXMAP_SLOTS * CONFIG_NR_CPUS)
If CONFIG_NR_CPUS was 32 (or even higher), and debugging was enabled, this could easily outgrow the reserved fixmap space, leading to region overlap.
Limit CONFIG_NR_CPUS to 16 when CONFIG_DEBUG_KMAP_LOCAL is on for ARM.
2. Fix the sanity checks (BUILD_BUG_ON) so they correctly compare the start and end addresses of the fixmap region.
Here’s the essence of the fix (from the commit)
#if defined(CONFIG_DEBUG_KMAP_LOCAL) && CONFIG_NR_CPUS > 16
#error "Too many CPUs with DEBUG_KMAP_LOCAL enabled! Reduce NR_CPUS to 16 or below."
#endif
And the improved build check ensures the fixmap won’t spill over
BUILD_BUG_ON(FIXMAP_START -
KM_LOCAL_PER_CPU_FIXMAP_SLOTS * CONFIG_NR_CPUS * PAGE_SIZE
< FIXMAP_END);
Impact
- Denial of Service: Any ARM system built with CONFIG_DEBUG_KMAP_LOCAL=y and NR_CPUS set above 16 could hang or crash during boot, especially on EFI-based systems.
- Privilege Escalation: Not directly exploitable by ordinary users. The risk mainly concerns system stability, not direct code execution.
Exploitation Example
It’s _not_ the kind of bug that a normal user could exploit with a shell script. But a malicious or misinformed admin (or automated system) who set these kernel config flags could make a device unbootable, causing potential disruption, denial-of-service, or even bricking of headless embedded devices.
Official Patch and Commit:
linux-arm-kernel: ARM: 9063/1: mm: reduce maximum number of CPUs if DEBUG_KMAP_LOCAL is enabled
CVE Record:
Linux Kernel Documentation:
Linux Kernel - Fixmap Mechanism
Summary Table
| Condition | Vulnerable? |
|---------------------------------------|:-----------:|
| ARM CPU, DEBUG_KMAP_LOCAL off | No |
| ARM CPU, DEBUG_KMAP_LOCAL on, NR_CPUS > 16 | Yes |
| Non-ARM arch, or NR_CPUS ≤ 16 | No |
Test any custom configurations on non-critical hardware.
Bottom line:
CVE-2021-46910 is a classic example of how debugging code can unexpectedly break real systems if you’re not careful with compile-time limits. If you’re building or deploying ARM Linux devices with custom kernels, _stay aware_, don’t over-allocate CPUs with debug flags on, and always watch the latest kernel patches!
*Exclusive longform explainer by [Assistant], April 2024. Please reference original kernel sources for further technical detail.*
Timeline
Published on: 02/27/2024 07:15:07 UTC
Last modified on: 04/17/2024 16:55:44 UTC