[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]
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.