Skip to content

jscpd grandchild leaks at 100% CPU when subprocess timeout fires #601

@cryptiklemur

Description

@cryptiklemur

desloppify/engine/detectors/jscpd_adapter.py runs jscpd via:

subprocess.run(
    [npx, "--yes", "jscpd", ...],
    timeout=120, check=True,
)

When the 120s timeout fires, subprocess.run only kills the direct child (npx); the node jscpd grandchild gets reparented to systemd --user and keeps running at ~100% CPU forever. On my box I ended up with 4 orphaned jscpd procs running 8h+ each (~35 cpu-hours wasted) before noticing.

Fix

Spawn with start_new_session=True (or process_group=0 on 3.11+) and on TimeoutExpired send SIGKILL to the whole process group:

proc = subprocess.Popen(argv, ..., start_new_session=True)
try:
    out, err = proc.communicate(timeout=120)
except subprocess.TimeoutExpired:
    os.killpg(os.getpgid(proc.pid), signal.SIGKILL)
    proc.communicate(timeout=10)
    raise

Repro

Point desloppify at any tree large enough that jscpd takes >120s. Watch pgrep -af jscpd after the scan returns — the jscpd node process is still there at 99% CPU, parented to systemd --user.

Env

  • desloppify 0.9.15
  • python 3.14
  • jscpd via npx --yes jscpd
  • linux 6.19, systemd 260

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