Subject: Re: [PATCH] grep: fall back to interpreter mode if JIT fails

From: Ævar Arnfjörð Bjarmason <[email-masked]>
Date: Fri, 16 Dec 2022 17:12:46 +0100

On Fri, Dec 16 2022, Mathias Krause wrote:

> From: Carlo Marcelo Arenas Belón <[email-masked]>
>
> Under Linux systems with SELinux's 'deny_execmem' or PaX's MPROTECT
> enabled, the allocation of PCRE2's JIT rwx memory may be prohibited,
> making pcre2_jit_compile() fail with PCRE2_ERROR_NOMEMORY (-48):
>
>   [user@fedora git]$ git grep -c PCRE2_JIT
>   grep.c:1
>
>   [user@fedora git]$ # Enable SELinux's W^X policy
>   [user@fedora git]$ sudo semanage boolean -m -1 deny_execmem
>
>   [user@fedora git]$ # JIT memory allocation fails, breaking 'git grep'
>   [user@fedora git]$ git grep -c PCRE2_JIT
>   fatal: Couldn't JIT the PCRE2 pattern 'PCRE2_JIT', got '-48'
>
> Instead of failing hard in this case and making 'git grep' unusable on
> such systems, simply fall back to interpreter mode, leading to a much
> better user experience.
>
> Such a change was already proposed 4 years ago [1] but wasn't merged for
> unknown reasons.

Yeah, it's unfortunate that it fell between the cracks, and it's good to
have such a fallback mechanism.

> 1. https://lore.kernel.org/r/[email-masked]
>
> Signed-off-by: Carlo Marcelo Arenas Belón <[email-masked]>
> Signed-off-by: Mathias Krause <[email-masked]>	# tweaked changelog, added comment
> ---
>  grep.c | 17 +++++++++++++++--
>  1 file changed, 15 insertions(+), 2 deletions(-)
>
> diff --git a/grep.c b/grep.c
> index 06eed694936c..f2ada528b21d 100644
> --- a/grep.c
> +++ b/grep.c
> @@ -317,8 +317,21 @@ static void compile_pcre2_pattern(struct grep_pat *p, const struct grep_opt *opt
>  	pcre2_config(PCRE2_CONFIG_JIT, &p->pcre2_jit_on);
>  	if (p->pcre2_jit_on) {
>  		jitret = pcre2_jit_compile(p->pcre2_pattern, PCRE2_JIT_COMPLETE);
> -		if (jitret)
> -			die("Couldn't JIT the PCRE2 pattern '%s', got '%d'\n", p->pattern, jitret);
> +		if (jitret) {
> +			/*
> +			 * Even though pcre2_config(PCRE2_CONFIG_JIT, ...)
> +			 * indicated JIT support, the library might still
> +			 * fail to generate JIT code for various reasons,
> +			 * e.g. when SELinux's 'deny_execmem' or PaX's
> +			 * MPROTECT prevent creating W|X memory mappings.
> +			 *
> +			 * Instead of faling hard, fall back to interpreter
> +			 * mode, just as if the pattern was prefixed with
> +			 * '(*NO_JIT)'.
> +			 */
> +			p->pcre2_jit_on = 0;
> +			return;

>From my reading of the docs it returns two different codes:
PCRE2_ERROR_JIT_BADOPTION or PCRE2_ERROR_NOMEMORY.

This change will start treating both the same, but we only want to allow
the latter, surely?

So shouldn't this be e.g.:

	jitret = pcre2_jit_compile(...);
	if (jitret == PCRE2_ERROR_NOMEMORY) {
		/* code you added here */
	} else if (jitret) {
		BUG(...);
	}

I put a BUG() there, we could keep the die(), but
PCRE2_ERROR_JIT_BADOPTION is really more appropriate as a BUG(), and if
it starts returning any other codes our use of the API is also in some
unknown state, so we should also BUG() out.