|
|
Message-ID: <20261027010332.GM3542221@port70.net> Date: Tue, 27 Oct 2026 02:03:32 +0100 From: Szabolcs Nagy <nsz@...t70.net> To: Thai Duong <thai@...if.io> Cc: musl@...ts.openwall.com Subject: Re: [SECURITY] musl: out-of-bounds read in regexec() backtracking (src/regex/regexec.c, 1.2.6) * Thai Duong <thai@...if.io> [2026-10-26 05:35:06 -0700]: > Calif.io, working with Anthropic, is reporting a memory-safety issue > in musl's regex matcher. In tre_tnfa_run_backtrack() > (src/regex/regexec.c), the backtracking retry path rolls back the > match state but not pos_add_next, so after a retry GET_NEXT_WCHAR > advances the byte position by a stale length. On a subject that mixes > single-byte and multibyte characters the recorded byte offsets desync > and the final strncmp (regexec.c:785) reads past the end of the > subject buffer. It is reachable through the exported regexec() when > the application uses a backreference pattern and passes an > attacker-controlled subject string, which regexec is documented to > accept. It is present at v1.2.6 and master. > > The full write-up and a source-level reproduction are in the attached > report, and the proof of concept is attached as ANT-2026-HMXB71C3.zip > and also mirrored at > https://drive.google.com/file/d/1AUXONoLV-U5XDuOvLk9IbjEEiRR8cI1p/view. where is the 'source-level reproduction' ? it should be a regcomp and a regexec call (2 lines of c code) but i see gibberish scripts that reference nonexistig things. such reports should clearly state which part was human verified and which bit is machine generated text. the pos handling is wrong so there is a bug, but it's not clear how to trigger oob read (i don't see the path that leads to oob string+so) > > This issue was found by Claude and verified by Calif.io engineers. Our > reference: ANT-2026-HMXB71C3. > > Thanks, > Calif.io > # musl libc: a crafted multibyte subject triggers an out-of-bounds read in regexec()'s backreference matcher > > **ANT-ID:** ANT-2026-HMXB71C3 > > > Reported by Calif.io, in collaboration with Anthropic. Our reference: ANT-2026-HMXB71C3. > > ## Summary > > Matching a backreference pattern against untrusted multibyte text with `regexec(3)` reads past the end of the subject buffer. In `src/regex/regexec.c`, `tre_tnfa_run_backtrack()` validates a backreference with `strncmp((const char*)string + so, str_byte - 1, (size_t)bt_len)` at `regexec.c:785`, where `so` and `bt_len` are byte offsets derived from the recorded submatch, and there is no check that `so + bt_len` stays within the subject length. Those offsets go out of range because the backtracker's restart path forgets one piece of scanner state. When an attempt fails and the matcher jumps to the `retry:` label (`regexec.c:884-897`), it rolls back `next_c` and `str_byte` to the start of the attempt but does not roll back `pos_add_next`, the byte length of the last character decoded. The `GET_NEXT_WCHAR()` at `regexec.c:695` then advances `pos` by that stale length (`pos += pos_add_next`), so when the subject mixes single-byte and multibyte characters `pos` gains `stale_len - true_len` bytes on every restart. The error accumulates, the recorded `so`/`eo` byte offsets inherit it, and `(const char*)string + so` walks off the end of the subject. With a hard-coded backreference pattern such as `\([a-zA-Z]*\) \1` (the standard repeated-word idiom) and an attacker-supplied subject like `"aaaa <multibyte char>"`, the read runs past the buffer; longer subjects push the offset far enough to cross an unmapped page and crash the process. On musl the default locale resolves to `C.UTF-8`, so the multibyte precondition holds for any program that calls `setlocale()`, including in a bare container with no locale set. > > ## Reproduce > > The proof of concept is attached, and is also mirrored at https://drive.google.com/file/d/1AUXONoLV-U5XDuOvLk9IbjEEiRR8cI1p/view. > > ## Recommendations > > At the `retry:` restart, save and restore `pos_add_next` alongside `next_c` and `str_byte`, so `pos` is re-advanced by the byte length of the character actually at `pos_start` rather than a stale length. Independently, bound the backreference comparison at `regexec.c:785` so it never reads past the subject: check `so >= 0` and `so + bt_len <= len` before the `strncmp`, and treat an out-of-range offset as a non-match. The comparison source `str_byte - 1` is also only correct for single-byte characters; it should be `str_byte - pos_add_next` so a multibyte current character is compared from its first byte. > > ## Affected > > musl v1.2.6 (the newest release) and master HEAD; `tre_tnfa_run_backtrack()` in `src/regex/regexec.c` restarts without restoring `pos_add_next` and performs the backreference `strncmp` with no bound on the subject offset in both, and the file is byte-identical at the two refs. No CVE covers it. The same defect is present in upstream TRE (`laurikari/tre`), from which this code is derived.
Powered by blists - more mailing lists
Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.