Skip to content

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

Merged
DeusData merged 1 commit into
DeusData:mainfrom
rainder:fix/args-json-stack-overflow
Jun 22, 2026
Merged

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

Conversation

@rainder

@rainder rainder commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

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>
@DeusData DeusData merged commit ff89931 into DeusData:main Jun 22, 2026
13 checks passed
@studyzy studyzy mentioned this pull request Jun 23, 2026
5 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

2 participants