From dcd8e05f0cf2d6be8996b5e979544ffead4d18bd Mon Sep 17 00:00:00 2001 From: Andre Wachsmuth Date: Sat, 27 Apr 2024 18:40:58 +0100 Subject: [PATCH] Make regexp execution loop interruptible #1189 (#1440) * Make regexp execution loop interruptible #1189 Uses Thread.currentThread.isInterrupted() so that the interruption flag remains set to true, we only terminate the RegExp evaluation loop, but other (potentially third-party calling) code may still have to check for the interrupted flag to stop its execution as well. I also added a test with a long-running regexp that fails without the interrupt check. Co-authored-by: Andre Wachsmuth --- src/org/mozilla/javascript/regexp/NativeRegExp.java | 9 ++++++--- .../javascript/tests/ObserveInstructionCountTest.java | 7 +++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/org/mozilla/javascript/regexp/NativeRegExp.java b/src/org/mozilla/javascript/regexp/NativeRegExp.java index 2dadc213dd..c776c45fda 100644 --- a/src/org/mozilla/javascript/regexp/NativeRegExp.java +++ b/src/org/mozilla/javascript/regexp/NativeRegExp.java @@ -1857,7 +1857,8 @@ private static int simpleMatch( return -1; } - private static boolean executeREBytecode(REGlobalData gData, String input, int end) { + private static boolean executeREBytecode( + Context cx, REGlobalData gData, String input, int end) { int pc = 0; byte[] program = gData.regexp.program; int continuationOp = REOP_END; @@ -1887,6 +1888,7 @@ private static boolean executeREBytecode(REGlobalData gData, String input, int e } for (; ; ) { + ScriptRuntime.addInstructionCount(cx, 5); if (reopIsSimple(op)) { int match = simpleMatch(gData, input, op, program, pc, end, true); @@ -2316,6 +2318,7 @@ && simpleMatch(gData, input, op, program, pc, end, false) < 0) { } private static boolean matchRegExp( + Context cx, REGlobalData gData, RECompiled re, String input, @@ -2369,7 +2372,7 @@ && upcase(matchCh) == upcase((char) anchorCh))) { for (int j = 0; j < re.parenCount; j++) { gData.parens[j] = -1L; } - boolean result = executeREBytecode(gData, input, end); + boolean result = executeREBytecode(cx, gData, input, end); gData.backTrackStackTop = null; gData.stateStackTop = null; @@ -2403,7 +2406,7 @@ Object executeRegExp( // // Call the recursive matcher to do the real work. // - boolean matches = matchRegExp(gData, re, str, start, end, res.multiline); + boolean matches = matchRegExp(cx, gData, re, str, start, end, res.multiline); if (!matches) { if (matchType != PREFIX) return null; return Undefined.instance; diff --git a/testsrc/org/mozilla/javascript/tests/ObserveInstructionCountTest.java b/testsrc/org/mozilla/javascript/tests/ObserveInstructionCountTest.java index a83b351c6b..72d34115ea 100644 --- a/testsrc/org/mozilla/javascript/tests/ObserveInstructionCountTest.java +++ b/testsrc/org/mozilla/javascript/tests/ObserveInstructionCountTest.java @@ -118,4 +118,11 @@ public void forever() { String source = "for(;;);"; baseCase(source); } + + @Test + public void longRunningRegExp() { + String source = + "/(.*){1,32000}[bc]/.test(\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\");"; + baseCase(source); + } }