Repository navigation
browser: stopping an attached session leaves its owned Chromium tab running #1107
Description
Activity
Bounded follow-up: supported launched sessions can provide per-task physical teardown
This does not close the attached-owned-tab bug reported above. I tested the alternative supported lifetime (
browser::sessions::startwith an ephemeral profile, thenbrowser::sessions::stop) against the unchanged official browser 0.2.12. No worker patch, UI audit, live site, credentials, or manual tab cleanup was used.Fixture and evidence
- Browser source
deb6f98dbd3c7cbc226234844761430a47b911cf; binary SHA-25690dc8a3dd2f212d4eb9971d67b5426064ce4bc7881c0bb5d3f131b47b5282219. - Chromium: Google Chrome for Testing 145.0.7632.6, executable SHA-256
481fea1516a1f2b76454664272f12cd9dd1f20117b21e1f1498e08bc7f872c00. - Playwright image digest
sha256:65cefd09a5e943921ecd3a6e5414c603db2eb161e9eb48f2e2ccc63486dc7dc0, bounded networkless runsc, 2 CPU / 3 GiB / 512 PIDs, read-only root, disposable tmpfs, non-root. - Real Engine and its configuration owner; browser's documented
--configseed.allow_attach=false, emptyuser_data_dir, two launched read-only sessions; each local sentinel page makes a counted HTTP request every 60 ms to an in-container loopback server. A sealedexecwrapper adds Chromium's container flags; it does not implement cleanup. - Standard Docker
--initwith PID1/sbin/docker-init, SHA-256cc5aa4571d1ec88f939a98b2242dfd8d7e28a51f0840886c11e65441e25cd460.
13/13 identity/lifetime assertions passed in a 9.77 s fixture. Specifically:
- Two sessions owned separate Chromium processes and ephemeral profiles.
sessions::stop(b1)returned{ok:true, was_running:true}in 269 ms.- Before any fixture shutdown, b1's polling stayed at 69 requests while b2 continued from 21 to 28.
- b1's ephemeral profile was physically absent and every captured PID + process-start identity was absent. This was not merely removal from
sessions::list. sessions::listcontained only b2; b2's process remained alive.- Repeating supported stop on b1 returned
{ok:true, was_running:false}. - Send the browser worker its supported SIGTERM: it exited code 0, b2 polling stopped, b2 profile disappeared, and all captured Chromium identities were absent. The other Engine/fixture processes were still alive during these assertions.
- A final process-table check found no Chromium descendants, before container disposal.
The container and sealed source were then removed by exact identity as fixture housekeeping. Their removal did not substitute for the native lifetime assertions.
Important envelope control
Without a PID1 reaper, a preceding run stopped polling and deleted the profile but left terminated Chromium descendants as zombies reparented to PID1. They were not still executing browser activity, but physical PID-absence failed. Adding the documented
--initreaper fixed that inspection/runtime envelope; the browser binary was unchanged. This distinguishes process reaping from the original still-active attached-tab leak. See Docker's PID1 guidance.Two earlier setup attempts failed before launching any page: the configuration seed was not selected, then the launcher was placed on a noexec tmpfs. Neither is lifecycle evidence. The successful fixture uses the real configuration owner and a sealed read-only executable launcher.
Scope and upstream regression proposal
This qualifies launched, per-task sessions with an actual reaper, supported stop, and worker SIGTERM in the tested envelope. It does not qualify adoption/attachment, idle sweeping, worker SIGKILL, host failure, Cardflow integration, or an immutable deployment. Our deployment/autonomy gates remain held for the separate Harness issue.
For the original attached-tab branch, please retain regression cases that assert physical CDP target absence AND cessation of page activity after stop; a logically empty session table is insufficient. An adopted caller-owned tab must remain untouched. Source still aborts event/driver tasks before attempting page close; a candidate fix should keep the CDP driver alive through the owned-target close, bound that operation, and report failure rather than silently claiming full teardown. That is a source-level repair proposal, not a tested patch or a downstream fork.
For our integration, the next candidate can use the supported launched-per-task lifetime instead of shared attached browsers, retaining exact task ownership and process reaping. No permanent packaging or live cutover has been performed.
- Browser source
Observed defect
With the unmodified
browser/v0.2.12Linux x86_64 release,browser::sessions::stopremoves an attached session frombrowser::sessions::listbut does not close the new tab that the session created (adopted: false). The page remains in the external Chromium CDP target list and its JavaScript continues running. Repeated attach/stop cycles accumulate live polling tabs.Test environment: Linux, Chromium 145.0.7632.6, private CDP connection to a separately owned browser. No browser-worker source changes. In an integration rehearsal, eight logically stopped sessions left four active application tabs in each of two independent browsers. Exact owned-target closure through CDP removed the extra traffic; shutting down the two dedicated browser processes removed all remaining resources. No adopted user tab was closed.
Minimal reproduction
about:blanktab).browser::sessions::attachwith that browser'scdp_url, an allowed test page URL andread_only: false. Verify the response saysadopted: falseand record the newly created target.browser::sessions::stopwith the returned session ID.browser::sessions::listis empty, then independently inspect the same browser's CDP target inventory.Expected: a fresh tab owned by an attached session is closed; pre-existing/adopted tabs and the external browser remain untouched. Logical session deletion must not be the only cleanup assertion.
Likely source cause (not a tested patch)
The same ordering is present in release source
deb6f98dbd3c7cbc226234844761430a47b911cfand current main00417f34dcee7465d5d4602f4ab497db106f3e55:browser/src/session.rs:668–675:shutdownaborts every task inself.tasks.browser/src/session.rs:692–697: only afterward,SessionKind::Attached { owns_page: true }awaitsself.page.clone().close(). Failure is logged at debug level and not surfaced as an unsuccessful cleanup.This appears to stop the protocol driver before asking it to close its owned target. Launched-browser shutdown has a similar protocol-ordering concern but was not qualified by this attach-mode reproduction.
Suggested fix/acceptance boundary
Keep the protocol handler alive until bounded page/browser teardown completes, then stop and await owned background tasks. Preserve the distinction between fresh owned tabs, adopted tabs and launched browsers; never close someone else's tab as a fallback. Add real-CDP regression tests asserting both logical session removal and physical target/process cleanup, including an unresponsive-browser timeout case. Report cleanup failure rather than treating a removed session-map entry as proof that the page stopped.