Re: [PATCH] libselinux: fix SEGV in JIT matching of recursive patterns in partial mode

Stephen Smalley <[email protected]>
Newsgroups org.kernel.vger.selinux
Message-ID <CAEjxPJ4yW94x_DLSe=zdmfKB95aagFmydTKsBnWGfjc6efr4hA@mail.gmail.com>
On Thu, Aug 13, 2026 at 3:35 AM Petr Matyas <[email protected]> wrote:
>
> The PCRE2 JIT compiler has a bug in its PCRE2_JIT_PARTIAL_SOFT code
> path: patterns containing recursive subroutine calls (the (?1...)
> syntax) cause the generated native code to dereference a null pointer
> at match time. pcre2_jit_compile() returns 0 (success) and the
> compiled JIT size is non-zero, so the bug is undetectable at compile
> time and only manifests when pcre2_match() executes the JIT code.
>
> The root cause is an interaction between OP_RECURSE backtracking and
> the hit_start tracking mechanism that the partial-soft JIT uses to
> remember the earliest position at which a partial match was seen.
> No fix is currently available in PCRE2.
>
> Commit 92af7f1b61db ("libselinux: prevent ReDoS in file context regex
> matching") introduced pcre2_jit_compile() calls with both
> PCRE2_JIT_COMPLETE and PCRE2_JIT_PARTIAL_SOFT. ClusterFuzz
> subsequently reported a SEGV in selabel_file_compiled-fuzzer via a
> crafted compiled file_contexts input that triggers the bug through
> the lazy text-compilation fallback path (version/arch mismatch causes
> the pre-compiled PCRE2 data to be skipped, so the embedded regex
> string is compiled on demand via regex_prepare_data()).
>
> Fix by dropping PCRE2_JIT_PARTIAL_SOFT. Per the PCRE2 documentation,
> when partial-soft JIT code has not been compiled, pcre2_match()
> automatically falls back to the interpreter for partial matches.
> Partial matches remain protected against ReDoS by the 10 000 000-step
> backtrack limit that was introduced in the same commit. Full matches
> continue to use PCRE2_JIT_COMPLETE and are unaffected.
>
> Reported-by: ClusterFuzz (testcase 5660382656790528)
> Fixes: 92af7f1b61db ("libselinux: prevent ReDoS in file context regex matching")
> Signed-off-by: Petr Matyas <[email protected]>

Thank you! This also appears to fix another new oss-fuzz report
(selinux:selabel_file_text-fuzzer: Heap-buffer-overflow in _pcre2_script_run_8)
that just showed up (reproducer attached and output below).

Acked-by: Stephen Smalley <[email protected]>

 ==250==ERROR: AddressSanitizer: heap-buffer-overflow on address
0x7bf4d87e0032 at pc 0x591ed94e7dd5 bp 0x7ffd3265e3f0 sp
0x7ffd3265e3e8
READ of size 1 at 0x7bf4d87e0032 thread T0
    #0 0x591ed94e7dd4 in _pcre2_script_run_8
/tmp/pcre2-src/src/pcre2_script_run.c:332:3
    #1 0x591ed9437b56 in do_script_run
/tmp/pcre2-src/src/pcre2_jit_compile.c:9599:7
    #2 0x7fd4d942cc0c  (<unknown module>)
0x7bf4d87e0032 is located 0 bytes after 2-byte region
[0x7bf4d87e0030,0x7bf4d87e0032)
allocated by thread T0 here:
    #0 0x591ed90f70e4 in __interceptor_malloc
/src/llvm-project/compiler-rt/lib/asan/asan_malloc_linux.cpp:67:3
    #1 0x591ed913b958 in LLVMFuzzerTestOneInput
selinux/libselinux/fuzz/selabel_file_text-fuzzer.c:137:8
    #2 0x591ed913b5a9 in ExecuteFilesOnyByOne
/src/aflplusplus/utils/aflpp_driver/aflpp_driver.c:267:7
SUMMARY: AddressSanitizer: heap-buffer-overflow
(/mnt/scratch0/clusterfuzz/bot/builds/clusterfuzz-builds-afl_selinux_32ddf78d9f476617df7a8f4cf22b4d9340aef874/revisions/selabel_file_text-fuzzer+0x57fdd4)
Shadow bytes around the buggy address:
  0x7bf4d87dfd80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x7bf4d87dfe00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x7bf4d87dfe80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x7bf4d87dff00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x7bf4d87dff80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x7bf4d87e0000: fa fa 00 05 fa fa[02]fa fa fa 00 04 fa fa 02 fa
  0x7bf4d87e0080: fa fa fd fd fa fa 00 fa fa fa fa fa fa fa fa fa
  0x7bf4d87e0100: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x7bf4d87e0180: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x7bf4d87e0200: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x7bf4d87e0280: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==250==ABORTING


> ---
>  libselinux/src/regex.c | 15 +++++++--------
>  1 file changed, 7 insertions(+), 8 deletions(-)
>
> diff --git a/libselinux/src/regex.c b/libselinux/src/regex.c
> index 3f7df4c7..017647b4 100644
> --- a/libselinux/src/regex.c
> +++ b/libselinux/src/regex.c
> @@ -103,11 +103,12 @@ int regex_prepare_data(struct regex_data **regex, char const *pattern_string,
>                 goto err;
>         }
>
> -       /* JIT-compile for complete and partial matching. pcre2_match() uses the
> -        * JIT automatically when available, avoiding the interpreter's
> -        * susceptibility to catastrophic backtracking. Failures are non-fatal. */
> -       (void)pcre2_jit_compile((*regex)->regex,
> -                               PCRE2_JIT_COMPLETE | PCRE2_JIT_PARTIAL_SOFT);
> +       /* JIT-compile for complete matching only. pcre2_match() uses the JIT
> +        * automatically when available, avoiding the interpreter's
> +        * susceptibility to catastrophic backtracking. Partial matches fall
> +        * back to the interpreter, protected by the match limit. Failures
> +        * are non-fatal. */
> +       (void)pcre2_jit_compile((*regex)->regex, PCRE2_JIT_COMPLETE);
>
>         return 0;
>
> @@ -163,9 +164,7 @@ int regex_load_mmap(struct mmap_area *mmap_area, struct regex_data **regex,
>                 if (rc != 1)
>                         goto err;
>
> -               (void)pcre2_jit_compile((*regex)->regex,
> -                                       PCRE2_JIT_COMPLETE |
> -                                               PCRE2_JIT_PARTIAL_SOFT);
> +               (void)pcre2_jit_compile((*regex)->regex, PCRE2_JIT_COMPLETE);
>
>                 *regex_compiled = true;
>         }
> --
> 2.55.0
>
clusterfuzz-testcase-minimized-selabel_file_text-fuzzer-4919041035403264 (application/octet-stream, 19 B) - not displayed
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.