@@ -953,8 +953,8 @@ module PrefixConstruction {
953953 lastStartState ( state ) and
954954 result = ""
955955 or
956- // the search stops past the last pumpable state.
957- lengthFromStart ( state ) <= max ( lengthFromStart ( any ( State s | isPumpable ( s , _) ) ) ) and
956+ // the search stops past the last redos candidate state.
957+ lengthFromStart ( state ) <= max ( lengthFromStart ( any ( State s | isReDoSCandidate ( s , _) ) ) ) and
958958 exists ( State prev |
959959 // select a unique predecessor (by an arbitrary measure)
960960 prev =
@@ -982,7 +982,7 @@ module PrefixConstruction {
982982 class StateInPumpableRegexp extends State {
983983 pragma [ noinline]
984984 StateInPumpableRegexp ( ) {
985- exists ( State s | isPumpable ( s , _) | getRoot ( s .getRepr ( ) ) = getRoot ( this .getRepr ( ) ) )
985+ exists ( State s | isReDoSCandidate ( s , _) | getRoot ( s .getRepr ( ) ) = getRoot ( this .getRepr ( ) ) )
986986 }
987987 }
988988}
@@ -1012,7 +1012,7 @@ module SuffixConstruction {
10121012 * are rejectable by appending some suffix.
10131013 */
10141014 predicate reachesOnlyRejectableSuffixes ( State fork , string w ) {
1015- isPumpable ( fork , w ) and
1015+ isReDoSCandidate ( fork , w ) and
10161016 forex ( State next | next = process ( fork , w , w .length ( ) - 1 ) | isDefinitelyRejectable ( next ) )
10171017 }
10181018
@@ -1053,7 +1053,7 @@ module SuffixConstruction {
10531053 * chars in `w` any number of times followed by the first `i+1` characters of `w`.
10541054 */
10551055 private State process ( State fork , string w , int i ) {
1056- isPumpable ( fork , w ) and
1056+ isReDoSCandidate ( fork , w ) and
10571057 exists ( State prev |
10581058 i = 0 and prev = fork
10591059 or
@@ -1070,13 +1070,13 @@ module SuffixConstruction {
10701070
10711071/**
10721072 * Holds if `term` may cause exponential backtracking on strings containing many repetitions of `witness`.
1073+ * Gets the minimum possible string that causes exponential backtracking.
10731074 */
10741075predicate isReDoSAttackable ( RegExpTerm term , string witness , State s ) {
10751076 exists ( int i , string c | s = Match ( term , i ) |
10761077 c =
10771078 min ( string w |
1078- isPumpable ( s , w ) and
1079- not isPumpable ( epsilonSucc + ( s ) , _) and
1079+ isReDoSCandidate ( s , w ) and
10801080 SuffixConstruction:: reachesOnlyRejectableSuffixes ( s , w )
10811081 |
10821082 w order by w .length ( ) , w
@@ -1085,6 +1085,15 @@ predicate isReDoSAttackable(RegExpTerm term, string witness, State s) {
10851085 )
10861086}
10871087
1088+ /**
1089+ * Holds if repeating `w' starting at `s` is a candidate for causing exponential backtracking.
1090+ * No check whether a rejected suffix exists has been made.
1091+ */
1092+ predicate isReDoSCandidate ( State s , string w ) {
1093+ isPumpable ( s , w ) and
1094+ not isPumpable ( epsilonSucc + ( s ) , _)
1095+ }
1096+
10881097/**
10891098 * Gets the result of backslash-escaping newlines, carriage-returns and
10901099 * backslashes in `s`.
0 commit comments