Re: [PATCH v3] libselinux: prevent ReDoS in file context regex matching
Petr Matyáš <[email protected]> Mon, 3 Aug 2026 17:05:45 +0200
| Newsgroups | org.kernel.vger.selinux |
|---|---|
| Message-ID | <CAJ9hfpWKAa=-woBfvHRM3CMY9KG=52xGPAp+ivKWaNhEAiEeFQ@mail.gmail.com> |
Hi, thanks for getting me the output, I really don't have access there. Looking into the fix in pcre2 (well Claude is), probably easier than reporting an issue there. Not sure if you/oss-fuzz are picking up pcre2 from a package (RHEL uses 10.44 which is 2 years old) or building from source so it might be failing for a while, unless you want me to add an ignorelist entry to oss-fuzz, which would then need to be removed after the fix is available to oss-fuzz tested system. Assuming I can create a fix and not just end up reporting an issue after al= l. Best regards Petr Matyas On Mon, 3 Aug 2026 at 14:43, Stephen Smalley <[email protected]> wrote: > > On Thu, Jul 30, 2026 at 1:42=E2=80=AFPM Stephen Smalley > <[email protected]> wrote: > > > > On Thu, Jul 30, 2026 at 1:23=E2=80=AFPM Stephen Smalley > > <[email protected]> wrote: > > > > > > On Thu, Jul 30, 2026 at 10:05=E2=80=AFAM Petr Matyas <p.matyas13@gmai= l.com> wrote: > > > > > > > > The PCRE2 interpreter is susceptible to catastrophic backtracking o= n > > > > crafted file context patterns or lookup keys. Since libselinux neve= r > > > > called pcre2_jit_compile(3), the JIT was available but unused, leav= ing > > > > the interpreter exposed on all platforms. > > > > > > > > Fix with two complementary mitigations: > > > > > > > > 1. Call pcre2_jit_compile() after every pattern compilation (includ= ing > > > > mmap deserialization) for both complete and partial matching mod= es. > > > > pcre2_match() uses the JIT automatically when available, which a= voids > > > > the interpreter's susceptibility to catastrophic backtracking. > > > > Note: pcre2_jit_match() is deliberately NOT used as it is known = to > > > > produce incorrect results on some platforms (e.g. aarch64). > > > > > > > > 2. Create a shared pcre2_match_context with a backtrack step limit = of > > > > 10 000 000 and pass it to pcre2_match(). PCRE2_ERROR_MATCHLIMIT = is > > > > treated as no-match to keep the lookup safe rather than failing > > > > noisily. This bounds worst-case matching time to milliseconds fo= r > > > > any pattern/subject combination, regardless of JIT availability. > > > > > > > > The vulnerability was reproduced and confirmed fixed on: > > > > - RHIVOS 2.0 / aarch64 > > > > - RHEL 10.3 / x86_64 > > > > - Fedora 44 / x86_64 > > > > > > > > Reproducer (149 bytes, base64): > > > > AwovMi0GLy8GCygKeAYGKwYLKAoKCgAAAPpXKgsoCi8vBgs8PG5vbmU+PgovMi0GLy8= GCygKeAYG > > > > KwYLKAoKCiNXLy8wCwsoCgojV1dXClcqCygKLy8vV1cueHhXV1d4eHgveCsGCygKCgo= nVy9XV3cy > > > > eHgveAcGJAsoCi8tLwYvMi0GLy8GCzw8bm9uZT4+3q2+7y8=3D > > > > > > > > Signed-off-by: Petr Matyas <[email protected]> > > > > > > Acked-by: Stephen Smalley <[email protected]> > > > > Merged. > > This commit produces a new oss-fuzz issue report, > https://issues.oss-fuzz.com/issues/541525193 > but quoting the stack trace below since you may lack access to the report= : > > Uninitialized bytes in MemcmpInterceptorCommon at offset 0 inside > [0x70b00000008c, 8) > =3D=3D248=3D=3DWARNING: MemorySanitizer: use-of-uninitialized-value > #0 0x56474054202c in ___interceptor_memcmp > /src/llvm-project/compiler-rt/lib/sanitizer_common/sanitizer_common_inter= ceptors.inc:880:10 > #1 0x793bcb92efb3 in libpcre2-8.so.0 > #2 0x793bcb94ecf1 in libpcre2-8.so.0 > #3 0x793bcb952edc in pcre2_jit_compile_8 > #4 0x5647405c1509 in regex_prepare_data selinux/libselinux/src/regex.c:10= 9:8 > #5 0x5647405b4828 in compile_regex selinux/libselinux/src/label_file.h:47= 6:7 > #6 0x5647405a04b5 in insert_spec selinux/libselinux/src/label_file.h:657:= 8 > #7 0x5647405a04b5 in process_line selinux/libselinux/src/label_file.h:898= :9 > #8 0x5647405a04b5 in process_text_file selinux/libselinux/src/label_file.= c:226:8 > #9 0x56474059baa1 in LLVMFuzzerTestOneInput > selinux/libselinux/fuzz/selabel_file_text-fuzzer.c:171:7 > #10 0x56474048522d in fuzzer::Fuzzer::ExecuteCallback(unsigned char > const*, unsigned long) > /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:619:13 > #11 0x56474046ffa2 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, > unsigned long) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:= 329:6 > #12 0x564740475e70 in fuzzer::FuzzerDriver(int*, char***, int > (*)(unsigned char const*, unsigned long)) > /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:865:9 > #13 0x5647404a19a2 in main > /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerMain.cpp:20:10 > #14 0x793bcb580082 in __libc_start_main > /build/glibc-B3wQXB/glibc-2.31/csu/libc-start.c:308:16 > #15 0x56474046908d in _start > Uninitialized value was created by a heap allocation > #0 0x56474053d7a2 in __interceptor_malloc > /src/llvm-project/compiler-rt/lib/msan/msan_interceptors.cpp:1047:3 > #1 0x793bcb915f61 in pcre2_compile_8 > SUMMARY: MemorySanitizer: use-of-uninitialized-value > (/mnt/scratch0/clusterfuzz/bot/builds/clusterfuzz-builds_selinux_f4e9262b= 6c04d654efaf1cf4240d4d2ed1e93020/revisions/selabel_file_text-fuzzer+0x12c02= c) > Exiting > > When I asked an AI about possible causes, it said the following: > ---<snip>--- > The most likely cause is simply that this patch is the first time > libselinux has ever called `pcre2_jit_compile()` =E2=80=94 the commit mes= sage > says as much ("the JIT was available but unused"). Before this patch, > `regex_match()` always ran through PCRE2's plain interpreter. After > it, `pcre2_match()` automatically switches to the JIT-compiled matcher > whenever compilation succeeded, which is a completely different code > path with its own memory-access patterns. > > PCRE2's JIT is known to read some stack/register slots that sanitizers > flag as uninitialized, even though the values are never actually used > to affect output =E2=80=94 this is a known, discussed behavior on the PCR= E2 > mailing list, where a user reproduced "Conditional jump or move > depends on uninitialised value(s)" originating from a stack > allocation, specifically when pcre2_jit_compile() was used, and > removing the JIT compile call made the warning disappear > [Oss-fuzz](https://issues.oss-fuzz.com/issues/474186379) . A PCRE2 > maintainer's response in that thread characterized it as intentional > optimization behavior in the JIT rather than a genuine bug. Under > libFuzzer+MSan (which oss-fuzz uses), this shows up as > "use-of-uninitialized-value" because MSan is stricter about > propagating "poisoned" bytes than Valgrind is. > > So the fuzzer isn't necessarily finding a new bug in libselinux's > logic =E2=80=94 it's newly exercising PCRE2's JIT matcher (previously dor= mant > in this codebase) on crafted/fuzzed file-context patterns, and the JIT > engine's normal register/stack handling trips MSan. This is consistent > with the patch note that `pcre2_jit_match()` was avoided due to known > correctness issues on some platforms =E2=80=94 the JIT engine here has a = track > record of subtle divergence from the interpreter. > > Practical next steps if you're triaging this upstream: > - Check whether the crash reproduces with `pcre2_jit_compile()` > removed (isolate JIT vs. non-JIT). > - Check whether it reproduces under regular ASan/UBSan (a true bug) > vs. only MSan (more likely a sanitizer-visibility artifact of JIT > codegen). > - If MSan-only, this is likely worth reporting upstream to PCRE2 > rather than something fixable in libselinux, since libselinux only > added the `pcre2_jit_compile()` call and doesn't control the generated > machine code. > ---snip>--- > > Not sure anything needs to be done here but noting it in case anyone > else has further insights.