[GH-ISSUE #7198] [Bug] Library identification enforces resource budgets after parsing oversized input #4298

Open
opened 2026-10-31 00:48:39 +00:00 by bsakaguchi · 1 comment
Owner

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

Summary

The Phase 1 library-function matcher proposed in #7196 validates and copies the complete function and signature inputs before applying the configured resource budgets.

As a result, max_functions, max_function_bytes, and max_signatures do not actually bound the work performed by the matcher. Oversized input can consume memory and CPU before being reported as unknown or rejected.

This currently affects the proposed domain prototype rather than a released production path, but it should be fixed before the matcher is merged or exposed through MCP.

Observed behavior

identifyLibraryFunctions first performs deep parsing of every function and signature:

const functions = rawFunctions.map((input) =>
  libraryFunctionInputSchema.parse(input),
);

const signatures = compileSignatures(
  rawSignatures.map((signature) =>
    libraryFunctionSignatureSchema.parse(signature),
  ),
  budgets.max_signatures,
);

The budget checks happen later, after the input has already been traversed and copied.

Relevant source: src/domain/native/libraryFunctionIdentification.ts.

Reproduction

Using the current implementation:

import { identifyLibraryFunctions } from "./src/domain/native/libraryFunctionIdentification.ts";

let functionReads = 0;
let byteReads = 0;

const bytes = new Proxy([0x90, 0x90], {
  get(target, key, receiver) {
    if (
      key === "length" ||
      (typeof key === "string" && /^\\d+$/.test(key))
    ) {
      byteReads++;
    }
    return Reflect.get(target, key, receiver);
  },
});

const functions = new Proxy(
  [
    { function_id: "f1", bytes: [0x90] },
    { function_id: "f2", bytes },
  ],
  {
    get(target, key, receiver) {
      if (typeof key === "string" && /^\\d+$/.test(key)) {
        functionReads++;
      }
      return Reflect.get(target, key, receiver);
    },
  },
);

const result = identifyLibraryFunctions(
  functions,
  [{ id: "nop", name: "nop", library: "libc", pattern: "90" }],
  { max_functions: 1, max_function_bytes: 1 },
);

console.log({ result, functionReads, byteReads });

The result reports the second function as budget-exceeded, but the instrumentation shows that the input was still traversed:

{
  "functionReads": 2,
  "byteReads": 6
}

The same ordering problem exists for the signature-set budget: all signatures are parsed before max_signatures is applied.

Expected behavior

Budgets should limit expensive parsing and representation expansion, not merely limit the later matching loop.

  • Functions beyond max_functions should be classified using only the minimum fields needed to report their function_id.
  • Function byte arrays beyond max_function_bytes should not be fully copied or traversed.
  • Signature sets beyond max_signatures should be rejected before parsing every signature.
  • Invalid input should still produce a clear validation error rather than being silently truncated.

The result should continue to preserve explicit unknowns, for example:

{
  "unknowns": [
    {
      "function_id": "f2",
      "reason": "function_budget_exceeded"
    }
  ]
}

Impact

If this matcher is exposed through MCP or provider integrations, caller-controlled oversized input could cause unnecessary allocation, parsing time, and memory pressure before the matcher reports the budget failure. That defeats the resource-boundary contract described by the Phase 1 proposal.

Suggested fix

Move budget enforcement to the raw-input boundary:

  1. Validate the budget object first.
  2. Check the function count before deep parsing.
  3. Validate the minimum identity fields needed to report over-budget functions.
  4. Inspect byte-array length before parsing all byte elements.
  5. Check the signature count before mapping and parsing the entire signature set.
  6. Add regression tests that verify both the returned unknowns and the absence of deep traversal beyond each configured budget.
Originally created by @amitpadhan525 on GitHub (Oct 26, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/7198 ## Summary The Phase 1 library-function matcher proposed in #7196 validates and copies the complete function and signature inputs before applying the configured resource budgets. As a result, `max_functions`, `max_function_bytes`, and `max_signatures` do not actually bound the work performed by the matcher. Oversized input can consume memory and CPU before being reported as unknown or rejected. This currently affects the proposed domain prototype rather than a released production path, but it should be fixed before the matcher is merged or exposed through MCP. ## Observed behavior `identifyLibraryFunctions` first performs deep parsing of every function and signature: ```ts const functions = rawFunctions.map((input) => libraryFunctionInputSchema.parse(input), ); const signatures = compileSignatures( rawSignatures.map((signature) => libraryFunctionSignatureSchema.parse(signature), ), budgets.max_signatures, ); ``` The budget checks happen later, after the input has already been traversed and copied. Relevant source: `src/domain/native/libraryFunctionIdentification.ts`. ## Reproduction Using the current implementation: ```ts import { identifyLibraryFunctions } from "./src/domain/native/libraryFunctionIdentification.ts"; let functionReads = 0; let byteReads = 0; const bytes = new Proxy([0x90, 0x90], { get(target, key, receiver) { if ( key === "length" || (typeof key === "string" && /^\\d+$/.test(key)) ) { byteReads++; } return Reflect.get(target, key, receiver); }, }); const functions = new Proxy( [ { function_id: "f1", bytes: [0x90] }, { function_id: "f2", bytes }, ], { get(target, key, receiver) { if (typeof key === "string" && /^\\d+$/.test(key)) { functionReads++; } return Reflect.get(target, key, receiver); }, }, ); const result = identifyLibraryFunctions( functions, [{ id: "nop", name: "nop", library: "libc", pattern: "90" }], { max_functions: 1, max_function_bytes: 1 }, ); console.log({ result, functionReads, byteReads }); ``` The result reports the second function as budget-exceeded, but the instrumentation shows that the input was still traversed: ```json { "functionReads": 2, "byteReads": 6 } ``` The same ordering problem exists for the signature-set budget: all signatures are parsed before `max_signatures` is applied. ## Expected behavior Budgets should limit expensive parsing and representation expansion, not merely limit the later matching loop. - Functions beyond `max_functions` should be classified using only the minimum fields needed to report their `function_id`. - Function byte arrays beyond `max_function_bytes` should not be fully copied or traversed. - Signature sets beyond `max_signatures` should be rejected before parsing every signature. - Invalid input should still produce a clear validation error rather than being silently truncated. The result should continue to preserve explicit unknowns, for example: ```json { "unknowns": [ { "function_id": "f2", "reason": "function_budget_exceeded" } ] } ``` ## Impact If this matcher is exposed through MCP or provider integrations, caller-controlled oversized input could cause unnecessary allocation, parsing time, and memory pressure before the matcher reports the budget failure. That defeats the resource-boundary contract described by the Phase 1 proposal. ## Suggested fix Move budget enforcement to the raw-input boundary: 1. Validate the budget object first. 2. Check the function count before deep parsing. 3. Validate the minimum identity fields needed to report over-budget functions. 4. Inspect byte-array length before parsing all byte elements. 5. Check the signature count before mapping and parsing the entire signature set. 6. Add regression tests that verify both the returned unknowns and the absence of deep traversal beyond each configured budget.
Author
Owner

@XinwangTao commented on GitHub (Oct 26, 2026):

同意,预算必须在 raw-input 边界生效,否则 "resource-boundary contract" 形同虚设——Proxy 计数复现很直观地证明了 functionReads/byteReads 在预算判定前就增长。补充两点:1) 第 4 点建议优先用 length 预检而非逐元素解析,因为 byte 数组可以惰性化(只保留引用,超限即按 function_budget_exceeded 上报,不需要完整复制);2) 对 max_signatures 也建议在 compileSignatures 之前按原始数组长度直接截断/拒绝,避免 parse 整批签名。回归测试用 Proxy 计数断言 "no deep traversal beyond budget" 的思路很好,建议顺带断言未知条目仍保留 function_id 字段,防止过度优化丢信息。

<!-- gh-comment-id:6411231730 --> @XinwangTao commented on GitHub (Oct 26, 2026): 同意,预算必须在 raw-input 边界生效,否则 "resource-boundary contract" 形同虚设——Proxy 计数复现很直观地证明了 functionReads/byteReads 在预算判定前就增长。补充两点:1) 第 4 点建议优先用 length 预检而非逐元素解析,因为 byte 数组可以惰性化(只保留引用,超限即按 function_budget_exceeded 上报,不需要完整复制);2) 对 max_signatures 也建议在 compileSignatures 之前按原始数组长度直接截断/拒绝,避免 parse 整批签名。回归测试用 Proxy 计数断言 "no deep traversal beyond budget" 的思路很好,建议顺带断言未知条目仍保留 function_id 字段,防止过度优化丢信息。
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#4298
No description provided.