Skip to content

Default PATH is inconsistent across containment backends - the same config resolves different commands #1102

Description

@theelliotm

Relevant area(s)

Linux, macOS, Windows

Brief description of your issue

There is no cross-backend contract for what PATH a sandboxed workload receives. Every backend
invents its own answer, and the answer also changes depending on whether process.env happens to
be non-empty. The result is that an identical config resolves different commands - or fails to
resolve them at all - depending on the backend and the host distribution.

This table shows what PATH equals in each scenario:

Backend process.env omitted process.env set, no PATH=
Seatbelt (macOS) /usr/bin:/bin:/usr/sbin:/sbin (seatbelt_runner.rs:726) same default
LXC (Linux) inherits host PATH liblxc default /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
Bubblewrap (Linux) none none
Windows AppContainer user registry Path via CreateEnvironmentBlock(bInherit=FALSE) none
Windows BaseContainer inherits wxc-exec's env (lpEnvironment = NULL) none

Three distinct problems fall out of this:

1. Bubblewrap can leave PATH entirely unset. bwrap_command.rs:702 emits --clearenv
and never restores PATH. It is the only backend where a workload
can receive no PATH whatsoever in both columns. Command resolution then silently falls back to
whatever the host shell was compiled with, which is distro-dependent:

bash build DEFAULT_PATH_VALUE
Debian/Ubuntu (config-top.h:59) /usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin:
Fedora/RHEL (bash.spec:142) /usr/local/bin:/usr/bin

This is already failing in CI. Nightly run 33731889129 (main @ ad80e6b), Bubblewrap suite:
rhel-10 failed while debian-13, ubuntu-24.04 and ubuntu-26.04 passed, because an sbin
tool was unresolvable on RHEL only. Debian/Ubuntu are not passing because MXC is correct there -
they pass because they inherit a vendor fallback that happens to include sbin.

2. Adding one unrelated env variable silently changes PATH on 3 of 5 backends. Because the
posture is keyed on whether process.env is non-empty rather than on whether it contains PATH,
adding FOO=bar - a variable with nothing to do with PATH - changes command resolution:

  • LXC (lxc_bindings.rs:105): flips from KEEP_ENV to --clear-env, so PATH changes from
    the host's to liblxc's default.
  • Windows AppContainer / BaseContainer (appcontainer_runner.rs:1030-1041,
    base_container_runner.rs:96-106): flips from a full environment block to
    build_explicit_entries, which is pure passthrough — so PATH goes from present to absent.

A user adding a single environment variable has no reason to expect their commands to stop
resolving. On Windows this is a strict replace semantic with no floor under it.

3. None of this is documented. docs/schema.md:102 describes env in a single line
("Environment variables as KEY=VALUE") and says nothing about clearing, defaults, or per-backend
divergence.

Steps to reproduce

  1. Build MXC for the platform under test (./build.sh, ./build-mac.sh, or build.bat).
  2. Save a config that reports its own PATH — no process.env:
     "version": "0.8.0-alpha",
     "containerId": "path-probe",
     "containment": "bubblewrap",
     "process": {
       "commandLine": "bash -c 'echo \"PATH=[${PATH-<unset>}]\"; command -v iptables || echo NO_IPTABLES'"
     }
  1. Run it: ./src/target/release/lxc-exec --experimental path-probe.json 
  2. Add a single unrelated variable - "env": ["FOO=bar"] - and run again. Compare the reported PATH.
  3. Repeat both runs with containment set to lxc, and on macOS with seatbelt 
    (mxc-exec-mac) and on Windows with processContainer (wxc-exec.exe, using cmd /c echo %PATH%).
  4. Repeat step 2 on both a Debian/Ubuntu host and a RHEL 10 host.

Expected behavior

A single documented contract that holds on every backend.

Actual behavior

Five backends exhibit five different behaviors when process.env is omitted, and three of them
change behavior when an unrelated variable is added.

Activity

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

Metadata

Metadata

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions