[PATCH] libselinux: fix SEGV in JIT matching of recursive patterns in partial mode
Petr Matyas <[email protected]>
| Newsgroups | org.kernel.vger.selinux |
|---|---|
| Message-ID | <[email protected]> |
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]>
---
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