Re: [PATCH v3] libselinux: prevent ReDoS in file context regex matching
Stephen Smalley <[email protected]> Mon, 3 Aug 2026 08:43:36 -0400
| Newsgroups | org.kernel.vger.selinux |
|---|---|
| Message-ID | <CAEjxPJ7q_G5VBFcY1+-1YMM18cvXtFyP-Oj97BLtc3SgJKWNWg@mail.gmail.com> |
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@gmail.= com> wrote: > > > > > > The PCRE2 interpreter is susceptible to catastrophic backtracking on > > > crafted file context patterns or lookup keys. Since libselinux never > > > called pcre2_jit_compile(3), the JIT was available but unused, leavin= g > > > the interpreter exposed on all platforms. > > > > > > Fix with two complementary mitigations: > > > > > > 1. Call pcre2_jit_compile() after every pattern compilation (includin= g > > > mmap deserialization) for both complete and partial matching modes= . > > > pcre2_match() uses the JIT automatically when available, which avo= ids > > > 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 for > > > 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+PgovMi0GLy8GC= ygKeAYG > > > KwYLKAoKCiNXLy8wCwsoCgojV1dXClcqCygKLy8vV1cueHhXV1d4eHgveCsGCygKCgonV= y9XV3cy > > > 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_interce= ptors.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:109:= 8 #5 0x5647405b4828 in compile_regex selinux/libselinux/src/label_file.h:476:= 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:32= 9: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_f4e9262b6c= 04d654efaf1cf4240d4d2ed1e93020/revisions/selabel_file_text-fuzzer+0x12c02c) 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 messa= ge 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 PCRE2 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 dorma= nt 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 tr= ack 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.