Skip to content

test: strengthen Rust policy_fn tests + fix c_smoke cdylib build (follow-ups to #103)#104

Merged
congwang-mk merged 2 commits into
mainfrom
test/policy-fn-and-ffi-ci-followups
Jun 15, 2026
Merged

test: strengthen Rust policy_fn tests + fix c_smoke cdylib build (follow-ups to #103)#104
congwang-mk merged 2 commits into
mainfrom
test/policy-fn-and-ffi-ci-followups

Conversation

@congwang-mk

Copy link
Copy Markdown
Contributor

Two post-merge follow-ups to #103 (which strengthened the Python policy_fn tests and fixed the dynamic-policy / fork-tracking bugs).

1. Strengthen the Rust policy_fn integration tests + add missing coverage

The Rust integration tests had the same false-passing patterns the Python suite did, and lacked coverage for the bugs #103 fixed (so they would not have caught them):

Strengthened (were false-passing):

  • test_policy_fn_deny_connect connected to a dead port and accepted any error as "blocked". Now targets a live allowlisted listener and asserts EPERM (errno 1) from the policy_fn deny.
  • test_policy_fn_restrict_network_takes_effect used restrict_network(&[]) (a no-op) and a dead port. Now uses two live loopback listeners + a non-empty restriction; asserts the listed IP connects and the non-listed one is refused (errno 111).
  • test_policy_fn_restrict_pid_network_without_allowlist connected to a dead port. Now uses a live listener so the refusal can only come from the per-pid restriction.

Added (no Rust test exercised these):

  • test_policy_fn_restrict_max_memory_enforced — 256 MiB ceiling, restrict to 64 MiB, assert a 128 MiB alloc is killed while the control succeeds.
  • test_policy_fn_restrict_max_processes_enforced — restrict to 1, assert the fork is denied with EAGAIN while the control forks.
  • test_policy_fn_fork_does_not_deadlock — 30 forks under an active policy_fn complete within a bounded timeout (fork-tracking regression).

The enforcement tests use run() (captured stdout) rather than run_interactive() (inherited stdio).

2. Build the cdylib in the c_smoke test (CI fix)

tests/c_smoke.rs linked whatever libsandlock_ffi.so already existed in target/, but Cargo does not treat the cdylib as a build prerequisite of an integration test (tests link the rlib). So cargo test never (re)builds the .so, and CI linked a stale artifact missing newer symbols — undefined reference to sandlock_action_set_inject_bytes.

The test now runs cargo build -p sandlock-ffi --lib to refresh the cdylib before locating and linking it, so it always links the current artifact. The recursive cargo is safe (the outer build lock is released before tests run).

Test plan

  • cargo test -p sandlock-core — 398 lib + 260 integration pass (incl. the 16 policy_fn integration tests: 3 strengthened + 3 new).
  • cargo test -p sandlock-ffi --test c_smoke — passes; verified by deleting the .so first (the test rebuilds it and passes instead of failing on a missing/stale artifact).

…rcement coverage

The Rust policy_fn integration tests had the same false-passing patterns
the Python suite did, and lacked coverage for the bugs just fixed:

- test_policy_fn_deny_connect connected to a dead port and accepted any
  error as "blocked". Now targets a live allowlisted listener and asserts
  EPERM (errno 1) from the policy_fn deny specifically.
- test_policy_fn_restrict_network_takes_effect used restrict_network(&[])
  (a no-op) and a dead port. Now uses two live loopback listeners and a
  non-empty restriction, asserting the listed IP connects and the
  non-listed one is refused (errno 111).
- test_policy_fn_restrict_pid_network_without_allowlist connected to a dead
  port. Now uses a live listener so the refusal can only come from the
  per-pid restriction.

New tests covering behavior no Rust test exercised (so the no-op /
deadlock bugs would have gone uncaught):

- test_policy_fn_restrict_max_memory_enforced — 256 MiB ceiling, restrict
  to 64 MiB, assert a 128 MiB alloc is killed while the control succeeds.
- test_policy_fn_restrict_max_processes_enforced — restrict to 1, assert
  the fork is denied with EAGAIN while the control forks.
- test_policy_fn_fork_does_not_deadlock — 30 forks under an active
  policy_fn complete within a bounded timeout (fork-tracking regression).

The enforcement tests use run() (captured stdout) rather than
run_interactive() (inherited stdio).

Signed-off-by: Cong Wang <cwang@multikernel.io>
The C smoke test linked whatever libsandlock_ffi.so already existed in
target/, but Cargo does not treat the cdylib as a build prerequisite of an
integration test (tests link the rlib). So `cargo test` never (re)builds
the .so, and CI linked a stale artifact missing newer symbols
(undefined reference to sandlock_action_set_inject_bytes).

Build the cdylib from within the test via `cargo build -p sandlock-ffi
--lib` before locating and linking it, so the C test always links the
current artifact. The recursive cargo invocation is safe — the outer build
lock is released before tests run. Verified by deleting the .so and
re-running: the test rebuilds it and passes.

Signed-off-by: Cong Wang <cwang@multikernel.io>
@congwang-mk congwang-mk merged commit c590134 into main Jun 15, 2026
12 checks passed
@congwang-mk congwang-mk deleted the test/policy-fn-and-ffi-ci-followups branch June 15, 2026 00:26
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.

1 participant