[PATCH] netfilter: nf_conntrack_h323: fix OOB read in get_bitmap()
Joas Antonio <[email protected]>
| Newsgroups | gmane.comp.security.firewalls.netfilter.devel |
|---|---|
| Message-ID | <CAOCBhwFzYNuxGQtjRNaisQt_aTNgst_u+FX5PCjWT0mcGqbKDQ@mail.gmail.com> |
Summary: get_bitmap() in net/netfilter/nf_conntrack_h323_asn1.c performs
an out-of-bounds 1-byte read when decoding a PER bitmap whose total bit
position (bs->bit + b) is an exact multiple of 8 in the range 16..24.
The preceding boundary check, nf_h323_error_boundary(), underestimates
the required byte count by one in exactly this case, so the OOB read is
not caught. Reachable remotely through an H.323 RAS message (UDP/1719)
processed by the nf_conntrack_h323 netfilter helper, when that helper
is loaded for a connection. Confirmed with a reproducer and a proposed
fix, both tested.
Affected version: Linux mainline, commit 3d6d817622b0a9721e3cc404df
3469171582be13 (2026-08-12), i.e. current as of this report. Verified
present in that exact commit; no prior fix exists upstream.
File / function: net/netfilter/nf_conntrack_h323_asn1.c, get_bitmap()
(around line 211), called from decode_seq() (around line 587-591).
Impact: out-of-bounds read of 1 byte past the end of the packet buffer
(heap-buffer-overflow / global-buffer-overflow depending on allocator
placement of the skb/linear buffer). Confirmed by AddressSanitizer in a
userspace harness linking the unmodified kernel source file. The read
value is masked out of get_bitmap()'s return value in all cases (see
"Details" below), so there is no information disclosure through this
path; the impact is a potential crash (denial of service) if the byte
read falls on an unmapped page, plus the OOB access itself as a defect
regardless of whether it is currently mapped.
Conditions: requires the nf_conntrack_h323 helper to be loaded and
attached to a UDP/1719 (RAS) flow (not loaded by default on most
distributions, but is the documented attack surface for the related
CVE-2026-23455 / CVE-2026-23456 / CVE-2026-43233, all in the same
parser). No authentication or established call state is required; the
first RAS packet is decoded via DecodeRasMessage() before any session
is validated.
Reproducer: an 8-byte input triggers the bug via DecodeRasMessage()
directly. Tested against the exact kernel source file
(net/netfilter/nf_conntrack_h323_asn1.c) compiled unmodified into a
small userspace harness with -fsanitize=address,fuzzer; also confirmed
independently in a plain non-fuzzer harness. AddressSanitizer reports:
heap-buffer-overflow, READ of size 1
#0 get_bitmap nf_conntrack_h323_asn1.c:232
#1 decode_seq nf_conntrack_h323_asn1.c:594
#2 decode_choice nf_conntrack_h323_asn1.c:816
#3 decode_seq nf_conntrack_h323_asn1.c:576
#4 decode_seq nf_conntrack_h323_asn1.c:576
#5 decode_choice nf_conntrack_h323_asn1.c:816
#6 DecodeRasMessage nf_conntrack_h323_asn1.c:835
The reproducer bytes are not attached to this message; available on
request.
Details: nf_h323_error_boundary(bs, bytes, bits) computes required
bytes as ceil((bits + bs->bit) / 8). get_bitmap(bs, b), with
l = bs->bit + b, reads (l >> 3) bytes in a loop, then for l < 32
unconditionally reads one more byte via *bs->cur to fold in any
leftover bits. When l is an exact multiple of 8, there are no
leftover bits, nf_h323_error_boundary()'s ceil() computes exactly
l >> 3 bytes (no "+1" since the remainder is 0), but get_bitmap()
still performs the extra read. This mismatch is the root cause.
It is also provable that this extra byte never contributes to the
returned value in that case (shift = 24 - l after the loop is always
below the mask window 0xffffffff << (32 - b) kept at the end of
get_bitmap()), so removing it changes no observable behavior for any
valid input -- confirmed by running the proposed fix through 200000
libFuzzer iterations against DecodeRasMessage / DecodeQ931 /
DecodeMultimediaSystemControlMessage with zero new failures and
identical decode outcomes on the existing reproducer corpus.
Proposed fix (tested: reproducer no longer crashes under ASAN;
200000-iteration fuzz regression run clean): only perform the extra
byte read when l has leftover bits (l & 7 != 0):
--- a/net/netfilter/nf_conntrack_h323_asn1.c
+++ b/net/netfilter/nf_conntrack_h323_asn1.c
@@ -228,7 +228,8 @@ static unsigned int get_bitmap(struct bitstr *bs,
unsigned int b)
v |= (unsigned int)(*bs->cur++) << shift;
if (l < 32) {
- v |= (unsigned int)(*bs->cur) << shift;
+ if (l & 7)
+ v |= (unsigned int)(*bs->cur) << shift;
v <<= bs->bit;
} else if (l > 32) {
v <<= bs->bit;
Fixes: 5e35941d9901 ("[NETFILTER]: Add H.323 conntrack/NAT helper")
Signed-off-by: Joas Antonio dos Santos <[email protected]>
Related: this parser has had three CVEs in the same file in 2026
(CVE-2026-23455, CVE-2026-23456, CVE-2026-43233); this is an
independent, fourth defect in the same decoder, not yet fixed
upstream.
REPORT_h323_getbitmap_PLAINTEXT.txt
(text/plain, 6.3 KB)
===================================================================== TEMPORARY SECTION - REMOVE BEFORE SENDING (required by Documentation/process/security-bugs.rst for AI-assisted reports - sender must review and delete this section) Recipients (via scripts/get_maintainer.pl): To: Pablo Neira Ayuso <[email protected]> Florian Westphal <[email protected]> Cc: [email protected] [email protected] Do not send to [email protected]: this bug was found with AI assistance, and security-bugs.rst mandates treating it as public from the start ("if you resorted to AI assistance to identify a bug, you must treat it as public"). Do not include [email protected] on this first message. That list is for distro coordination AFTER maintainers accept a fix (Documentation/process/security-bugs.rst, section "Coordination with other groups"), not for the initial report. Before sending, check your mail client's settings (do not let it reformat as HTML or mangle line wrapping) - see Documentation/process/email-clients.rst. Fixes: tag confirmed against a full-history clone of torvalds/linux (git blame -L 211,246 -- net/netfilter/nf_conntrack_h323_asn1.c): the buggy get_bitmap() logic is unchanged since the file's original commit, 5e35941d990123f155b02d5663e51a24f816b6f3, "[NETFILTER]: Add H.323 conntrack/NAT helper" (Jing Min Zhao, 2006-03-20). Attached patch already filled in: Joas Antonio dos Santos <[email protected]>, 13 Aug 2026, Fixes: 5e35941d9901. ===================================================================== Subject: [PATCH] netfilter: nf_conntrack_h323: fix OOB read in get_bitmap() Summary: get_bitmap() in net/netfilter/nf_conntrack_h323_asn1.c performs an out-of-bounds 1-byte read when decoding a PER bitmap whose total bit position (bs->bit + b) is an exact multiple of 8 in the range 16..24. The preceding boundary check, nf_h323_error_boundary(), underestimates the required byte count by one in exactly this case, so the OOB read is not caught. Reachable remotely through an H.323 RAS message (UDP/1719) processed by the nf_conntrack_h323 netfilter helper, when that helper is loaded for a connection. Confirmed with a reproducer and a proposed fix, both tested. Affected version: Linux mainline, commit 3d6d817622b0a9721e3cc404df 3469171582be13 (2026-08-12), i.e. current as of this report. Verified present in that exact commit; no prior fix exists upstream. File / function: net/netfilter/nf_conntrack_h323_asn1.c, get_bitmap() (around line 211), called from decode_seq() (around line 587-591). Impact: out-of-bounds read of 1 byte past the end of the packet buffer (heap-buffer-overflow / global-buffer-overflow depending on allocator placement of the skb/linear buffer). Confirmed by AddressSanitizer in a userspace harness linking the unmodified kernel source file. The read value is masked out of get_bitmap()'s return value in all cases (see "Details" below), so there is no information disclosure through this path; the impact is a potential crash (denial of service) if the byte read falls on an unmapped page, plus the OOB access itself as a defect regardless of whether it is currently mapped. Conditions: requires the nf_conntrack_h323 helper to be loaded and attached to a UDP/1719 (RAS) flow (not loaded by default on most distributions, but is the documented attack surface for the related CVE-2026-23455 / CVE-2026-23456 / CVE-2026-43233, all in the same parser). No authentication or established call state is required; the first RAS packet is decoded via DecodeRasMessage() before any session is validated. Reproducer: an 8-byte input triggers the bug via DecodeRasMessage() directly (bytes: 5b 5b 05 05 39 00 10 04). Tested against the exact kernel source file (net/netfilter/nf_conntrack_h323_asn1.c) compiled unmodified into a small userspace harness with -fsanitize=address,fuzzer; also confirmed independently in a plain non-fuzzer harness. AddressSanitizer reports: heap-buffer-overflow, READ of size 1 #0 get_bitmap nf_conntrack_h323_asn1.c:232 #1 decode_seq nf_conntrack_h323_asn1.c:594 #2 decode_choice nf_conntrack_h323_asn1.c:816 #3 decode_seq nf_conntrack_h323_asn1.c:576 #4 decode_seq nf_conntrack_h323_asn1.c:576 #5 decode_choice nf_conntrack_h323_asn1.c:816 #6 DecodeRasMessage nf_conntrack_h323_asn1.c:835 The reproducer bytes are not attached to this message; available on request (kernel AI-assisted-report policy). Details: nf_h323_error_boundary(bs, bytes, bits) computes required bytes as ceil((bits + bs->bit) / 8). get_bitmap(bs, b), with l = bs->bit + b, reads (l >> 3) bytes in a loop, then for l < 32 unconditionally reads one more byte via *bs->cur to fold in any leftover bits. When l is an exact multiple of 8, there are no leftover bits, nf_h323_error_boundary()'s ceil() computes exactly l >> 3 bytes (no "+1" since the remainder is 0), but get_bitmap() still performs the extra read. This mismatch is the root cause. It is also provable that this extra byte never contributes to the returned value in that case (shift = 24 - l after the loop is always below the mask window 0xffffffff << (32 - b) kept at the end of get_bitmap()), so removing it changes no observable behavior for any valid input -- confirmed by running the proposed fix through 200000 libFuzzer iterations against DecodeRasMessage / DecodeQ931 / DecodeMultimediaSystemControlMessage with zero new failures and identical decode outcomes on the existing reproducer corpus. Proposed fix (tested: reproducer no longer crashes under ASAN; 200000-iteration fuzz regression run clean): only perform the extra byte read when l has leftover bits (l & 7 != 0): --- a/net/netfilter/nf_conntrack_h323_asn1.c +++ b/net/netfilter/nf_conntrack_h323_asn1.c @@ -228,7 +228,8 @@ static unsigned int get_bitmap(struct bitstr *bs, unsigned int b) v |= (unsigned int)(*bs->cur++) << shift; if (l < 32) { - v |= (unsigned int)(*bs->cur) << shift; + if (l & 7) + v |= (unsigned int)(*bs->cur) << shift; v <<= bs->bit; } else if (l > 32) { v <<= bs->bit; Full patch in git format-patch form attached separately (0001-netfilter-nf_conntrack_h323-fix-oob-read-in-get_bit.patch). Fixes: 5e35941d9901 ("[NETFILTER]: Add H.323 conntrack/NAT helper") Related: this parser has had three CVEs in the same file in 2026 (CVE-2026-23455, CVE-2026-23456, CVE-2026-43233); this is an independent, fourth defect in the same decoder, not yet fixed upstream.
0001-netfilter-nf_conntrack_h323-fix-oob-read-in-get_bit.patch
(application/octet-stream, 3.4 KB)
From: Joas Antonio dos Santos <[email protected]> Date: Thu, 13 Aug 2026 00:00:00 +0000 Subject: [PATCH] netfilter: nf_conntrack_h323: fix OOB read in get_bitmap() get_bitmap() decodes b <= 32 PER bits starting at bs->cur/bs->bit. Let l = bs->bit + b. For 8 < l < 32 it reads (l >> 3) full bytes in the loop, then unconditionally peeks one more byte: for (bytes = l >> 3, shift = 24, v = 0; bytes; bytes--, shift -= 8) v |= (unsigned int)(*bs->cur++) << shift; if (l < 32) { v |= (unsigned int)(*bs->cur) << shift; /* unconditional peek */ v <<= bs->bit; } When l is an exact multiple of 8 (l == 16 or l == 24), the loop already consumed every bit that was requested: there are no leftover bits in a next byte, and bs->bit is set to l & 0x7 == 0 right after. The peek at *bs->cur in that case reads one byte past what the bitstream actually contains for this field. That extra byte is also provably never observed by any caller: with l a multiple of 8, shift after the loop equals 24 - l, which is always <= 32 - b - 1 (since l >= b), i.e. strictly below the mask window kept by the final: v &= 0xffffffff << (32 - b); so the peeked byte's bits are always masked out of the returned value. The read is a pure out-of-bounds access with no effect on get_bitmap()'s result for any valid input; removing it changes no observable behavior. The bug is reachable from decode_seq() when decoding a SEQUENCE extension bitmap: if (nf_h323_error_boundary(bs, 0, 7)) return H323_ERROR_BOUND; bmp2_len = get_bits(bs, 7) + 1; /* attacker controlled, 1..128 */ if (nf_h323_error_boundary(bs, 0, bmp2_len)) return H323_ERROR_BOUND; if (bmp2_len > 32) return H323_ERROR_RANGE; bmp2 = get_bitmap(bs, bmp2_len); nf_h323_error_boundary() computes the required byte count as ceil((bits + bs->bit) / 8), i.e. exactly l >> 3 bytes when l is a multiple of 8 -- it does not know about get_bitmap()'s extra peek, so the boundary check underestimates by one byte and the OOB read is not caught. Reachable remotely via RasMessage (RAS/UDP 1719), decoded through DecodeRasMessage() -> decode_choice() -> decode_seq() (nested) -> decode_choice() -> decode_seq() -> get_bitmap(), whenever an extension bitmap length places l = bs->bit + bmp2_len at 16 or 24. Confirmed with a userspace harness linking this file unmodified (ASAN, 8-byte input, RAS message path); see report for details. Fix: only perform the peek when there are actually leftover bits in the next byte (l & 7 != 0). For l a multiple of 8, bs->cur already points at the correct next unread byte and bs->bit is correctly 0; no read of that byte is needed or safe to perform here. Fixes: 5e35941d9901 ("[NETFILTER]: Add H.323 conntrack/NAT helper") Signed-off-by: Joas Antonio dos Santos <[email protected]> --- net/netfilter/nf_conntrack_h323_asn1.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/netfilter/nf_conntrack_h323_asn1.c b/net/netfilter/nf_conntrack_h323_asn1.c index 3d6d817..0000000 100644 --- a/net/netfilter/nf_conntrack_h323_asn1.c +++ b/net/netfilter/nf_conntrack_h323_asn1.c @@ -228,7 +228,8 @@ static unsigned int get_bitmap(struct bitstr *bs, unsigned int b) v |= (unsigned int)(*bs->cur++) << shift; if (l < 32) { - v |= (unsigned int)(*bs->cur) << shift; + if (l & 7) + v |= (unsigned int)(*bs->cur) << shift; v <<= bs->bit; } else if (l > 32) { v <<= bs->bit; -- [VERSAO-GIT]