------------------------------------------------------------------------ ------------------------------------------------------------------------
Affected: OpenBSD -current prior to 2026-06-18 (fixed in -current) Vendor: OpenBSD Severity: Medium Reporter: Argus Systems Date: 2026-06-12 CVE: CVE-2026-56099
1. SUMMARY ==========
The mplsdoerror() function in sys/netmpls/mplsinput.c parses an incoming MPLS label stack into a fixed-size local array, struct shimhdr stack[MPLSINKERNELLOOPMAX] (16 entries). When the parse loop completes without encountering the Bottom-of-Stack (BoS) label, nstk reaches MPLSINKERNELLOOPMAX (16). Several subsequent code paths then compute a copy length of (nstk + 1) sizeof(shim) -- 17 entries -- and use it with icmpdoexthdr(), MPREPEND(), and mcopyback() against the 16-entry stack object. This reads one struct shimhdr (4 bytes) past the end of the array, and that data is reflected back to the sender inside the generated ICMP/MPLS error response.
2. AFFECTED VERSIONS ====================
The (nstk + 1) length computations against the 16-entry stack[] array were introduced with the ICMP/MPLS error path on 2010-09-13 (commit 201d6983add, "First shot at ICMP error handling inside an MPLS path. Currently only TTL exceeded errors for IPv4 are handled."). The parse loop was bounded by MPLSINKERNELLOOPMAX (16), but nothing rejected a stack that ran to completion without a BoS bit, so nstk could reach 16 and the subsequent (nstk + 1) reads accessed stack[16].
Affected: OpenBSD -current prior to 2026-06-18 (mplsinput.c pre v1.82).
3. DETAILS ==========
Vulnerable code (sys/netmpls/mplsinput.c, mplsdoerror):
struct shimhdr stack[MPLSINKERNELLOOPMAX]; / 16 entries / ... for (nstk = 0; nstk < MPLSINKERNELLOOPMAX; nstk++) { ... stack[nstk] = mtod(m, struct shimhdr ); madj(m, sizeof(shim)); if (MPLSBOSISSET(stack[nstk].shimlabel)) break; } / no guard: with no BoS bit set, nstk == 16 here /
shim = &stack[0]; ... case IPVERSION: ... if (icmpdoexthdr(m, ICMPEXTMPLS, 1, stack, (nstk + 1) sizeof(shim))) return (NULL); ...
MPLSINKERNELLOOPMAX is defined as 16 and sizeof(struct shimhdr) is 4. With nstk == 16, each of these copies 17 4 = 68 bytes from a 64-byte stack[] object, reading stack[16] -- one struct shimhdr (4 bytes) of adjacent kernel stack -- and including it in the response.
The same (nstk + 1) length is later used to prepend and mcopyback() the stack back onto the reflected packet:
MPREPEND(m, (nstk + 1) sizeof(shim), MNOWAIT); ... mcopyback(m, 0, (nstk + 1) sizeof(shim), stack, MNOWAIT);
so the leaked entry also travels on the wire as the 17th MPLS shim header of the returned frame.
4. REACHABILITY ===============
The path is reachable remotely via mplsinput() -> mplsdoerror() on systems that have MPLS enabled on an interface. The trigger is a crafted MPLS frame (EtherType 0x8847) carrying 16 labels with no BoS bit set and an outermost label TTL of 1, so the TTL-exceeded error path is taken:
mplsinput (ttl <= 1) -> mplsdoerror(m, ICMPTIMXCEED, ICMPTIMXCEEDINTRANS, 0)
The inner payload must be IPv4 so the IPVERSION branch is reached.
5. IMPACT =========
Each crafted packet leaks 4 bytes of kernel stack memory adjacent to the stack[] array. The leak is carried in the ICMP/MPLS extension object of the error response reflected back to the sender, so an attacker can harvest the leaked bytes.
6. PROOF OF CONCEPT ===================
A Python/Scapy PoC sends a 16-label MPLS frame with no BoS bit set and an outermost label TTL of 1, then captures the reply. On a vulnerable kernel the reply carries 17 MPLS shim headers on the wire; the 17th (stack[16]) is the leaked kernel stack data.
PoC: https://pop.argus-systems.ai/attachments/poc-008-mpls-stack-leak.py
7. FIX ======
Fixed in -current by mvs on 2026-06-18. The fix adds a guard that drops a label stack which runs to completion without a BoS bit, so nstk can no longer reach MPLSINKERNELLOOPMAX:
if (nstk >= MPLSINKERNELLOOPMAX) { mfreem(m); return (NULL); }
Fix commit (mplsinput.c v1.82): https://github.com/openbsd/src/commit/6a23123ec05f1eb29cfcaae0f3a468b2e1983cfd
8. TIMELINE ===========
2026-06-12 Reported to security () openbsd org with PoC 2026-06-18 Fix committed to -current
9. CREDIT =========
Discovered and reported by Argus Systems (https://byteray.co.uk/).
10. REFERENCES ==============
Advisory: https://pop.argus-systems.ai/advisory/adv-040.html
Proof of concept: https://pop.argus-systems.ai/attachments/poc-008-mpls-stack-leak.py
Fix commit: https://github.com/openbsd/src/commit/6a23123ec05f1eb29cfcaae0f3a468b2e1983cfd