found a remotely triggerable out-of-bounds read in the Linux kernel's H.323 connection tracking parser (CVE-2026-23456, CVSS 8.2). no authentication. no privileges. no user interaction. send a malformed packet to port 1720 on any Linux firewall or NAT gateway running nf_conntrack_h323 and you're reading kernel slab memory.
some context on why this matters more than a typical kernel OOB. H.323 is a VoIP signaling protocol from the 1990s. it is everywhere you don't think to look. telecom carriers, enterprise PBXs, session border controllers, hospital phone systems, building intercoms, elevator emergency phones, legacy videoconferencing. every Linux-based firewall or NAT device that needs to track H.323 connections for dynamic port allocation loads nf_conntrack_h323, which contains a full ASN.1 PER decoder running inline in the kernel, parsing untrusted data from the network, at wire speed, with direct access to kernel memory. this module auto-loads when H.323 traffic hits a conntrack rule. on many distributions it's loaded by default. the attack surface is: send a packet from the internet to a machine that might be doing NAT for a phone system somewhere behind it.
the bug. in decode_int(), the CONS case:
nf_h323_error_boundary(bs, 0, 2) len = get_bits(bs, 2) + 1 BYTE_ALIGN(bs) v = get_uint(bs, len)
the boundary check validates 2 bits for get_bits(). it does not validate len bytes for get_uint(). the length field is bounds-checked. the data described by the length field is not. craft a H.323/RAS packet where the bitstream is truncated after the length field. get_uint() walks 1β4 bytes off the end of a slab allocation. the attacker controls which allocation this is and can potentially influence what's adjacent in the slab cache. 1β4 bytes doesn't sound like much until you remember that kernel pointers, ASLR secrets, and crypto material all live in slab memory and a single leaked pointer can defeat KASLR.
now the interesting part. after the patch landed, Jakub Kicinski's AI code reviewer flagged five other locations in the same file as having the same bug: UNCO in decode_int, SEMI in decode_bitstr, SEMI and default in decode_octstr, BYTE in decode_bmpstr. all five advance bs->cur without checking that enough bytes remain. Florian went through each one and found a post-advance boundary check after every single one. "this LLM response is bunk."
he was right. but the reason he was right is the reason the CONS case is a real bug and the other five are not, and I think this is where current AI code review genuinely cannot tell the difference.
the other cases do this: advance bs->cur past the data without dereferencing, then check nf_h323_error_boundary(bs, 0, 0) after the switch block. the pointer overshoots. nothing reads through it. the boundary check fires. the function returns an error. pointer arithmetic past the end of a buffer is not a memory safety violation, only pointer dereference is. the pointer moved into illegal territory but nobody looked through the window.
the CONS case is different. get_uint(bs, len) dereferences *bs->cur++ inline. it reads 1β4 bytes from memory as part of advancing. the dereference and the advance are the same operation. there is no "temporary overshoot" because the bytes are physically read from memory during the overshoot. a post-advance boundary check cannot un-read memory. the AI saw "pointer advances without pre-check" six times and pattern-matched all of them as the same bug. five of them advance a pointer. one of them reads through a pointer while advancing. pointer arithmetic vs pointer dereference is the entire vulnerability, and current AI review can't see the difference because it's matching on control flow shape, not on what the CPU actually does when the instruction executes.
the fix is two lines. one call to nf_h323_error_boundary(bs, len, 0) between get_bits() and get_uint(). the original commit is 5e35941d9901, "[NETFILTER]: Add H.323 conntrack/NAT helper", from 2007. twenty years of a full ASN.1 decoder running in kernel space, parsing untrusted packets from the network, with a missing bounds check on a length-prefixed read. loaded by default on most distributions. reachable without authentication. the fix is two lines.
reported by Klaudia Kloc and Dawid MoczadΕo from @VidocSecurity. I verified the bug, wrote the PoC, and submitted the patch. patched in stable 5.10β6.19.
We had to let our intern go one week into his internship.
He was extremely incompetent and highly unethical.
Warning the Street so you donβt make the same mistake we did.