Re: [PATCH 1/2] libselinux: prevent ReDoS in file context regex matching

Stephen Smalley <[email protected]> Wed, 29 Jul 2026 11:51:10 -0400
Newsgroups org.kernel.vger.selinux
Message-ID <CAEjxPJ5Cd422TFMiXNow=m1LOwZL_gwpLXd9u=JgNYWbszwU=A@mail.gmail.com>
On Wed, Jul 29, 2026 at 9:40 AM Petr Matyas <[email protected]> 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) or pcre2_jit_match(3), the JIT was
> available but unused, leaving the interpreter exposed on all platforms.
>
> Fix with two complementary mitigations:
>
> 1. Call pcre2_jit_compile() after every pattern compilation (including
>    mmap deserialization) to engage JIT for both complete and partial
>    matching. regex_match() now calls pcre2_jit_match() and falls back to
>    pcre2_match() only on PCRE2_ERROR_JIT_BADOPTION (JIT unavailable or
>    not compiled for the requested mode).
>
> 2. Create a shared pcre2_match_context with a backtrack step limit of
>    10 000 000 and pass it to both pcre2_jit_match() and the fallback
>    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+PgovMi0GLy8GCygKeAYG
> KwYLKAoKCiNXLy8wCwsoCgojV1dXClcqCygKLy8vV1cueHhXV1d4eHgveCsGCygKCgonVy9XV3cy
> eHgveAcGJAsoCi8tLwYvMi0GLy8GCzw8bm9uZT4+3q2+7y8=
>
> Signed-off-by: Petr Matyas <[email protected]>

Thanks for the report and patch. Given that file_contexts* is
root-owned and the caller provides the paths
to lookup, this is only self-DoS. Will fix but don't think it warrants
a security advisory; let us know if you
think differently. See below though for one comment on the code.

> ---
> @@ -262,9 +289,15 @@ int regex_match(struct regex_data *regex, char const *subject, int partial)
>                         return REGEX_ERROR;
>         }
>
> -       rc = pcre2_match(regex->regex, (PCRE2_SPTR)subject,
> -                        PCRE2_ZERO_TERMINATED, 0,
> -                        partial ? PCRE2_PARTIAL_SOFT : 0, match_data, NULL);
> +       rc = pcre2_jit_match(regex->regex, (PCRE2_SPTR)subject,
> +                            PCRE2_ZERO_TERMINATED, 0,
> +                            partial ? PCRE2_PARTIAL_SOFT : 0, match_data,
> +                            match_context);
> +       if (rc == PCRE2_ERROR_JIT_BADOPTION)
> +               rc = pcre2_match(regex->regex, (PCRE2_SPTR)subject,
> +                                PCRE2_ZERO_TERMINATED, 0,
> +                                partial ? PCRE2_PARTIAL_SOFT : 0, match_data,
> +                                match_context);

You only fall back to pcre2_match() on PCRE2_ERROR_JIT_BADOPTION, so
if we hit PCRE2_ERROR_JIT_STACKLIMIT
the lookup will fail rather than falling back to interpreted.