Skip to content

[Bubblewrap] Restrict socket address families and io_uring with an always-on seccomp filter - #1529

Open
Soham Das (SohamDas2021) wants to merge 1 commit into
mainfrom
sohamdas2021-bubblewrap-af-vsock-filter
Open

Soham Das (SohamDas2021) wants to merge 1 commit into
mainfrom
sohamdas2021-bubblewrap-af-vsock-filter

Conversation

@SohamDas2021

@SohamDas2021 Soham Das (SohamDas2021) commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

📖 Description

Sandboxed workloads could open AF_VSOCK sockets. The network namespace does not isolate VSOCK, so a workload could reach host or hypervisor services despite its egress policy. AF_PACKET and other unusual socket families were also unrestricted.

Every Bubblewrap launch now installs a seccomp filter:

  • socket() and socketpair() succeed only for Unix-domain, IPv4, IPv6, and netlink sockets. Every other family, including VSOCK, packet, and future families, fails with EPERM.
  • io_uring (io_uring_setup, io_uring_enter, io_uring_register) fails with ENOSYS. Without this, a workload could bypass the family check with io_uring's IORING_OP_SOCKET. Programs that probe for io_uring fall back to ordinary syscalls.
  • x86-64 hosts keep x32 and i386 programs working. AArch64 hosts keep AArch32 programs working.
  • If the filter cannot be created or passed to Bubblewrap, the launch fails instead of running unfiltered.
  • On Linux architectures the filter does not support, Bubblewrap is now reported as unavailable instead of failing at launch.

Limitations

  • Legacy i386 socketcall passes the family through a pointer that seccomp cannot inspect, so its socket-creation subcalls always fail with EPERM. Other socketcall operations still work.
  • A workload that requires io_uring and has no fallback will no longer run under Bubblewrap.

🔗 References

🔍 Validation

  • cargo fmt --all -- --check: passed
  • cargo test -p mxc-sdk --lib seccomp_filter: 17 passed. These tests run the generated filter through a small test interpreter on every supported ABI.
  • cargo test -p mxc-sdk --lib bwrap_version: passed
  • cargo clippy -p mxc-sdk --lib --tests --target x86_64-unknown-linux-gnu -- -D warnings: passed
  • cargo clippy -p mxc-sdk --lib --target aarch64-unknown-linux-gnu -- -D warnings: passed
  • cargo test -p mxc-sdk --test wxc_e2e_tests_e2e_bubblewrap_characterization (WSL, kernel 6.18): 15 passed. New tests:
    • An unassigned family fails with EPERM from both socket() and socketpair(). This test runs on every host.
    • AF_VSOCK fails with EPERM. This test skips if the host itself cannot create AF_VSOCK.
    • io_uring_setup fails with ENOSYS.
    • Creating AF_UNIX and AF_INET sockets still works.
  • Mutation check: with filter installation disabled, all three new denial tests fail.
  • io_uring bypass probe in WSL: before this change, IORING_OP_SOCKET returned a working AF_VSOCK socket inside the sandbox. It now fails with ENOSYS.
  • Bubblewrap test scripts (WSL, run unprivileged): basic, environment, readonly_denial, version_gate, teardown, provider_loss, network, network_proxy, firewall, dns, directional, localnet, linux_process_default all passed.
  • Not run:
    • The filesystem scripts that need sudo, and the root-only inbound deny test.
    • Native runs on AArch64, and with real i386, x32, or AArch32 binaries. These paths are covered only by the interpreter tests.

✅ 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

…ways-on seccomp filter

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 6e313a8c-149b-4c27-b3f7-06592ff192cd
Copilot AI balanced review requested due to automatic review settings October 19, 2026 18:22
@SohamDas2021
Soham Das (SohamDas2021) requested a review from a team as a code owner October 19, 2026 18:22
@azure-pipelines

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

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.

🟡 Changes recommended

The filter rejects valid allowed-family socket calls with nonzero unused upper argument bits; compatibility paths also warrant human validation.

1 open issue
What changed in this PR

Adds an always-on Bubblewrap seccomp filter to block socket families that can bypass network isolation.

Changes:

  • Restricts socket families and disables io_uring.
  • Fails closed during filter setup and rejects unsupported architectures.
  • Adds filter tests and documents compatibility limitations.
File Description
src/​mxc-sdk/​tests/​wxc_e2e_tests_e2e_bubblewrap_characterization.rs Adds socket-family and io_uring integration checks.
src/​mxc-sdk/​src/​backends/​bubblewrap/​common/​seccomp_filter.rs Implements filter generation and interpreter tests.
src/​mxc-sdk/​src/​backends/​bubblewrap/​common/​proxy_network.rs Exposes descriptor inheritance for filter delivery.
src/​mxc-sdk/​src/​backends/​bubblewrap/​common/​mod.rs Registers the filter module.
src/​mxc-sdk/​src/​backends/​bubblewrap/​common/​bwrap_version.rs Reports unsupported architectures as unavailable.
src/​mxc-sdk/​src/​backends/​bubblewrap/​common/​bwrap_runner.rs Installs the filter during launch.
docs/​backends/​bwrap/​bubblewrap-backend.md Documents restrictions and compatibility.

Review effort: Balanced

Comment on lines +361 to +368
program.push(Instruction::statement(
BPF_LD_W_ABS,
SECCOMP_DATA_ARG0_HIGH_OFFSET,
));
program.push(Instruction::jump(BPF_JMP_JEQ_K, 0));
program.push(Instruction::statement(BPF_RET_K, errno(EPERM)));
let high_word_check = program.len() - 2;
program[high_word_check].jump_if_true = 1;

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.

2 participants