Shimmed grep (embedded ugrep) allocates until host OOM on bounded-quantifier patterns

Status Closed — duplicate
Reported on v2.1.233
Maintainer reply None cached
Activity 2 comments · opened Aug 15, 2026 · closed Aug 20, 2026

Summary

Claude Code installs a shell function that silently replaces grep with the ugrep binary embedded in the claude executable. On patterns containing two wide bounded quantifiers (e.g. .{0,60}(…).{0,300}), ugrep's automaton construction allocates ~235 MB/s linearly until it exhausts host memory (18–20 GB observed), taking the whole machine down.

This crashed my workstation twice (2026-08-08 and 2026-08-15).

The severity is not the OOM itself — it's that the substitution makes the failure undiagnosable. Because the shell function only exists inside a Claude Code session, the natural reproduction step (running the same command in a normal terminal) executes GNU grep and completes in 0.15 s. Every investigation therefore concludes "the command is not the problem" and starts hunting for a memory leak in Claude Code. Two separate sessions spent hours eliminating hooks, subagents, locales, and versions before finding it.

The symptom is also actively misleading: since ugrep runs inside the claude binary (exec -a ugrep "$CLAUDE_CODE_EXECPATH"), the process whose RSS climbs to 20 GB is claude. It looks exactly like a Claude Code memory leak. It isn't one.

Reproduction

# Any small UTF-8 or ASCII text file. Mine was 36 KB of HTML.
PATTERN='.{0,60}(Erbas|10 aout|creche).{0,300}'

# Inside a Claude Code session — bound it, or your machine dies:
( ulimit -v 4000000 -t 25; time grep -c -oiE "$PATTERN" file.html )
#   → 17.05s, SIGSEGV (core dumped) after exhausting the 4 GB cap

# Same session, bypassing the shell function:
( ulimit -v 4000000 -t 25; time command /usr/bin/grep -c -oiE "$PATTERN" file.html )
#   → 0.148s, correct output

Without ulimit, allocation continues past 18 GB and the host OOMs.

Sometimes ugrep detects it and bails with an error instead — but only after burning the memory:

ugrep: error: error at position 116
rb(?:a|A)(?:s|S))(?:[^\n\x80-\xbf][\x80-\xbf]*){0,300}
                         exceeds complexity limits

What I isolated

Measured on a 36 KB file, one variable at a time:

| Pattern | ugrep (session) | GNU grep |
|---|---|---|
| .{0,20}(…).{0,200} | 46 ms | 46 ms |
| .{0,60}(…).{0,300} | 17 s → segfault at 4 GB | 148 ms |

  • Not UTF-8 specific — a pure-ASCII alternation explodes identically.
  • Not the alternation — a single branch explodes identically.
  • Not -i — explodes without it.
  • It is the product of the two bounds. One wide .{0,200} alone is fine (46 ms).

Environment

| | |
|---|---|
| Claude Code | 2.1.233 |
| OS | Ubuntu 24.04.4 LTS, kernel 7.0.0-28 |
| RAM | 31 GB |
| Shell | bash |
| Substituted binary | ugrep 7.5.0 x86_64-pc-linux-gnu +sse2 |
| Real grep | GNU grep 3.11 |

The shell snapshot (~/.claude/shell-snapshots/snapshot-bash-*.sh) defines functions for grep, find (→ bfs), and pkill. Relevant part:

function grep {
  ...
  local _cc_bin="${CLAUDE_CODE_EXECPATH:-}"
  [[ -x $_cc_bin ]] || _cc_bin=/home/user/.local/bin/claude
  if [[ ! -x $_cc_bin ]]; then command grep ${1+"$@"}; return; fi
  ...
  (exec -a ugrep "$_cc_bin" -G --ignore-files --hidden -I --exclude-dir=.git ... ${1+"$@"})
}

Suggestions

Roughly in order of value:

  1. Cap ugrep's memory. A pattern-compilation blowup should fail that one command, not the host. This alone downgrades the bug from "workstation dies" to "command errors out".
  2. Provide an opt-out. There is currently no environment variable guarding these functions — the only escape hatch is knowing to type command grep. Something like CLAUDE_CODE_NO_COMMAND_SHIMS=1 would let users bisect this class of problem in minutes instead of hours.
  3. Make the substitution discoverable. Even a line in claude doctor output ("grep/find/pkill are shimmed to embedded ugrep/bfs in this session") would have saved both investigations. The current behaviour violates the reasonable assumption that grep means the system's grep.
  4. Consider falling back to command grep when ugrep exits abnormally, so behaviour degrades to "slower" rather than "host down".

Happy to provide the core dump, the full shell snapshot, or memory-growth curves if useful.

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗