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.