Skip to content

[Bubblewrap] Fork bwrap from a process-lifetime thread - #1526

Open
Keith Mattix II (keithmattix) wants to merge 2 commits into
microsoft:mainfrom
keithmattix:fix/bwrap-process-lifetime-spawner
Open

Keith Mattix II (keithmattix) wants to merge 2 commits into
microsoft:mainfrom
keithmattix:fix/bwrap-process-lifetime-spawner

Conversation

@keithmattix

@keithmattix Keith Mattix II (keithmattix) commented Oct 19, 2026 •

Copy link
Copy Markdown

📖 Description

bwrap --die-with-parent arms PR_SET_PDEATHSIG in the launcher, and Linux delivers it when the thread that forked bwrap exits, not the process. v1::spawn forked bwrap on the caller's thread, so a sandbox started from a pooled or short-lived thread (Tokio spawn_blocking, a .NET thread-pool thread, a scoped worker) and waited on elsewhere was SIGKILLed when that thread was retired, surfacing as Exited(-1) with truncated output. The proxy (runtimeConfig.networkProxy) path hit it deterministically because spawn blocks until bwrap has armed the signal.

Route the bwrap spawn through a dedicated thread that lives for the process lifetime, so the sandbox still dies with the embedding process but no longer with an arbitrary caller thread. Adds a regression test that spawns on a thread which exits while the workload is still running.

🔗 References

Resolves #1525

🔍 Validation

Added automated test coverage and also tested manually by enabling network proxy.

✅ Checklist

📋 Issue Type

  • Bug fix
  • Feature
  • Task

GitHub Actions runs the PR validation build automatically. The ADO pipeline
(MXC-PR-Build) is the Azure version of the PR pipeline, kept in parity with the GitHub
Actions build; it runs on merge to main, and Microsoft reviewers with write access can trigger it
on a PR with /azp run. See pull request builds.

If the dependency-feed-check check fails on a new dependency, the crate must be added to
the feed before the PR can pass. See pull request builds
for the steps.

Microsoft Reviewers: Open in CodeFlow

`bwrap --die-with-parent` arms PR_SET_PDEATHSIG in the launcher, and Linux
delivers it when the *thread* that forked bwrap exits, not the process.
`v1::spawn` forked bwrap on the caller's thread, so a sandbox started from a
pooled or short-lived thread (Tokio `spawn_blocking`, a .NET thread-pool
thread, a scoped worker) and waited on elsewhere was SIGKILLed when that
thread was retired, surfacing as `Exited(-1)` with truncated output. The
proxy (`runtimeConfig.networkProxy`) path hit it deterministically because
spawn blocks until bwrap has armed the signal.

Route the bwrap spawn through a dedicated thread that lives for the process
lifetime, so the sandbox still dies with the embedding process but no longer
with an arbitrary caller thread. Adds a regression test that spawns on a
thread which exits while the workload is still running.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings October 19, 2026 15:32
@keithmattix

Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree company="Solo.io"

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The permanent native thread needs human validation of embedding and shared-library lifetime behavior across SDKs.

No open issues 🎉

What changed in this PR

Fixes #1525 by launching Bubblewrap from a process-lifetime thread, preventing sandbox termination when the caller’s thread exits.

Changes:

  • Routes launches through a dedicated thread while preserving synchronous results.
  • Adds launch and caller-thread-exit regression tests.
  • Documents the parent-death signal behavior.
File Description
src/​mxc-sdk/​tests/​streaming_bubblewrap.rs Tests survival after the spawning thread exits.
src/​mxc-sdk/​src/​backends/​bubblewrap/​common/​mod.rs Registers the Linux-only spawner module.
src/​mxc-sdk/​src/​backends/​bubblewrap/​common/​lifetime_spawner.rs Implements the dedicated launch thread and unit tests.
src/​mxc-sdk/​src/​backends/​bubblewrap/​common/​bwrap_runner.rs Routes Bubblewrap launches through the spawner.
docs/​backends/​bwrap/​bubblewrap-backend.md Explains thread-independent sandbox lifetime.

Review effort: Balanced

bwrap --die-with-parent ties a sandbox to the thread that forked bwrap,
which the Rust SDK fixed by spawning from a process-lifetime thread.
Pin the same behavior in the other SDKs:

- .NET: MxcContainer.Spawn on a thread that exits while the workload
  runs. It fails against the native library without the fix.
- Node: the public spawn from a worker_threads Worker that is terminated
  while the workload runs. Node's spawn runs on the libuv pool, so this
  guards against moving it onto the calling thread.

Both gate on a usable Bubblewrap; MXC_BWRAP_TESTS_REQUIRE_EXECUTION turns
the skip into a failure, as for the Rust suites.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings October 19, 2026 17:37

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Sandbox parent-death signaling and cross-runtime teardown warrant final human review with Linux host validation.

No open issues 🎉

Review effort: Balanced

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bubblewrap sandbox can die when its spawning thread exits

2 participants