[GH-ISSUE #5257] [Bug] Large search_bytes patterns overflow input validation before provider admission #3212

Closed
opened 2026-10-31 00:37:54 +00:00 by bsakaguchi · 1 comment
Owner

Originally created by @morluto on GitHub (Oct 19, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/5257

Before submitting

  • Searched open/closed reports for search_bytes, byte pattern, regex and stack failures.
  • Synthetic arguments only; no credentials or target data.

Area

MCP or agent integration

Observed behavior

On main 49b4edfe77, a compiled public stdio MCP search_bytes call with a valid 8 MiB pattern returns isError: true with plain text Maximum call stack size exceeded. The same happens to an 8 MiB pattern with one invalid final pair. Each request fits within the default 10 MiB stdio input buffer. The small valid-pattern control correctly returns the structured target_unavailable diagnostic because no app is open.

Reproduction

Executed once through the compiled CLI's MCP stdio server using the installed SDK, pinned Node 24.18.0 and locked dependencies. From a built checkout:

import { Client } from '@modelcontextprotocol/client';
import { StdioClientTransport } from '@modelcontextprotocol/client/stdio';
const transport = new StdioClientTransport({
  command: process.execPath, args: ['scripts/rea.mjs', 'mcp'],
  env: { PATH: process.env.PATH, REA_LOG_LEVEL: 'silent',
    HOPPER_LAUNCHER_PATH: '/rea-unconfigured-deep-provider/hopper' },
  stderr: 'pipe'
});
const client = new Client({ name: 'byte-pattern-audit', version: '1' });
try {
  await client.connect(transport);
  for (const pattern of ['aa', 'aa'.repeat(4 * 1024 * 1024)]) {
    const result = await client.callTool({ name: 'search_bytes', arguments: { pattern } });
    console.log({ chars: pattern.length, isError: result.isError, content: result.content });
  }
} finally { await client.close(); await transport.close(); }

Run as an ESM Node script. No binary/provider was opened and no target was executed. The analogous source-only call to the actual public input schema also throws for a 4 MiB valid pattern.

Expected behavior

The large valid input should pass canonical validation and reach the ordinary no-open-target diagnostic in this reproduction. Malformed input should receive the existing normal input-validation error. Do not silently truncate a pattern, or add a new arbitrary size cap to hide the parser failure.

Impact and scope

Observed: large valid and malformed caller input loses the normal error contract before provider admission. No process/server crash, real Ghidra execution, Windows/Linux run or published npm reproduction was performed. CLI argv-size limits are platform-dependent and were not tested. Source-derived: both the public tool and Ghidra socket input schema share the vulnerable bytePatternFields; its semantic refinement repeats parsing and allocates byte/mask arrays solely for validation.

REA version

Package 6.7.0; source and compiled checkout 49b4edfe77.

Runtime and platform

Node 24.18.0, macOS 26.3 arm64.

Target and artifact context

Synthetic hexadecimal/wildcard byte-pattern arguments; no binary open or deep provider needed.

Interface

MCP

Earliest failure stage

Request parsing or validation

Relevant output or evidence

Small control: structured JSON error code target_unavailable with message No app is open....

Large valid and malformed controls: isError: true, content [{"type":"text","text":"Maximum call stack size exceeded"}].

Analysis or suspected cause

Source-derived: repeated wildcard/hex pair regexes with inter-pair whitespace in src/domain/native/bytePattern.ts exhaust V8's regexp stack. The semantic refinement calls the same regexes again and materializes arrays that no production consumer uses. A domain-owned iterative scanner can preserve exact pair/wildcard/whitespace/mask semantics without size-dependent regex failure or those allocations. This was found while auditing the pattern family adjacent to #5253, which concerned base64, not byte-pattern inputs.

Originally created by @morluto on GitHub (Oct 19, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/5257 ### Before submitting - [x] Searched open/closed reports for search_bytes, byte pattern, regex and stack failures. - [x] Synthetic arguments only; no credentials or target data. ### Area MCP or agent integration ### Observed behavior On main 49b4edfe7788495ba548e423c9c3b82dc14cca03, a compiled public stdio MCP `search_bytes` call with a valid 8 MiB pattern returns `isError: true` with plain text `Maximum call stack size exceeded`. The same happens to an 8 MiB pattern with one invalid final pair. Each request fits within the default 10 MiB stdio input buffer. The small valid-pattern control correctly returns the structured `target_unavailable` diagnostic because no app is open. ### Reproduction Executed once through the compiled CLI's MCP stdio server using the installed SDK, pinned Node 24.18.0 and locked dependencies. From a built checkout: ```js import { Client } from '@modelcontextprotocol/client'; import { StdioClientTransport } from '@modelcontextprotocol/client/stdio'; const transport = new StdioClientTransport({ command: process.execPath, args: ['scripts/rea.mjs', 'mcp'], env: { PATH: process.env.PATH, REA_LOG_LEVEL: 'silent', HOPPER_LAUNCHER_PATH: '/rea-unconfigured-deep-provider/hopper' }, stderr: 'pipe' }); const client = new Client({ name: 'byte-pattern-audit', version: '1' }); try { await client.connect(transport); for (const pattern of ['aa', 'aa'.repeat(4 * 1024 * 1024)]) { const result = await client.callTool({ name: 'search_bytes', arguments: { pattern } }); console.log({ chars: pattern.length, isError: result.isError, content: result.content }); } } finally { await client.close(); await transport.close(); } ``` Run as an ESM Node script. No binary/provider was opened and no target was executed. The analogous source-only call to the actual public input schema also throws for a 4 MiB valid pattern. ### Expected behavior The large valid input should pass canonical validation and reach the ordinary no-open-target diagnostic in this reproduction. Malformed input should receive the existing normal input-validation error. Do not silently truncate a pattern, or add a new arbitrary size cap to hide the parser failure. ### Impact and scope Observed: large valid and malformed caller input loses the normal error contract before provider admission. No process/server crash, real Ghidra execution, Windows/Linux run or published npm reproduction was performed. CLI argv-size limits are platform-dependent and were not tested. Source-derived: both the public tool and Ghidra socket input schema share the vulnerable `bytePatternFields`; its semantic refinement repeats parsing and allocates byte/mask arrays solely for validation. ### REA version Package 6.7.0; source and compiled checkout 49b4edfe7788495ba548e423c9c3b82dc14cca03. ### Runtime and platform Node 24.18.0, macOS 26.3 arm64. ### Target and artifact context Synthetic hexadecimal/wildcard byte-pattern arguments; no binary open or deep provider needed. ### Interface MCP ### Earliest failure stage Request parsing or validation ### Relevant output or evidence Small control: structured JSON error code `target_unavailable` with message `No app is open...`. Large valid and malformed controls: `isError: true`, content `[{"type":"text","text":"Maximum call stack size exceeded"}]`. ### Analysis or suspected cause Source-derived: repeated wildcard/hex pair regexes with inter-pair whitespace in `src/domain/native/bytePattern.ts` exhaust V8's regexp stack. The semantic refinement calls the same regexes again and materializes arrays that no production consumer uses. A domain-owned iterative scanner can preserve exact pair/wildcard/whitespace/mask semantics without size-dependent regex failure or those allocations. This was found while auditing the pattern family adjacent to #5253, which concerned base64, not byte-pattern inputs.
bsakaguchi 2026-10-31 00:37:54 +00:00
  • closed this issue
  • added the
    triage
    bug
    labels
Author
Owner

@morluto commented on GitHub (Oct 19, 2026):

Fixed on main in 255d906e1.

The shared domain validator now scans byte pairs and aligned masks with bounded scalar state. It preserves complete-pair syntax, ECMAScript whitespace, whole-byte wildcards, mask length/effective-bit checks and first-error precedence. Removed the repeated regex gates and the duplicate parsing/materialized arrays that production discarded. Advertised fields describe the syntax; both public and Ghidra input schemas still enforce the exact shared refinement before dispatch. Caller strings are retained unchanged; no arbitrary length cap was introduced.

Executed on pinned Node24.18/macOS26.3: 8 focused test files/83 tests passed, including public compiled stdio8MiB valid/malformed patterns, a long mismatched mask and connection reuse; Ghidra boundary fixtures retain original queries. Twenty thousand deterministic small cases matched previous grammar/first-error behavior. npm run check, npm run docs:check and test discovery passed.

Installed Gemini CLI0.29.5 forwarded/validated207schemas and241canonicalexamples through its local model adapter. The first full call hit a separate macOS selected-path/subject-identity assertion; a canonical-TMPDIR rerun passed end to end. The resulting independent path-provenance finding is #5258. This is not live Google API or real Ghidra search verification. No Windows/Linux or alternate Node-major run was performed.

<!-- gh-comment-id:6269257298 --> @morluto commented on GitHub (Oct 19, 2026): Fixed on main in 255d906e1. The shared domain validator now scans byte pairs and aligned masks with bounded scalar state. It preserves complete-pair syntax, ECMAScript whitespace, whole-byte wildcards, mask length/effective-bit checks and first-error precedence. Removed the repeated regex gates and the duplicate parsing/materialized arrays that production discarded. Advertised fields describe the syntax; both public and Ghidra input schemas still enforce the exact shared refinement before dispatch. Caller strings are retained unchanged; no arbitrary length cap was introduced. Executed on pinned Node24.18/macOS26.3: 8 focused test files/83 tests passed, including public compiled stdio8MiB valid/malformed patterns, a long mismatched mask and connection reuse; Ghidra boundary fixtures retain original queries. Twenty thousand deterministic small cases matched previous grammar/first-error behavior. `npm run check`, `npm run docs:check` and test discovery passed. Installed Gemini CLI0.29.5 forwarded/validated207schemas and241canonicalexamples through its local model adapter. The first full call hit a separate macOS selected-path/subject-identity assertion; a canonical-TMPDIR rerun passed end to end. The resulting independent path-provenance finding is #5258. This is not live Google API or real Ghidra search verification. No Windows/Linux or alternate Node-major run was performed.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#3212
No description provided.