Skip to content

Cached tasks cannot spawn child processes inside a rootless bubblewrap sandbox (EPERM) #700

Description

@lifeiscontent

Summary

Inside a rootless bubblewrap sandbox, a cache-enabled task cannot spawn child processes. Every spawn/spawnSync/execFile fails with EPERM before the child runs. Setting cache: false on the same task, with nothing else changed, makes it work.

The path is absolute and executable in both cases, so this is not PATH resolution.

Reproduction

Sandbox is entered as:

bwrap --die-with-parent --new-session \
  --unshare-user --unshare-pid --unshare-ipc --unshare-uts --unshare-cgroup --unshare-net \
  --cap-drop ALL \
  --clearenv --setenv PATH /runtime/bin \
  --ro-bind <toolchain-image> /runtime \
  --tmpfs /tmp --dev /dev --proc /proc \
  --bind <checkout> /workspace \
  -- /runtime/bin/sh -c 'cd /workspace && vp run -r test'

There is no /usr/bin, /bin or /usr/lib in the sandbox; everything is under /runtime.

Any cached task whose command spawns a child reproduces it:

execFileSync("/tmp/shell/sh", ["-n"], { input: script });
task config spawn
{ command: 'vp test' } EPERM
{ command: 'vp test', cache: false } works

In one vp run -r test over a workspace, the packages I had flipped to cache: false spawned fine while the packages still cached failed in the same run, same sandbox, same commit. Flipping two packages took the spawn failures from 74 to 0.

Observed

Error: spawnSync /tmp/shell/sh EPERM
Error: spawn EPERM

Nothing is printed by the child. git subprocesses fail the same way one level down:

fatal: cannot exec 'git-receive-pack': Operation not permitted

Environment

  • vite-plus 0.3.0
  • Linux x86_64, glibc
  • rootless bubblewrap, seccomp, unprivileged user namespaces

Notes

Not #569/#576 — 0.3.0 has both. LD_PRELOAD is unset going in, so not #340.

Guess: fspy_preload_unix has to inject into each child, and this sandbox is --cap-drop ALL with /usr/lib unmounted, so either the preload object is unreachable in the child's mount namespace or something it does on init is denied — and the exec is refused instead of tracking degrading.

Falling back to caching without file tracking would be better than failing the spawn. Today the only workaround is disabling the cache for the whole package.

Happy to run patches or a debug build against the sandbox.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions