Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions re2/dfa.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2106,6 +2106,13 @@ bool DFA::PossibleMatchRange(std::string* min, std::string* max, int maxlen) {
}
if (!extended) {
// Done, no need for PrefixSuccessor.
// If *max is empty and *min is not, we have no way to express
// "no maximum string" and must report failure: returning true
// would give a range with min > max, violating the documented
// invariant. (The empty regexp, with min == max == "", is
// handled correctly here.)
if (max->empty() && !min->empty())
return false;
return true;
}
}
Expand Down
8 changes: 8 additions & 0 deletions re2/re2.cc
Original file line number Diff line number Diff line change
Expand Up @@ -626,6 +626,14 @@ bool RE2::PossibleMatchRange(std::string* min, std::string* max,
// but we still have useful information from prefix_.
// Round up *max to allow any possible suffix.
PrefixSuccessor(max);
// If *max is empty, the prefix consisted entirely of 0xFF bytes
// and PrefixSuccessor has no successor to return. This would
// produce a range with min > max, violating the documented
// invariant "min <= s <= max" for any anchored match s.
// (The empty regexp is handled correctly elsewhere: it returns
// min == max == "".)
if (max->empty() && !min->empty())
return false;
} else {
// Nothing useful.
*min = "";
Expand Down
10 changes: 10 additions & 0 deletions re2/testing/possible_match_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,16 @@ TEST(PossibleMatchRange, Failures) {
// Fails because it's a malformed regexp.
EXPECT_FALSE(RE2("*hello").PossibleMatchRange(&min, &max, 10))
<< "min=" << absl::CEscape(min) << ", max=" << absl::CEscape(max);

// A repeated high byte in Latin1 used to return true with min > max
// (contract violation): min="\xFF", max="". The regexp "^<0xFF><0xFF>+"
// never matches the empty string, so the empty max is a lie.
EXPECT_FALSE(RE2("^\xFF\xFF+", RE2::Latin1).
PossibleMatchRange(&min, &max, 100))
<< "min=" << absl::CEscape(min) << ", max=" << absl::CEscape(max);
EXPECT_FALSE(RE2("^\xFF+", RE2::Latin1).
PossibleMatchRange(&min, &max, 100))
<< "min=" << absl::CEscape(min) << ", max=" << absl::CEscape(max);
}

// Exhaustive test: generate all regexps within parameters,
Expand Down