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.