Skip to content

Native kernel stack-overflows on deeply nested C/C++ files, killing the whole index process (SIGSEGV) #1581

Description

@apollo600

Summary

codegraph init / codegraph index dies with Segmentation fault (core dumped) — the entire CLI process, not just a parse worker — when the tree contains a C/C++ file with very deep brace nesting.

The crash is a native stack overflow in codegraph-kernel.node, hit inside a parse worker. Parse workers are created without resourceLimits, so they get Node's default 4 MB thread stack (the main thread gets 8 MB). A native stack overflow cannot be caught by the JS layer the way a WASM abort can, and because worker threads share the process, the overflow takes the whole codegraph process down — no error message, no per-file fallback, no partial index.

Repro'd on llvm/llvm-project (31,608 scanned files), where the offending file is clang/test/Parser/parser_overflow.c — a clang regression test that deliberately nests 16,384 {.

  • codegraph: v1.5.0 (npm bundle, lib/kernel/codegraph-kernel.node)
  • node: v24.18.0
  • platform: Linux 5.15.0 x86_64, 64 cores, 247 GB RAM

Reproduction

Self-contained, no llvm checkout needed. Any C file with ~10k nested braces does it:

python3 -c "d=16384; open('deep.c','w').write('void foo(void) {\n'+'{'*d+'}'*d+'\n}\n')"
codegraph init .
│  ◆ Scanning files — 1 found
│  · Parsing code  ████████████░░░░░░░░░░░░  50%
[1]    2903910 segmentation fault (core dumped)

Minimal direct repro against the kernel, with the stack size as the only variable:

// one.js — node one.js <file> [stackSizeMb]
const { Worker, isMainThread, workerData } = require('worker_threads');
const fs = require('fs');

if (isMainThread) {
  const stackMb = process.argv[3] ? parseFloat(process.argv[3]) : null;
  const w = new Worker(__filename, {
    workerData: { file: process.argv[2] },
    resourceLimits: stackMb ? { stackSizeMb: stackMb } : undefined,
  });
  w.on('exit', (code) => console.log(`stackMb=${stackMb ?? 'default(4)'} exitCode=${code}`));
} else {
  const LIB = '<...>/codegraph/versions/v1.5.0/lib/dist/extraction';
  const { detectLanguage } = require(LIB + '/grammars');
  const { tryKernelExtractRaw } = require(LIB + '/kernel');
  const content = fs.readFileSync(workerData.file, 'utf8');
  const r = tryKernelExtractRaw(workerData.file, content, detectLanguage(workerData.file, content));
  console.log('parsed ok, nodes =', r ? r.counts.nodes : 'deferred');
}
$ node one.js deep.c        # default 4 MB worker stack
Segmentation fault (core dumped)          # rc=139

$ node one.js deep.c 4      # rc=139
$ node one.js deep.c 6      # rc=139
$ node one.js deep.c 8      # parsed ok, nodes = 2   exitCode=0
$ node one.js deep.c 64     # parsed ok, nodes = 2   exitCode=0

Relevant code

  • dist/extraction/parse-pool.js:131this.createWorker = () => new Worker(scriptPath); — no resourceLimits, so the parse worker runs on Node's default 4 MB stack.
  • dist/extraction/parse-worker.js:90tryKernelExtractRaw(filePath, content, language), the call that faults.
  • dist/extraction/kernel/index.jsDEFAULT_ROUTED includes c / cpp (R7a, 2026-07-17), so C/C++ takes the kernel path by default.
  • dist/extraction/parse-worker.js:112-119 — the catch that handles WASM memory failures; unreachable for a native SIGSEGV.

Workarounds for anyone hitting this now

CODEGRAPH_KERNEL=0 codegraph index .     # WASM path, ~25% slower, parses the file correctly

or exclude the file in codegraph.json:

{ "exclude": ["clang/test/Parser/parser_overflow.c"] }

Any repo carrying parser stress-test fixtures is exposed — clang, gcc test suites, fuzzer corpora.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions