Bug 1691184: Recompile if necessary before retrying interrupted regexp r=mgaudet

If an interrupt occurs during regexp execution, we return up the stack to RegExpShared::execute to handle it, then try again. Normally it's safe (if slow) to GC and discard jitcode at this point, because we can fall back to interpreted bytecode (which is not discarded). However, if the input string is long enough, then we [jump straight to compilation without producing bytecode](https://searchfox.org/mozilla-central/rev/7067bbd8194f4346ec59d77c33cd88f06763e090/js/src/vm/RegExpObject.cpp#590-596). In that case, when we resume, we will have neither bytecode nor jitcode, and end up dereferencing a null pointer.

The fix is to recompile after handling the interrupt. In addition to fixing the crash, forcing compilation here should improve our chance of eventual success (compared to resuming in the regexp interpreter).

Differential Revision: https://phabricator.services.mozilla.com/D104479
This commit is contained in:
Iain Ireland 2021-03-09 18:34:43 +00:00
Родитель df8238361e
Коммит b8f20f5b00
2 изменённых файлов: 16 добавлений и 2 удалений

Просмотреть файл

@ -684,6 +684,8 @@ RegExpRunStatus ExecuteRaw(jit::JitCode* code, const CharT* chars,
RegExpRunStatus Interpret(JSContext* cx, MutableHandleRegExpShared re,
HandleLinearString input, size_t startIndex,
VectorMatchPairs* matches) {
MOZ_ASSERT(re->getByteCode(input->hasLatin1Chars()));
HandleScope handleScope(cx->isolate);
V8HandleRegExp wrappedRegExp(v8::internal::JSRegExp(re), cx->isolate);
V8HandleString wrappedInput(v8::internal::String(input), cx->isolate);

Просмотреть файл

@ -591,12 +591,16 @@ bool RegExpShared::compileIfNecessary(JSContext* cx,
// We start by interpreting regexps, then compile them once they are
// sufficiently hot. For very long input strings, we tier up eagerly.
codeKind = RegExpShared::CodeKind::Bytecode;
if (IsNativeRegExpEnabled() &&
(re->markedForTierUp() || input->length() > 1000)) {
if (re->markedForTierUp() || input->length() > 1000) {
codeKind = RegExpShared::CodeKind::Jitcode;
}
}
// Fall back to bytecode if native codegen is not available.
if (!IsNativeRegExpEnabled() && codeKind == RegExpShared::CodeKind::Jitcode) {
codeKind = RegExpShared::CodeKind::Bytecode;
}
bool needsCompile = false;
if (re->kind() == RegExpShared::Kind::Unparsed) {
needsCompile = true;
@ -669,6 +673,14 @@ RegExpRunStatus RegExpShared::execute(JSContext* cx,
return RegExpRunStatus_Error;
}
if (interruptRetries++ < maxInterruptRetries) {
// The initial execution may have been interpreted, or the
// interrupt may have triggered a GC that discarded jitcode.
// To maximize the chance of succeeding before being
// interrupted again, we want to ensure we are compiled.
if (!compileIfNecessary(cx, re, input,
RegExpShared::CodeKind::Jitcode)) {
return RegExpRunStatus_Error;
}
continue;
}
}