Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
199 changes: 158 additions & 41 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,127 @@ concurrency:
cancel-in-progress: true

jobs:
# ─── Tier 0: fast feedback gate ─────────────────────────────────────
#
# Catches formatting, lint, dependency and semver issues in ~30s.
# Blocks all downstream jobs — no expensive runners are wasted on
# broken code.
#
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable
with:
components: rustfmt, clippy
- uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2

- name: Formatting
run: cargo fmt --check

- name: Clippy
run: cargo clippy --workspace -- -D warnings

- name: Cargo Deny
uses: EmbarkStudios/cargo-deny-action@6c8f9facfa5047ec02d8485b6bf52b587b7777d1 # v2

- name: SemVer Check
uses: obi1kenobi/cargo-semver-checks-action@6b69fcf40e9b5fb17adeb57e4b6ecd020649a239 # v2
with:
package: evalbox

# ─── Tier 1: build check (all targets) ──────────────────────────────
#
# Validates compilation for every supported target. Cross-compile
# targets use cargo check only (no tests). fail-fast: true — if it
# doesn't compile for one target, stop early.
#
build-check:
name: Check (${{ matrix.target }})
needs: lint
runs-on: ${{ matrix.runner }}
strategy:
fail-fast: true
matrix:
include:
- target: x86_64-unknown-linux-gnu
runner: ubuntu-latest
scope: "--workspace"
install: ""
- target: aarch64-unknown-linux-gnu
runner: ubuntu-latest
scope: "--workspace"
install: "sudo apt-get update && sudo apt-get install -y gcc-aarch64-linux-gnu"
- target: x86_64-pc-windows-msvc
runner: windows-latest
scope: "-p evalbox-sandbox"
install: ""
- target: aarch64-pc-windows-msvc
runner: windows-latest
scope: "-p evalbox-sandbox"
install: ""
- target: x86_64-unknown-linux-musl
runner: ubuntu-latest
scope: "--workspace"
install: "sudo apt-get update && sudo apt-get install -y musl-tools"
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable
with:
targets: ${{ matrix.target }}
- uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
with:
key: ${{ matrix.target }}

- name: Install tools
if: matrix.install != ''
run: ${{ matrix.install }}

- name: cargo check
run: cargo check ${{ matrix.scope }} --target ${{ matrix.target }}

# ─── Tier 2: tests (native runners only) ────────────────────────────
#
# Runs the full test suite on targets with native hardware.
# fail-fast: false — see all platform failures at once.
#
# Windows tests only evalbox-sandbox (platform-agnostic crate);
# evalbox-sys depends on Linux syscalls.
#
test:
name: Test (${{ matrix.name }})
needs: build-check
runs-on: ${{ matrix.runner }}
strategy:
fail-fast: false
matrix:
include:
- name: Linux x86_64
runner: ubuntu-latest
scope: "--workspace"
- name: Linux ARM64
runner: ubuntu-24.04-arm
scope: "--workspace"
- name: Windows x86_64
runner: windows-latest
scope: "-p evalbox-sandbox"
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable
- uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2

- name: cargo test
run: cargo test ${{ matrix.scope }}

# ─── Tier 3: heavy checks ───────────────────────────────────────────
#
# Nix flake check (doc build, integration, reproducibility) and
# E2E security tests. Only run after all tests pass.
#
nix-checks:
name: Nix Checks
needs: test
runs-on: ubuntu-latest
permissions:
id-token: write
Expand All @@ -21,51 +140,49 @@ jobs:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: DeterminateSystems/determinate-nix-action@bafaa638b9d5ec0e7e3ac1a7fc80453ef1fd265f # v3
- uses: DeterminateSystems/magic-nix-cache-action@908b263ff629f4cc17666315b7fd3ec127c6244d # main
- name: Run checks (clippy, fmt, test, doc)
- name: nix flake check
run: nix flake check -L

# E2E security tests require kernel 6.12+ (Landlock ABI v5).
# GHA ubuntu-latest currently ships 6.11; image 20260209 has 6.14 but hasn't propagated yet.
# Uncomment when runners get kernel 6.12+.
# ─── E2E security tests (require kernel 6.12+ for Landlock ABI v5) ─
#
# e2e:
# name: E2E (${{ matrix.distro }})
# runs-on: ubuntu-latest
# needs: nix-checks
# permissions:
# id-token: write
# contents: read
# strategy:
# fail-fast: false
# matrix:
# distro: [ubuntu:24.04, fedora:41, alpine:3.21]
# steps:
# - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
# - uses: DeterminateSystems/determinate-nix-action@bafaa638b9d5ec0e7e3ac1a7fc80453ef1fd265f # v3
# - uses: DeterminateSystems/magic-nix-cache-action@908b263ff629f4cc17666315b7fd3ec127c6244d # main
# - name: Build security test binary
# run: nix build -L .#security-test-bin
# - name: Run security tests in ${{ matrix.distro }}
# run: |
# TEST_BIN=$(realpath result/bin/security_tests-*)
# docker run --rm --privileged \
# -v /nix/store:/nix/store:ro \
# ${{ matrix.distro }} \
# "$TEST_BIN" --ignored --test-threads=1

cargo-deny:
name: Cargo Deny
# Tests ACTUAL sandbox isolation: seccomp blocks, Landlock filesystem
# rules, network blocking, resource limits, CVE payloads.
#
# GHA ubuntu-latest ships kernel 6.11 — enable when runners get 6.12+.
#
e2e:
name: E2E (${{ matrix.distro }})
if: false # TODO: enable when GHA runners ship kernel 6.12+
needs: test
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
strategy:
fail-fast: false
matrix:
distro: [ubuntu:24.04, fedora:41, alpine:3.21]
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: EmbarkStudios/cargo-deny-action@6c8f9facfa5047ec02d8485b6bf52b587b7777d1 # v2
- uses: DeterminateSystems/determinate-nix-action@bafaa638b9d5ec0e7e3ac1a7fc80453ef1fd265f # v3
- uses: DeterminateSystems/magic-nix-cache-action@908b263ff629f4cc17666315b7fd3ec127c6244d # main

semver-check:
name: SemVer Check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
- uses: obi1kenobi/cargo-semver-checks-action@6b69fcf40e9b5fb17adeb57e4b6ecd020649a239 # v2
with:
package: evalbox
- name: Check kernel version
run: |
KVER=$(uname -r | cut -d. -f1-2)
echo "Kernel: $KVER"
if [ "$(echo "$KVER < 6.12" | bc)" -eq 1 ]; then
echo "::error::Kernel $KVER < 6.12, Landlock ABI v5 not available"
exit 1
fi

- name: Build security test binary
run: nix build -L .#security-test-bin

- name: Run security tests in ${{ matrix.distro }}
run: |
TEST_BIN=$(realpath result/bin/security_tests-*)
docker run --rm --privileged \
-v /nix/store:/nix/store:ro \
${{ matrix.distro }} \
"$TEST_BIN" --ignored --test-threads=1
6 changes: 3 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ members = [
name = "evalbox"

[workspace.package]
version = "0.1.1"
version = "0.2.0"
edition = "2024"
rust-version = "1.85"
license = "MIT OR Apache-2.0"
Expand All @@ -22,9 +22,9 @@ keywords = ["sandbox", "isolation", "security", "landlock", "seccomp"]
categories = ["os::linux-apis", "development-tools"]

[workspace.dependencies]
evalbox = { version = "0.1.1", path = "crates/evalbox" }
evalbox-sys = { version = "0.1.1", path = "crates/evalbox-sys" }
evalbox-sandbox = { version = "0.1.1", path = "crates/evalbox-sandbox" }
evalbox = { version = "0.2.0", path = "crates/evalbox" }
evalbox-sys = { version = "0.2.0", path = "crates/evalbox-sys" }
evalbox-sandbox = { version = "0.2.0", path = "crates/evalbox-sandbox" }

libc = "0.2"
serde = { version = "1", features = ["derive"] }
Expand Down
79 changes: 79 additions & 0 deletions TODO.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# TODO

## Upstream: rust-lang/libc

### PR: add missing `SYS_sendfile` and `SYS_fadvise64` on aarch64

**Repo**: https://github.com/rust-lang/libc
**Type**: PR (not issue — small enough to just submit directly)
**Precedent**: PR #1435 (Firecracker team, merged in 1 hour)

The `libc` crate does not export `SYS_sendfile` (71) or `SYS_fadvise64` (223) for aarch64. The kernel defines them via `__NR3264_*` macros in `asm-generic/unistd.h` — the indirection likely caused them to be skipped when the aarch64 table was originally added. Other architectures using the same generic table (riscv64, loongarch64) already have them.

**Files to change in rust-lang/libc:**

```
src/unix/linux_like/linux/gnu/b64/aarch64/mod.rs
+ pub const SYS_sendfile: c_long = 71;
+ pub const SYS_fadvise64: c_long = 223;

src/unix/linux_like/linux/musl/b64/aarch64/mod.rs
+ pub const SYS_sendfile: c_long = 71;
+ pub const SYS_fadvise64: c_long = 223;

libc-test/semver/linux-aarch64.txt
+ SYS_fadvise64
+ SYS_sendfile
```

**PR title**: `aarch64: add missing SYS_sendfile and SYS_fadvise64 constants`

**PR body**:

> The aarch64 syscall table is missing `SYS_sendfile` (71) and `SYS_fadvise64` (223).
>
> These are defined in the kernel via `__NR3264_*` macros in `include/uapi/asm-generic/unistd.h`
> and resolve to `__NR_sendfile = 71` and `__NR_fadvise64 = 223` on 64-bit architectures.
> They are also present in glibc's `sysdeps/unix/sysv/linux/aarch64/arch-syscall.h`.
>
> Other architectures sharing the same generic syscall table (riscv64, loongarch64) already
> export these constants. The aarch64 module skips numbers 71 and 223 entirely.
>
> This affects projects using seccomp-BPF on aarch64 that need to whitelist these syscalls
> (e.g. sandbox implementations).

**Notes**:
- Target `libc-0.2` branch (current stable, gets published to crates.io)
- Constants are manually maintained, not auto-generated
- Run `cd libc-test && cargo test` and `./ci/style.py` before submitting
- The repo has a v1.0 migration happening but `libc-0.2` still receives releases

**Workaround in evalbox** (until upstream merges):
```rust
// crates/evalbox-sys/src/seccomp.rs
#[cfg(target_arch = "aarch64")]
mod nr {
pub const SYS_SENDFILE: i64 = 71;
pub const SYS_FADVISE64: i64 = 223;
}
```

**Evidence**:
- Checked libc 0.2.182, 0.2.185, 0.2.186 and `main` branch — all missing
- Kernel: https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/unistd.h
- glibc: `sysdeps/unix/sysv/linux/aarch64/arch-syscall.h` defines both
- Related: #1348 (similar musl gap, fixed in #1435 by Firecracker team)

---

## CI blockers

### E2E security tests — kernel 6.12+

GHA `ubuntu-latest` ships kernel 6.11. E2E security tests need Landlock ABI v5 (signal/IPC scoping) which requires 6.12+. Disabled with `if: false`.

**Action**: Monitor GHA runner updates. Remove `if: false` when 6.12+ lands.

### Windows ARM64 native runner

`windows-11-arm` is in GHA public preview (unstable). Currently cross-compiling `aarch64-pc-windows-msvc` from x86_64. When runner stabilizes, add native job with tests.
19 changes: 15 additions & 4 deletions crates/evalbox-sandbox/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,27 @@ repository.workspace = true
description = "Sandbox orchestration for evalbox"

[dependencies]
thiserror.workspace = true
which.workspace = true
tempfile.workspace = true

# Linux-only dependencies (seccomp, landlock, pidfd, mio epoll)
[target.'cfg(target_os = "linux")'.dependencies]
evalbox-sys.workspace = true
libc.workspace = true
rustix.workspace = true
tempfile.workspace = true
mio.workspace = true
thiserror.workspace = true
which.workspace = true

[build-dependencies]
# Windows-only dependencies (future: windows-sys for Job Objects, AppContainer)
# [target.'cfg(target_os = "windows")'.dependencies]
# windows-sys = { version = "0.59", features = [...] }

[target.'cfg(target_os = "linux")'.build-dependencies]
cc = "1.2"

[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]

[lints]
workspace = true
Loading
Loading