diff --git a/glib/gregex.c b/glib/gregex.c index 466abfba0..d1633a8b2 100644 --- a/glib/gregex.c +++ b/glib/gregex.c @@ -252,6 +252,13 @@ struct _GRegex GRegexMatchFlags orig_match_opts; /* options used as default match options, gregex values */ uint32_t jit_options; /* options which were enabled for jit compiler */ JITStatus jit_status; /* indicates the status of jit compiler for this compiled regex */ + /* The jit_status here does _not_ correspond to whether we used the JIT in the last invocation, + * which may be affected by match_options or a JIT_STACK_LIMIT error, but whether it was ever + * enabled for the current regex AND current set of jit_options. + * JIT_STATUS_DEFAULT means enablement was never tried, + * JIT_STATUS_ENABLED means it was tried and successful (even if we're not currently using it), + * and JIT_STATUS_DISABLED means it was tried and failed (so we shouldn't try again). + */ }; /* TRUE if ret is an error code, FALSE otherwise. */ @@ -918,35 +925,47 @@ enable_jit_with_match_options (GMatchInfo *match_info, /* no new options enabled */ if (new_jit_options == old_jit_options) - return match_info->regex->jit_status; + { + g_assert (match_info->regex->jit_status != JIT_STATUS_DEFAULT); + return match_info->regex->jit_status; + } retval = pcre2_jit_compile (match_info->regex->pcre_re, new_jit_options); - switch (retval) + if (retval == 0) { - case 0: /* JIT enabled successfully */ + match_info->regex->jit_status = JIT_STATUS_ENABLED; + match_info->regex->jit_options = new_jit_options; /* Set min stack size for JIT to 32KiB and max to 512KiB */ match_info->jit_stack = pcre2_jit_stack_create (1 << 15, 1 << 19, NULL); pcre2_jit_stack_assign (match_info->match_context, NULL, match_info->jit_stack); - return JIT_STATUS_ENABLED; - case PCRE2_ERROR_NOMEMORY: - g_debug ("JIT compilation was requested with G_REGEX_OPTIMIZE, " - "but JIT was unable to allocate executable memory for the " - "compiler. Falling back to interpretive code."); - return JIT_STATUS_DISABLED; - case PCRE2_ERROR_JIT_BADOPTION: - g_debug ("JIT compilation was requested with G_REGEX_OPTIMIZE, " - "but JIT support is not available. Falling back to " - "interpretive code."); - return JIT_STATUS_DISABLED; - break; - default: - g_debug ("JIT compilation was requested with G_REGEX_OPTIMIZE, " - "but request for JIT support had unexpectedly failed (error %d). " - "Falling back to interpretive code.", retval); - return JIT_STATUS_DISABLED; - break; } + else + { + match_info->regex->jit_status = JIT_STATUS_DISABLED; + + switch (retval) + { + case PCRE2_ERROR_NOMEMORY: + g_debug ("JIT compilation was requested with G_REGEX_OPTIMIZE, " + "but JIT was unable to allocate executable memory for the " + "compiler. Falling back to interpretive code."); + break; + case PCRE2_ERROR_JIT_BADOPTION: + g_debug ("JIT compilation was requested with G_REGEX_OPTIMIZE, " + "but JIT support is not available. Falling back to " + "interpretive code."); + break; + default: + g_debug ("JIT compilation was requested with G_REGEX_OPTIMIZE, " + "but request for JIT support had unexpectedly failed (error %d). " + "Falling back to interpretive code.", + retval); + break; + } + } + + return match_info->regex->jit_status; g_assert_not_reached (); } diff --git a/glib/tests/regex.c b/glib/tests/regex.c index f18db483c..108252629 100644 --- a/glib/tests/regex.c +++ b/glib/tests/regex.c @@ -2517,6 +2517,28 @@ test_unmatched_named_subpattern (void) g_regex_unref (regex); } +static void +test_compiled_regex_after_jit_failure (void) +{ + GRegex *regex = NULL; + char string[LARGE_TEST_STRING_LEN]; + + g_test_summary ("Test that failed OPTIMIZE regex doesn't cause issues on subsequent matches"); + g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/2824"); + + regex = g_regex_new ("^(?:[ \t\n]|[^[:cntrl:]])*$", G_REGEX_OPTIMIZE, 0, NULL); + + /* Generate large enough string to cause JIT failure on this regex. */ + memset (string, '*', LARGE_TEST_STRING_LEN); + string[LARGE_TEST_STRING_LEN - 1] = '\0'; + + g_assert_true (g_regex_match (regex, string, 0, NULL)); + /* Second assert here is the key - does the first JIT overflow mess up our state? */ + g_assert_true (g_regex_match (regex, string, 0, NULL)); + + g_regex_unref (regex); +} + int main (int argc, char *argv[]) { @@ -2537,6 +2559,7 @@ main (int argc, char *argv[]) g_test_add_func ("/regex/compile-errors", test_compile_errors); g_test_add_func ("/regex/jit-unsupported-matching", test_jit_unsupported_matching_options); g_test_add_func ("/regex/unmatched-named-subpattern", test_unmatched_named_subpattern); + g_test_add_func ("/regex/compiled-regex-after-jit-failure", test_compiled_regex_after_jit_failure); /* TEST_NEW(pattern, compile_opts, match_opts) */ TEST_NEW("[A-Z]+", G_REGEX_CASELESS | G_REGEX_EXTENDED | G_REGEX_OPTIMIZE, G_REGEX_MATCH_NOTBOL | G_REGEX_MATCH_PARTIAL);