OpenBSD Root Exploit Chains 37-Year-Old Shared-Memory Bug
Calif chained a 37-year-old SysV integer overflow with an OOB write to get root on OpenBSD 7.9 past KARL, SMAP and SMEP — and the PoC is already public.

Root on OpenBSD 7.9, with KARL relinking, SMAP and SMEP all switched on, and no information-leak bug to help. That's what security firm Calif demonstrated this week, according to its published research — and the entry point is an integer overflow that has been sitting in BSD shared-memory code for roughly 37 years.
Before the OpenBSD jokes write themselves, let's be precise about who pays here. This is a local privilege escalation, not a remote hole: an attacker needs code execution as an unprivileged user first. So the bill lands on anyone running multi-user OpenBSD systems, shared build hosts, jailed services or any environment where a hostile local account is the working assumption rather than the edge case. If that's you, the era of treating local priv-esc patches as second-tier maintenance is over.
A 16-bit counter from the Reagan years
The bug is almost insulting in its simplicity. In the original BSD sources, the shared-memory attachment counter — shm_nattch — was defined as a signed 16-bit short. Its maximum is 32,767. Calif found that repeatedly forking a process with shared-memory attachment records could push the counter past that ceiling, wrapping it to a negative value. That integer overflow, combined with a race condition, produces a use-after-free.
OpenBSD — a project whose entire brand is proactive auditing — carried this arithmetic fossil for decades. One commenter on the Hacker News thread noted the equivalent NetBSD code was accidentally fixed during a rewrite back in August 1999. Accidentally. Nobody found it on purpose; it just got swept up in a refactor. That detail matters more than any exploit primitive in the writeup, because it tells you the limits of code review as a control. Audit all you like: a signed short that nobody had a reason to touch since the 1980s doesn't look like an attack surface until someone asks the rude question.
OpenBSD addressed the overflow and the second flaw — a separate out-of-bounds write in the kernel — in errata 026 and 018 respectively, and Calif published full proof-of-concept code after the fixes shipped, The Hacker News reported. So the patch exists before the public exploit. That's the supply chain working as designed. Now for the part that should make your patch queue nervous.
The chain is the product, not the bugs
Individually, these were two local bugs of the sort that get filed, fixed and forgotten. Chained, they walk through three of OpenBSD's flagship mitigations without a single infoleak. The sequence, per Calif's writeup, is a small masterclass in heap economics:
- Map 127 SysV shared-memory segments, unmap them with
munmap(), then fork 259 processes to wrap the attach count to -32,643. - Groom the heap using allocator metrics from
sysctl(KERN_MALLOCSTATS)to track when new pages appear. - Win a race between
IPC_RMIDin one process and a size-confused attach in a helper process. - Use an IP socket with
IP_MULTICAST_TTLset to a non-default value to place a persistent 16-byteip_moptionsstruct next to the target. - Fire the conditional -1 out-of-bounds write to corrupt the membership counters, then
close()the socket so the cleanup path decrements attacker-chosen memory twice — flipping the PTE self-referential mapping from user to supervisor, defeating SMAP/SMEP, and overwriting a predictable kernel pointer for root.
None of those steps requires defeating randomisation by brute force, and that's the uncomfortable bit. KARL relinks the kernel binary to randomise function and data addresses — but, as Calif points out, the kernel's memory allocators remain deterministic. Early boot allocations land in the same places on the same hardware, every boot. The exploit simply aims at the predictable structures that contain function pointers — the lo0 and enc0 ioctl handlers, in this case — overwriting one for control flow and using the other to restore the pointer afterwards.

What you're actually buying with "secure by default"
Here's the question your board should be asking, and it isn't whether OpenBSD is insecure now. It's this: what did the mitigations actually purchase? KARL, SMAP and SMEP raised the cost of exploitation from a single bug to two bugs plus weeks of specialist heap grooming. That is real value. It is also finite value, because exploit chaining arithmetic is brutal: mitigation strength multiplies attacker effort, but bug count divides it. Two mediocre local bugs in one codebase equals one root shell.
We've seen the same maths on the offensive side recently: when Xbow spent $40K on AI compute to build a Chrome exploit chain, the lesson wasn't that Chrome is broken — it's that chaining is a budget line, and budgets keep falling. A human researcher chaining a 37-year-old overflow today is a workflow an automated tool will attempt on your whole estate tomorrow. The time-to-chain is the metric to watch, not the CVE count.
To its credit, Calif didn't stop at the trophy. Its writeup proposes concrete hardening: safe-unlinking checks on doubly linked lists (which FreeBSD and NetBSD already implement in sys/queue.h), randomising the self-referential PTE map address at boot using a random byte to select a PML4 slot, and making the kernel allocator non-deterministic. Whether OpenBSD adopts them is a resourcing question, not a technical one — the implementations were sketched out in the same document.
So what changes for defenders this week
First, apply errata 026 and 018. Obvious, but say it anyway: the window between public PoC and your patch run is the only period that matters, and that window is now open.
Second, re-rank local priv-esc in your patch policy. Most organisations triage by remote-exploitability and let local kernel bugs ride until the next maintenance window. Chaining inverts that logic on any host where untrusted code runs — CI runners, shared shells, container hosts, anything with local users you don't fully trust. If you can't say with confidence exactly which accounts can execute code on a given box, your real exposure is the chaining scenario, not the lone CVE.
Third, spend a moment on the cheaper control. SysV shared memory is a decades-old IPC mechanism; on many modern systems nothing legitimate uses it. Restricting or auditing its use, and watching for pathological fork() rates from single processes, costs a fraction of an incident. The expensive control — mitigations that force attackers to chain — already did its job by making this hard. The cheap one is making sure nobody gets to try.
My verdict: this matters, but not because OpenBSD's halo slipped. It matters because it's a clean demonstration that audit reputation and mitigation layers are depreciation schedules, not guarantees. Bugs older than some of your engineers are still load-bearing in your kernel. Patch this cycle, reprioritise local priv-esc, and stop paying for "audited" as if it were a finished state rather than a recurring subscription.
Sources
This article draws on reporting and advisories from:


