Skip to content

fix out-of-bounds read in FuzzySetMatch/FuzzyMatchParse octet scan#6206

Open
aizu-m wants to merge 1 commit into
cfengine:masterfrom
aizu-m:addr-lib-octet-scan-oob
Open

fix out-of-bounds read in FuzzySetMatch/FuzzyMatchParse octet scan#6206
aizu-m wants to merge 1 commit into
cfengine:masterfrom
aizu-m:addr-lib-octet-scan-oob

Conversation

@aizu-m

@aizu-m aizu-m commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

FuzzySetMatch splits an address into octets with sscanf("%63[^.]") in a fixed 4-iteration loop and does sp += strlen(octet) + 1 to step over the dot. The final octet has no dot, so the +1 steps over the NUL instead, and when the address has fewer octets than the pattern the pointer leaves the string.

  1. sp2 walks past the terminating NUL of s2, so the next sscanf reads off the end (ASan: heap-buffer-overflow READ in strlen/sscanf). buffer2 is never reset either, so a failed scan keeps the previous octet.
  2. s2 gets here from untrusted data via the isipinsubnet() and iprange() policy functions, whose address argument is an arbitrary string, e.g. isipinsubnet("1.2.3.4", "1").
  3. the IPv6 match loop and the IPv4 range loop in FuzzyMatchParse have the same walk-off.

Reset the scan buffer, stop with no match once the address runs out of octets, and only step over a separator that is actually present. Same change at all three sites. The regression test in addr_lib_test.c aborts under ASan on the current code and passes with the fix.

AddressSanitizer, matching a pattern against a shorter address:

    ERROR: AddressSanitizer: heap-buffer-overflow READ of size 1
        #0 strlen
        cfengine#1 sscanf
        cfengine#2 FuzzySetMatch addr_lib.c

Noticed this reading the IP range matcher. FuzzySetMatch() splits an
address into octets with a fixed 4-iteration loop:

    for (i = 0; i < 4; i++) {
        sscanf(sp1, "%63[^.]", buffer1);
        ...
        sscanf(sp2, "%63[^.]", buffer2);
        sp2 += strlen(buffer2) + 1;
    }

The +1 steps over the '.' separator. But on the last octet there is no
separator, so it steps over the terminating NUL instead. When s2 has
fewer octets than s1 (say pattern "1.2.3.4" against address "1"), sp2
walks past the end of the string and the next sscanf reads out of bounds.
buffer2 is not reset either, so a failed scan keeps stale content.

s2 reaches here from untrusted data through the isipinsubnet() and
iprange() policy functions, whose address argument is an arbitrary
string.

Only advance past a separator that is actually there, reset buffer2, and
stop with no match once the address runs out of octets. The IPv6 match
loop and the IPv4 range loop in FuzzyMatchParse() had the same walk-off,
fixed the same way. Added a regression test that trips ASan on the old
code.

Changelog: Title
Signed-off-by: Aizal Khan <aizumusheer2@gmail.com>

@larsewi larsewi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks 🚀

@larsewi

larsewi commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

@cf-bottom Jenkins please :)

@cf-bottom

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants