From 3be20c56bc82ea6c898070015f05e1bb7627d810 Mon Sep 17 00:00:00 2001 From: VoltVoks <39782935+VoltVoks@users.noreply.github.com> Date: Thu, 21 May 2026 15:41:59 -0700 Subject: [PATCH 1/2] Avoid Abseil hash cache crashes under ASan --- re2/compile.cc | 15 ++++++++++----- re2/dfa.cc | 13 ++++++++----- re2/testing/re2_test.cc | 19 +++++++++++++++++++ 3 files changed, 37 insertions(+), 10 deletions(-) diff --git a/re2/compile.cc b/re2/compile.cc index 95c1b32dd..c9860a67e 100644 --- a/re2/compile.cc +++ b/re2/compile.cc @@ -12,9 +12,9 @@ #include #include +#include #include -#include "absl/container/flat_hash_map.h" #include "absl/log/absl_check.h" #include "absl/log/absl_log.h" #include "absl/strings/string_view.h" @@ -215,7 +215,7 @@ class Compiler : public Regexp::Walker { int64_t max_mem_; // Total memory budget. - absl::flat_hash_map rune_cache_; + std::unordered_map rune_cache_; Frag rune_range_; RE2::Anchor anchor_; // anchor mode for RE2::Set @@ -482,9 +482,12 @@ static uint64_t MakeRuneCacheKey(uint8_t lo, uint8_t hi, bool foldcase, int Compiler::CachedRuneByteSuffix(uint8_t lo, uint8_t hi, bool foldcase, int next) { uint64_t key = MakeRuneCacheKey(lo, hi, foldcase, next); - absl::flat_hash_map::const_iterator it = rune_cache_.find(key); - if (it != rune_cache_.end()) - return it->second; + if (!rune_cache_.empty()) { + std::unordered_map::const_iterator it = + rune_cache_.find(key); + if (it != rune_cache_.end()) + return it->second; + } int id = UncachedRuneByteSuffix(lo, hi, foldcase, next); rune_cache_[key] = id; return id; @@ -497,6 +500,8 @@ bool Compiler::IsCachedRuneByteSuffix(int id) { int next = inst_[id].out(); uint64_t key = MakeRuneCacheKey(lo, hi, foldcase, next); + if (rune_cache_.empty()) + return false; return rune_cache_.find(key) != rune_cache_.end(); } diff --git a/re2/dfa.cc b/re2/dfa.cc index d587a5520..c072dd5a6 100644 --- a/re2/dfa.cc +++ b/re2/dfa.cc @@ -31,13 +31,13 @@ #include #include #include +#include +#include #include #include #include "absl/base/call_once.h" #include "absl/base/thread_annotations.h" -#include "absl/container/flat_hash_map.h" -#include "absl/container/flat_hash_set.h" #include "absl/hash/hash.h" #include "absl/log/absl_check.h" #include "absl/log/absl_log.h" @@ -165,7 +165,7 @@ class DFA { } }; - typedef absl::flat_hash_set StateSet; + typedef std::unordered_set StateSet; private: // Make it easier to swap in a scalable reader-writer mutex. @@ -801,6 +801,9 @@ DFA::State* DFA::CachedState(int* inst, int ninst, uint32_t flag) { // Clear the cache. Must hold cache_mutex_.w or be in destructor. void DFA::ClearCache() { + if (state_cache_.empty()) + return; + StateSet::iterator begin = state_cache_.begin(); StateSet::iterator end = state_cache_.end(); while (begin != end) { @@ -1932,7 +1935,7 @@ int DFA::BuildAllStates(const Prog::DFAStateCallback& cb) { // Add start state to work queue. // Note that any State* that we handle here must point into the cache, // so we can simply depend on pointer-as-a-number hashing and equality. - absl::flat_hash_map m; + std::unordered_map m; std::deque q; m.emplace(params.start, static_cast(m.size())); q.push_back(params.start); @@ -2006,7 +2009,7 @@ bool DFA::PossibleMatchRange(std::string* min, std::string* max, int maxlen) { // Also note that previously_visited_states[UnseenStatePtr] will, in the STL // tradition, implicitly insert a '0' value at first use. We take advantage // of that property below. - absl::flat_hash_map previously_visited_states; + std::unordered_map previously_visited_states; // Pick out start state for anchored search at beginning of text. RWLocker l(&cache_mutex_); diff --git a/re2/testing/re2_test.cc b/re2/testing/re2_test.cc index 5c1822d7f..6a080bdee 100644 --- a/re2/testing/re2_test.cc +++ b/re2/testing/re2_test.cc @@ -1196,6 +1196,25 @@ TEST(RE2, NoCrash) { ASSERT_FALSE(RE2::PartialMatch("a\\b", re)); } + // Issue 596: compiling a UTF-8 char class with a trailing hyphen should not + // crash while consulting the compiler's rune suffix cache. + { + const std::string pattern = "((([\xC3\xA4-])))"; + RE2 re(pattern, RE2::Quiet); + ASSERT_TRUE(re.ok()); + } + + // Issue 589: a tiny DFA memory budget should fail safely without crashing + // while tearing down an empty state cache. + { + RE2::Options opt; + opt.set_log_errors(false); + opt.set_max_mem(4096); + RE2 re("A", opt); + ASSERT_TRUE(re.ok()); + ASSERT_TRUE(RE2::PartialMatch("fooAbar", re)); + } + // Test that using an enormous regexp doesn't crash { RE2 re("(((.{100}){100}){100}){100}", RE2::Quiet); From 23f0b7fcff8be163d33680c74f1b3816690d1484 Mon Sep 17 00:00:00 2001 From: VoltVoks <39782935+VoltVoks@users.noreply.github.com> Date: Fri, 10 Jul 2026 21:59:28 -0700 Subject: [PATCH 2/2] chore: retrigger Google CLA check