Full Report
Null dereferences are commonly known as an unexploitable bug. Sure, it's a denial of service but not much else. Well, in the context of some situations, we can make it more. In Linux, when a memory corruption issue is found, the kernel will attempt to recover; this is called an oops. The oops recovery path will NOT clean up the existing code correctly. Most of the time, it simply leaves this alone and kills the entire task. In C, there the main way to track the amount of open references is by using a counter known as a reference counter (refcount). If a new reference is made, the counter is incremented. If it's removed, it's decremented. If the refcount hits 0, then the object is freed, since there are no references to it. The act of leaving the state alone sounds like it would be fine. But, in fact, it's very scary to trigger these handlers, since the code is never fully completed. In the case of a refcount, we could trigger an oops with open references that never get closed. Over enough time, a 32-bit uint could overflow. Eventually, when this is set to 0, the object will be freed, creating a use after free on the object. The author had written in bug report about a null dereference in the kernel. Simply put: when a task's VMA is not mapped at all, the mm_struct_mmap will be null. Trying to access this will lead to a null dereference in the kernel, simply by reading process mapping files. Once the kernel oops occurs, a few things are left in weird states: struct file, mm_users and task struct have a recount leak. Two locks will never be freed, resulting in a hang forever on certain operations. Because of the locks, only the refcount leak on mm_users has the potential for exploitation. Even with this though, it uses the overflow safe atomic_t type. With some Linux shenanigans that I don't fully understand, this doesn't matter though. After avoiding deadlocks and other mm_users specific problems, this is possible to overflow though. The author believes that on a server that print serial logs to console, this would take over 2 years to exploit. On a Kali Linux box with a GUI, this took 8 days to hit. To turn this into an actual exploit, the author uses this UAF to cause further havoc within the AIO subsystem. They took this to a double free crash but didn't exploit it any further. The solution to this new attack idea? Introduce an oops limit that will cause the kernel to panic after so many oops occur. However, I doubt this will be picked up for most OS's, since little bugs happen all the time. It would be a bummer if your server crashed because it had a oops once every month. This is a very clever exploit strategy that I hope to see more in the future about!
Analysis Summary
# Vulnerability: Linux Kernel Refcount Overflow via Null-Dereference Oops
## CVE Details
- **CVE ID**: CVE-2022-47929 (Related to the specific null-deref in `show_smaps_rollup`)
- **CVSS Score**: 7.0 (High) - *Estimated based on local exploitation potential*
- **CWE**: CWE-476 (NULL Pointer Dereference), CWE-667 (Improper Locking), CWE-190 (Integer Overflow)
## Affected Systems
- **Products**: Linux Kernel
- **Versions**: Found in versions leading up to 6.2; specifically fixed in 5.15.86, 6.0.12, 6.1.1.
- **Configurations**: Systems with `/proc/` filesystem enabled where unprivileged users can trigger specific `task_mmu` operations. Exploitation is significantly faster on systems that log to a GUI console (e.g., Kali Linux) versus serial consoles.
## Vulnerability Description
The vulnerability stems from how the Linux kernel handles "Oops" events (non-fatal kernel errors). When a null-pointer dereference occurs, the kernel attempts to recover by killing the current task. However, this "recovery" path bypasses standard cleanup code.
In the specific case of `show_smaps_rollup`, an attacker can trigger a null-dereference by reading the smaps of a process with no VMAs. This causes an Oops while the kernel still holds:
1. **Reference Counts**: `mm_users` and `task_struct` counters are incremented but never decremented.
2. **Locks**: The `mmap_read_lock` is taken but never released.
By repeatedly triggering this Oops, an attacker can leak references until a 32-bit `atomic_t` counter overflows. Once the counter wraps back to zero, the kernel believes there are no more users and frees the associated `mm_struct`, leading to a **Use-After-Free (UAF)** condition.
## Exploitation
- **Status**: PoC available (demonstrated to travel from UAF to a double-free crash).
- **Complexity**: High (requires bypassing deadlocks and managing long-duration execution).
- **Attack Vector**: Local (requires the ability to execute code and read `/proc` entries).
- **Timeframe**: ~8 days on a high-speed GUI-based system; ~2 years on a slow serial-logging server.
## Impact
- **Confidentiality**: High (UAF can lead to arbitrary memory read).
- **Integrity**: High (UAF and double-free enable memory corruption and privilege escalation).
- **Availability**: High (Causes kernel panic or permanent deadlocking of system tasks).
## Remediation
### Patches
- **Specific Bug Fix**: Updated `fs/proc/task_mmu.c` to prevent the null-dereference (Commit `33fc9e26b7cb39f0d4219c875a2451802249c225`).
- **Generic Mitigation**: The Linux kernel now includes an **Oops Limit** (Commit `48ea09cddae0b794cde2070f106ef676703dbcd3`). If the number of Oops events exceeds a set threshold (defaulting to 10,000), the kernel will trigger a panic to prevent refcount overflow attacks.
### Workarounds
- Set `kernel.panic_on_oops = 1` via sysctl to force a reboot on the first error, preventing the iterative accumulation of leaked references.
## Detection
- **Indicators of Compromise**: Rapidly filling kernel logs (`dmesg`) containing repeated "Oops: 0000 [#1]" or similar backtraces related to `show_smaps_rollup` or `task_mmu`.
- **Detection Methods**: Monitor for high rates of kernel Oops events or processes that remain in "D" (uninterruptible sleep) state due to leaked `mmap` locks.
## References
- **Project Zero Blog**: hXXps://projectzero[.]google[.]com/2023/01/exploiting-null-dereferences-in-linux[.]html
- **Kernel Fix**: hXXps://git[.]kernel[.]org/pub/scm/linux/kernel/git/stable/linux[.]git/commit/fs/proc/task_mmu[.]c?id=33fc9e26b7cb39f0d4219c875a2451802249c225
- **Oops Limit Implementation**: hXXps://git[.]kernel[.]org/pub/scm/linux/kernel/git/torvalds/linux[.]git/commit/?id=48ea09cddae0b794cde2070f106ef676703dbcd3