grep/pcre2: better support invalid UTF-8 haystacks
Improve the support for invalid UTF-8 haystacks given a non-ASCII
needle when using the PCREv2 backend.
This is a more complete fix for a bug I started to fix in
870eea8166 (grep: do not enter PCRE2_UTF mode on fixed matching,
2019-07-26), now that PCREv2 has the PCRE2_MATCH_INVALID_UTF mode we
can make use of it.
This fixes the sort of case described in 8a5999838e (grep: stess test
PCRE v2 on invalid UTF-8 data, 2019-07-26), i.e.:
- The subject string is non-ASCII (e.g. "ævar")
- We're under a is_utf8_locale(), e.g. "en_US.UTF-8", not "C"
- We are using --ignore-case, or we're a non-fixed pattern
If those conditions were satisfied and we matched found non-valid
UTF-8 data PCREv2 might bark on it, in practice this only happened
under the JIT backend (turned on by default on most platforms).
Ultimately this fixes a "regression" in b65abcafc7 ("grep: use PCRE v2
for optimized fixed-string search", 2019-07-01), I'm putting that in
scare-quotes because before then we wouldn't properly support these
complex case-folding, locale etc. cases either, it just broke in
different ways.
There was a bug related to this the PCRE2_NO_START_OPTIMIZE flag fixed
in PCREv2 10.36. It can be worked around by setting the
PCRE2_NO_START_OPTIMIZE flag. Let's do that in those cases, and add
tests for the bug.
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Ævar Arnfjörð Bjarmason committedJan 24, 2021 at 18:28 UTC95ca1f987edd23389e3079d0a7fe6d0f89927b68
index efeb6dc58d..ad0af66a26 100644--- a/grep.c+++ b/grep.c@@ -492,7 +492,23 @@ static void compile_pcre2_pattern(struct grep_pat *p, const struct grep_opt *opt } if (!opt->ignore_locale && is_utf8_locale() && has_non_ascii(p->pattern) && !(!opt->ignore_case && (p->fixed || p->is_fixed)))- options |= PCRE2_UTF;+ options |= (PCRE2_UTF | PCRE2_MATCH_INVALID_UTF);++ /* Work around https://bugs.exim.org/show_bug.cgi?id=2642 fixed in 10.36 */+ if (PCRE2_MATCH_INVALID_UTF && options & (PCRE2_UTF | PCRE2_CASELESS)) {+ struct strbuf buf;+ int len;+ int err;++ if ((len = pcre2_config(PCRE2_CONFIG_VERSION, NULL)) < 0)+ BUG("pcre2_config(..., NULL) failed: %d", len);+ strbuf_init(&buf, len + 1);+ if ((err = pcre2_config(PCRE2_CONFIG_VERSION, buf.buf)) < 0)+ BUG("pcre2_config(..., buf.buf) failed: %d", err);+ if (versioncmp(buf.buf, "10.36") < 0)+ options |= PCRE2_NO_START_OPTIMIZE;+ strbuf_release(&buf);+ } p->pcre2_pattern = pcre2_compile((PCRE2_SPTR)p->pattern, p->patternlen, options, &error, &erroffset,
grep.h
+4
index b5c4e223a8..ade21c8812 100644--- a/grep.h+++ b/grep.h@@ -18,6 +18,10 @@ typedef int pcre2_code; typedef int pcre2_match_data; typedef int pcre2_compile_context; #endif+#ifndef PCRE2_MATCH_INVALID_UTF+/* PCRE2_MATCH_* dummy also with !USE_LIBPCRE2, for test-pcre2-config.c */+#define PCRE2_MATCH_INVALID_UTF 0+#endif #include "thread-utils.h" #include "userdiff.h"
t/helper/test-pcre2-config.c
+12
new file mode 100644index 0000000000..5258fdddba--- /dev/null+++ b/t/helper/test-pcre2-config.c@@ -0,0 +1,12 @@+#include "test-tool.h"+#include "cache.h"+#include "grep.h"++int cmd__pcre2_config(int argc, const char **argv)+{+ if (argc == 2 && !strcmp(argv[1], "has-PCRE2_MATCH_INVALID_UTF")) {+ int value = PCRE2_MATCH_INVALID_UTF;+ return !value;+ }+ return 1;+}