Skip to content

fix(evinse): stop atom on timeout or when cdxgen is killed, and read every reachables chunk - #4433

Merged
prabhu merged 10 commits into
masterfrom
fix/4419-atom-orphan-timeout
Oct 1, 2026
Merged

prabhu merged 10 commits into
masterfrom
fix/4419-atom-orphan-timeout

Conversation

@prabhu

@prabhu prabhu commented Oct 1, 2026 •

Copy link
Copy Markdown
Member

Fixes #4419: atom no longer survives cdxgen's timeout, and evinse stops dropping reachables flows.

Root cause. cdxgen spawns the atom dispatcher (node index.js) on every platform, so the runtime is a grandchild for the native and jar providers alike. The spawn timeout SIGKILLed only the dispatcher, and the runtime was reparented to init.

Changes

  • Passes ATOM_TIMEOUT, just inside cdxgen's own spawn timeout, so the dispatcher stops atom itself. This already works with the pinned atom 3.1.1. An explicit ATOM_TIMEOUT gives atom its own limit, independent of CDXGEN_TIMEOUT_MS.
  • Passes ATOM_PARENT_PID, so atom 4's dispatcher and runtime stop when cdxgen is killed.
  • Reports a timeout as a timeout, and removes the half-written atom so the next slice does not fail to load it.
  • Reads every reachables chunk (<base>_N.json). Only the first 1000 flows used to reach the BOM.
  • Skips truncated or malformed slices files with a warning instead of throwing.
  • Picks the heap option from the provider that is actually installed. A missing native package used to make every slice fail with Unknown option -XX:MaxHeapSize.
  • Bumps @appthreat/atom-parsetools to 1.8.0 (Stop a helper and its children when its supervisor is gone AppThreat/atom-parsetools#14), so the parsers atom starts stop with it.
  • Docs: ENV.md, EVINSE.md, TROUBLESHOOTING.md.

CodeQL alerts on master

  • alert 945 (js/tainted-format-string): the package.json path is logged as an argument, not as part of the format string.
  • alerts 947 and 948 (js/shell-command-constructed-from-input): getImage and exportImage now pass only well-formed image references to pull, save, inspect and the daemon API. No shell was involved, but an option such as -o<file> could take the image argument's place.

Related: AppThreat/atom#267, AppThreat/chen#189 (Python reachables hotspots and import resolution), AppThreat/atom-parsetools#14. The complete fix for a killed cdxgen arrives with the atom 4.0.0 bump.

Testing

  • New atomUtils, display and docker poku tests; the full poku suite passes (248 files).
  • Reproducers: before, with CDXGEN_TIMEOUT_MS=5000 evinse -l python --with-reachables on django-oscar, atom outlived cdxgen with PPID 1. After, nothing outlives a cdxgen timeout, on 3.1.1 or 4.0.0. With atom 4.0.0, nothing outlives a SIGKILLed cdxgen either.

prabhu added 7 commits October 1, 2026 10:58
The atom dispatcher falls back to the jar package when the platform's native
package is missing (optional dependencies skipped, a failed download), but
cdxgen chose the provider kind from the platform alone. On such an install
cdxgen passed -XX:MaxHeapSize as an atom argument and every slice failed with
'Unknown option -XX:MaxHeapSize'. The kind now follows what is installed
where cdxgen can see it: the native binary, else the jar package, else the
platform's kind. The sub-package lookup also covers pnpm's store layout.
cdxgen's spawn timeout killed only the atom dispatcher, so the runtime under
it (the native binary or the JVM) kept running, re-parented, with its heap
(#4419). cdxgen now:

- passes ATOM_TIMEOUT, just inside its own spawn timeout, so the dispatcher
  stops the runtime itself; an explicit ATOM_TIMEOUT sets atom's limit
  independently of CDXGEN_TIMEOUT_MS;
- passes ATOM_PARENT_PID, so atom 4's dispatcher and runtime stop when
  cdxgen is killed;
- reports a run that hit a time limit as a timeout, instead of a silent
  failure or a generic non-zero exit;
- removes the half-written atom a stopped run leaves, which the next slice
  of the same run would otherwise fail to load.
atom writes reachables at most 1000 flows per file (<base>.json,
<base>_1.json, ...), but evinse and printReachables read only the base file,
so every flow past the first thousand was dropped from the BOM. Chunks are now
read until the first gap, a chunk older than the base file (left by an earlier
run) ends the sequence, and stale chunks are removed before atom writes a new
set.
- A status-1 exit past atom's time limit counts as the legacy dispatcher
  stopping atom only when atom's output shows no failure of its own (an
  exhausted heap, a JVM crash, an atom 'Failure:'); any other non-zero
  status is atom's own exit.
- Slices files are read through readSlicesFile, which warns and skips a
  truncated or malformed file instead of throwing; a bad reachables chunk
  ends the sequence and keeps the flows read before it.
- The chunk scan tolerates a chunk vanishing between the existence check and
  its stat, and states the assumption behind the mtime slack.
- ENV.md: the SIGKILL follow-up is atom 4's, and cdxgen's last-resort kill
  comes 30 seconds after atom's limit or a quarter of the limit when shorter.
printReachables read .reachables off the result unguarded, so a truncated
slices file, which readReachablesSlices now reports and skips, still ended
the -p/--print path with a TypeError.
prabhu added 2 commits October 1, 2026 15:29
A path containing %s or %o was read as a format specifier. Addresses CodeQL js/tainted-format-string (code scanning alert 945).
… and daemon

getImage and exportImage handed their argument to `pull`, `save` and `inspect`
(or the daemon API) as given. Inputs that reach exportImage without the
isImageReference check, such as anything containing ":latest", could put an
option such as `-o<file>` where the image argument goes. Both entry points now
require the exact string to be an image reference. No shell is involved: the
CLI is spawned with an argument array. Addresses CodeQL
js/shell-command-constructed-from-input (code scanning alerts 947 and 948).
@prabhu
prabhu force-pushed the fix/4419-atom-orphan-timeout branch from 9f959eb to 8951893 Compare October 1, 2026 14:29
1.8.0 stops astgen, php-parse, rbastgen and scalasem, with the processes they started, once the process named by ATOM_PARENT_PID is gone.
@prabhu
prabhu merged commit 6881b4d into master Oct 1, 2026
83 checks passed
@prabhu
prabhu deleted the fix/4419-atom-orphan-timeout branch October 1, 2026 16:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai-auto A person set the task, and AI agents carried it through to completion bug Something isn't working evinse lang:python

Projects

None yet

Development

Successfully merging this pull request may close these issues.

cdxgen exits while the atom JVM keeps running (orphaned process at MaxRAMPercentage=90) on large repositories

1 participant