Skip to content

fix(pipeline): prevent stack-buffer-overflow in append_args_json#475

Open
rainder wants to merge 1 commit into
DeusData:mainfrom
rainder:fix/args-json-stack-overflow
Open

fix(pipeline): prevent stack-buffer-overflow in append_args_json#475
rainder wants to merge 1 commit into
DeusData:mainfrom
rainder:fix/args-json-stack-overflow

Conversation

@rainder

@rainder rainder commented Jun 16, 2026

Copy link
Copy Markdown

Summary

Fixes the stack-buffer-overflow — the SIGABRT that crashes the MCP server when index_repository runs over a large TypeScript codebase.

Root cause: format_call_arg() returns snprintf's untruncated length. append_args_json() did pos += (size_t)n, so a call carrying enough long arguments pushed pos past the fixed CBM_SZ_2K props stack buffer in emit_normal_calls_edge(); the trailing buf[pos] = '\0' (and buf[pos++] = ']') then wrote out of bounds. The stack canary caught it as an abort, so full-repo indexing died in the parallel resolve pass.

AddressSanitizer pinpoint:

ERROR: AddressSanitizer: stack-buffer-overflow
WRITE of size 1
  #0 append_args_json   pass_parallel.c:1124
  #1 finalize_and_emit  pass_parallel.c:1183
  #2 emit_normal_calls_edge pass_parallel.c:1266
'props' (line 1261) [2048 B] <== access overflows this variable

Fix

When an argument does not fully fit, roll back to before its separator and stop appending — atomic field, matching append_json_string()'s existing behaviour — so pos can never advance past the buffer. No new system/popen/fork/network calls (no security-allowlist.txt change needed).

Test

parallel_args_json_no_overflow (tests/test_parallel.c) indexes a fixture whose single call carries 60 long string args (args JSON well past 2 KB). Aborts under the ASan test build without this fix; passes with it.

Verification

  • make test (ASan + UBSan): 5605 passed, 0 sanitizer errors
  • ASan cli index_repository on the originally-crashing repo: clean, completes
  • Prod build (-O2, mimalloc, stack-protector): fresh full index exits 0 (was SIGABRT 134)
  • clang-format clean; change is +58/−2 across 2 files

Out of scope: the sibling pass_definitions.c appender was checked and does not share this pattern.

🤖 Generated with Claude Code

A call carrying enough long arguments drove append_args_json()'s running
position past the fixed CBM_SZ_2K `props` stack buffer in
emit_normal_calls_edge(): format_call_arg() returns snprintf's *untruncated*
length, so `pos += (size_t)n` could exceed `bufsize`, after which the
trailing `buf[pos] = '\0'` (and `buf[pos++] = ']'`) wrote out of bounds. The
stack canary caught it as SIGABRT, so full-repo indexing of large TypeScript
codebases crashed the server in the parallel resolve pass
(emit_service_edge -> emit_normal_calls_edge -> finalize_and_emit ->
append_args_json). Confirmed with AddressSanitizer:
stack-buffer-overflow WRITE at pass_parallel.c:1124, 'props' (2048 B).

Fix: when an argument does not fully fit, roll back to before its separator
and stop appending (atomic field, matching append_json_string's behaviour),
so `pos` can never advance past the buffer.

Add regression test parallel_args_json_no_overflow: indexes a fixture whose
single call carries 60 long string args (args JSON well past 2 KB); under the
ASan test build it aborts without this fix and passes with it.

Signed-off-by: Andrius Skerla <1492322+rainder@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant