Skip to content

[LXC] (3/5) Harden concurrency and cleanup - #1329

Open
Soham Das (SohamDas2021) wants to merge 1 commit into
mainfrom
sohamdas2021-1291-pr3
Open

Soham Das (SohamDas2021) wants to merge 1 commit into
mainfrom
sohamdas2021-1291-pr3

Conversation

@SohamDas2021

@SohamDas2021 Soham Das (SohamDas2021) commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

Stacked on #1326 (2/5). Review only the top commit; the rest is that PR.

PR 2 made two in-process LXC sandboxes able to live at once in one host process. Nothing in the backend was written for that, so this PR closes the two holes before the engine arm makes the path reachable.

The signal watchdog is the executor binary's alone

On a fatal signal the executor destroys the container it has on record. There is one record, so a second concurrent sandbox overwrote the first's — leaving that container and its firewall chains to leak. The watchdog also ends the process, which a library must not do to its caller.

Nothing registers now unless the executor armed the watchdog. lxc-exec arms it before it does anything else, so the binary behaves exactly as before. A library host arms nothing, and there each sandbox handle already tears itself down.

Bubblewrap runs through this same code and never registers a container, so it is unaffected. A test pins that.

A container a live sandbox holds is refused, not taken

A container applies its network policy only when it starts. So a second sandbox naming one that is already running would restart it to apply its own policy — killing the first sandbox's workload. Handles are bound to a container name rather than to an instance, so dropping one handle could stop another's container.

The second sandbox is now refused, and told which container it collided with and which field to change. The name frees as soon as the first sandbox finishes or its handle drops. Generated names retry on collision. Excluding sandboxes in other processes is out of scope and documented as a limitation.

Validation

fmt, clippy on the host, x86_64-unknown-linux-gnu, and aarch64-apple-darwin, plus bwrap_common and the lxc binaries. 377 tests on Windows and 398 on Linux, the latter through the exact release command #1326 added to CI. The three behaviors this PR adds were each confirmed by mutation — reverting a guard fails its own test and no other.

Still unreachable: the engine arm lands in 4/5.

@SohamDas2021
Soham Das (SohamDas2021) requested a review from a team as a code owner September 29, 2026 21:37
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
There may be pipelines that require an authorized user to comment /azp run to run.

@SohamDas2021
Soham Das (SohamDas2021) added this pull request to stack #1327 September 29, 2026 21:37
@SohamDas2021 Soham Das (SohamDas2021) changed the title [LXC] (3/5) Confine the signal watchdog to the executor binary and refuse a container a live sandbox holds [LXC] (3/5) Harden concurrency Sep 29, 2026
@SohamDas2021 Soham Das (SohamDas2021) changed the title [LXC] (3/5) Harden concurrency [LXC] (3/5) Harden concurrency and cleanup Sep 29, 2026
Comment thread src/backends/lxc/common/src/lxc_runner.rs Fixed
Comment thread src/backends/lxc/common/src/lxc_runner.rs Fixed
Copilot AI balanced review requested due to automatic review settings September 29, 2026 23:01

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.

Copilot review overview

🟡 Changes recommended

A successful non-consuming wait() retains the container-name claim and incorrectly blocks subsequent sandboxes.

Review effort: Balanced
Findings: 1 Medium severity

Open (1)
What changed in this PR

Hardens LXC concurrency by isolating executor-only signal cleanup and preventing concurrent reuse of container names.

Changes:

  • Gates watchdog registration to lxc-exec.
  • Adds process-local container-name claims with collision retries.
  • Adds regression tests for cleanup and collision behavior.
File Description
signal_cleanup.rs Restricts watchdog state publication to installed executors.
lxc_runner.rs Adds container-name ownership and concurrency tests.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +895 to +897
/// Keeps this sandbox's container out of reach of a second one in this
/// process.
_name_claim: ContainerNameClaim,
Base automatically changed from sohamdas2021-1291-pr2 to main September 30, 2026 21:28
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 959d4b60-0a77-4533-b88a-20a5be80703f
Copilot AI balanced review requested due to automatic review settings September 30, 2026 21:41

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.

Copilot review overview

🟡 Changes recommended

Successful waits retain name claims, and background descendants can still lose firewall enforcement during teardown.

Review effort: Balanced
Findings: 1 High severity · 1 Medium severity

Open (2)

Comment on lines 774 to +777
start_directory: start_directory(request).unwrap_or_default(),
exec_env,
timeout,
_name_claim: name_claim,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Confirmed, and pre-existing on the shared tear_down that lxc-exec also uses, the fix lands in (4/5) with the engine arm that makes the path reachable.

Comment on lines +153 to +157
struct ContainerNameClaim {
name: String,
}

impl ContainerNameClaim {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

question: I'm slightly confused on what this is for/ it get us tbh. Is this something to do with already running containers lxc created containers and their names?

})
}

fn claim_generated_name(&self) -> Result<ContainerNameClaim, ScriptResponse> {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

note: same about this function, if lxc is creating containers and we're just a wrapper for it, what's the generated name stuff for? I assumed it was simple and we're just calling the lxc executables, but seems like its more complex than I thought?

is this about two containers not being allowed the same name?

if let Some(overridden) = WATCHDOG_OVERRIDE.with(std::cell::Cell::get) {
return overridden;
}
WATCHDOG_INSTALLED.load(Ordering::Acquire)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

note: slightly confused on the watchdog usecase tbh.

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.

4 participants