libstdc++'s std::regex matcher recurses about once per matched character, so '.*', 'a+' or '(a|b)*' on a line of a few tens of thousands of characters (minified JS/JSON) overflowed the 8 MiB stack and crashed the editor from the regex search prompt or regex replace. Regex search and regex replace now run their matching on a worker thread with a 1 GiB reserved stack (RunWithLargeStack; the caller waits, so nothing is shared concurrently). Renderers skip regex match highlighting on lines over 10,000 bytes. Catastrophic backtracking (e.g. (a*)*b) is still possible: std::regex has no time limit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015rcVQiiWyTzoNrsvLSaAfB
23 lines
938 B
C++
23 lines
938 B
C++
// RegexGuard.h - keep std::regex from overflowing the stack
|
|
//
|
|
// libstdc++'s std::regex matcher recurses roughly once per character of a
|
|
// match, so a pattern such as ".*" or "a+" on a line of a few tens of
|
|
// thousands of characters (minified JS/JSON) overflows a default 8 MiB
|
|
// stack and crashes the editor.
|
|
#pragma once
|
|
|
|
#include <cstddef>
|
|
#include <functional>
|
|
|
|
namespace kte {
|
|
// Renderers highlight regex matches only on lines up to this many bytes;
|
|
// longer lines are drawn without match highlighting.
|
|
constexpr std::size_t kRegexRenderLineLimit = 10000;
|
|
|
|
// Run fn to completion on a thread with a large stack (address space is
|
|
// reserved, pages are committed only as used), so regex work over long lines
|
|
// cannot overflow. If such a thread cannot be created, fn runs on the calling
|
|
// thread. Exceptions thrown by fn are rethrown to the caller.
|
|
void RunWithLargeStack(const std::function<void()> &fn);
|
|
} // namespace kte
|