Remote SSH: every sandboxed agent shell command leaks 2 orphan cursorsandbox processes (7 threads each) → user cgroup pids.max exhausted, host unusable

Where does the bug appear (feature/product)?

Cursor IDE

Describe the Bug

On a Remote SSH host, each shell command the agent runs inside the sandbox leaves two cursorsandbox helper processes behind after the command finishes:

cursorsandbox --run-proxy --policy-json {…}

cursorsandbox --policy ~/.cursor/sandbox-policies/sandbox-policy- – /bin/bash -O extglob -c …

Both are immediately reparented to PID 1, each has 7 threads, and they never exit. Since the kernel counts threads toward pids.max, every sandboxed command permanently consumes 14 pids in user-.slice. Over a few days of normal agent use the slice reaches pids.max (20634 on Ubuntu 24.04 default), after which every fork() for that user fails (Resource temporarily unavailable), including SSH login (client_loop: send disconnect: Connection reset). The only recovery was rebooting the VPS. This happened to us twice (2026-09-09 and 2026-10-03). A monitor cron recorded 1,462 orphan cursorsandbox processes on 2026-09-18.

Commands run with the sandbox disabled for that call (e.g. required_permissions: [“all”]) do not leak.

Steps to Reproduce

  1. Connect to a Linux host via Remote SSH (Ubuntu 24.04, kernel 6.8, CURSOR_SANDBOX_LANDLOCK_STATUS=fully_enforced, AppArmor profile cursor-sandbox-remote present).
  2. On the host: pgrep -c cursorsandbox → N.
  3. Ask the agent to run any trivial sandboxed shell command, e.g. echo hi. Wait for it to finish.
  4. pgrep -c cursorsandbox → N + 2. ps -u $USER -o pid,ppid,sid,nlwp,etimes,args | grep cursorsandbox shows both new processes with ppid=1, nlwp=7, same SID, and the finished command is gone.
  5. Repeat; the count grows by 2 per command and never decreases. cat /sys/fs/cgroup/user.slice/user-$(id -u).slice/pids.current grows by 14 per command.

Sample from step 4 (two commands → 4 orphans; the live /bin/bash on the last line is the running command and has a different SID):

  PID    PPID   SID    NLWP ELAPSED ARGS
  93379     1   93378    7      28 cursorsandbox --run-proxy --policy-json {"allow":["public.ecr.aws", ...
  93432     1   93378    7      28 cursorsandbox --policy /home/ubuntu/.cursor/sandbox-policies/sandbox-policy-9a4063ca25bf3bae -- /bin/bash -O extglob -c ...
  93885     1   93884    7      13 cursorsandbox --run-proxy --policy-json {"allow":["public.ecr.aws", ...
  93936     1   93884    7      13 cursorsandbox --policy /home/ubuntu/.cursor/sandbox-policies/sandbox-policy-382f08c1343ea146 -- /bin/bash -O extglob -c ...
  94499  5406   94499    1       0 /bin/bash -O extglob -c snap=$(command cat <&3) && ...

Expected Behavior

The proxy and policy helper processes exit when the sandboxed command exits (or are reaped by cursor-server), so the pid count returns to N.

Operating System

Windows 10/11

Version Information

cursor-server 3.22.12, commit 3a92974361033b2051526321308c2740fe5912c0, build 2026-09-26T04:55:12Z (~/.cursor-server/bin/linux-x64/<commit>/product.json). Also observed on earlier builds present on the host (the leak started at least by 2026-09-09).

For AI issues: which model did you use?

not model-related; reproduces with any model and any sandboxed command.

Additional Information

  • Related threads (local-host variants of the same leak): Critical Governance Issue: cursor_sandbox and Seccomp blocking Root/Sudo kill signals, Sandbox processes persist after Cursor exit. This report adds the Remote SSH case and the quantitative impact (14 pids per command → pids.max exhaustion → host lockout).
  • On this host the orphans can be killed by the owning user with SIGTERM (no AppArmor denial), so our workaround is a cron job every 10 min that kills sessions containing only cursorsandbox processes older than 10 min. Session-based detection is needed because the live command and its helpers are all reparented to PID 1 immediately, so ppid cannot distinguish in-use helpers from leaked ones.
  • Happy to provide ~/.cursor-server/data/logs/… or run a trace build.

Does this stop you from using Cursor

Yes - Cursor is unusable

Hey @MASAODES, thanks for the report and the numbers. This is a known issue we’re already tracking, and I’ve added your report to it.

What’s happening: on hosts with the Cursor AppArmor profile, the two helper processes rely on a signal from the finished command to shut down, and the profile currently blocks that signal between sandboxed processes. Your session-based cron is a sound way to clean them up in the meantime.

If you’d rather stop the leak at the source, adding one line to the profile on the remote host should let the helpers exit on their own:

  1. Open /etc/apparmor.d/cursor-sandbox-remote with sudo.
  2. In the profile cursor_sandbox_remote block, under signal (receive) peer=unconfined,, add:
    signal (send, receive) peer=cursor_sandbox_remote,
    
  3. Reload it: sudo apparmor_parser -r /etc/apparmor.d/cursor-sandbox-remote
  4. Kill the existing orphans once, then run a sandboxed command and check that pgrep -c cursorsandbox returns to the same number afterwards.

A future update of the AppArmor package may ask about the modified file; that’s expected. Let me know if the count still grows after the change.

Thanks @Colin, that was exactly it.

Before the change, the kernel log showed a pair of denials for every sandboxed command:
apparmor=“DENIED” operation=“signal” profile=“cursor_sandbox_remote” signal=kill peer=“cursor_sandbox_remote” (one send, one receive)

After adding signal (send, receive) peer=cursor_sandbox_remote, to the cursor_sandbox_remote block and reloading, I cleaned up the existing orphans once. Since then, many sandboxed commands have run and pgrep -c cursorsandbox has stayed at 0, with no more signal denials in the log.

FYI: cursor-sandbox-apparmor 0.6.2 (currently in the apt repo) still lacks this rule, so I’m re-applying it with a small daily script until the fix ships in the package. Looking forward to the official fix.