Skip to content

test(suidhelper): losetup/thin_dump coverage + E2E Rust coverage collection - #65

Merged
markovejnovic merged 11 commits into
mainfrom
test/native-cov
Jul 9, 2026
Merged

test(suidhelper): losetup/thin_dump coverage + E2E Rust coverage collection#65
markovejnovic merged 11 commits into
mainfrom
test/native-cov

Conversation

@markovejnovic

Copy link
Copy Markdown
Contributor

Summary

  • tools/losetup.rs and tools/thin_dump.rs showed 0%/near-0% Rust coverage despite being exercised by the live E2E — the integration job ran an uninstrumented helper and uploaded no Rust lcov, and no Rust test touched these files.
  • New tests (lib-level + binary-level L4): ok_backing_file refusal/accept contracts (confinement property, symlink-escape, inheritable-fd inode round-trip), losetup attach/detach argv+fd contracts, thin-dump argv/parse/error contracts. Local root slice: losetup.rs 59/62 lines, thin_dump.rs 20/20 (was 0%).
  • Product bug found & fixed by these tests: main.rs ran Cli::parse() before security_gate::init(), so config-consulting value_parsers (losetup attach's backing-file gate) memoized the production /etc/hyper/config.toml before the test seam could redirect it. Gate init now precedes parse (release builds unaffected — the gate is compile-time secure there).
  • E2E Rust coverage collection: cargo xtask stamp gains an env-gated (HYPER_SUIDHELPER_INSTRUMENT_COVERAGE=1) -C instrument-coverage build (helper only — never the guest agent); the integration job sets it job-wide, routes profiles via LLVM_PROFILE_FILE, and a post-test step merges/exports lcov (pinned-nightly llvm-tools) and uploads under flags: rust.

Test plan

  • cargo nextest run --all-features 164/164; root slice 32/32; fmt/clippy clean
  • mix gate: format/compile/credo/dialyzer clean, mix test 367 passed (after mix suidhelper.install — the main.rs fix changes the helper checksum)
  • Integration job's Export suidhelper E2E coverage step finds profraws and uploads (this PR's CI run)

Some subcommands' clap value_parsers (losetup attach's ok_backing_file)
consult Config::get() while clap parses argv, and Config consults the
security gate. With init() running after Cli::parse(), that forces the
process-wide Config LazyLock while the gate is still closed, so it loads
and memoizes the production /etc/hyper/config.toml for the rest of the
process — bypassing the insecure-test-seam config redirect for any
subcommand whose parser touches config. Move the gate resolution ahead
of Cli::parse() so the first config load, wherever it happens, sees the
correct gate state. No behavior change in release builds (the gate is
always secure there); only the test seam ordering was broken.
commit 6da778c hardcoded /srv/hyper as work_dir after misdiagnosing a
security-gate ordering bug as a config-loading bug in the helper. With
main.rs now resolving the security gate before clap parses argv, the
insecure-test-seam config redirect works for every subcommand, including
losetup attach, so the tests can go back to a tempdir-canonicalized
work_dir as originally specified (task-3-brief.md Step 1) instead of
depending on /srv/hyper existing on the host.
@codecov

codecov Bot commented Jul 8, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ All tests successful. No failed tests found.

📢 Thoughts on this report? Let us know!

@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown

Test Results

  4 files  ± 0  193 suites  +3   3m 12s ⏱️ +53s
557 tests +16  557 ✅ +16    0 💤 ±0  0 ❌ ±0 
942 runs  +16  557 ✅ +16  385 💤 ±0  0 ❌ ±0 

Results for commit 990bf42. ± Comparison against base commit d57336c.

♻️ This comment has been updated with latest results.

…le loss

The jailer subcommand execve's on success and _exit(2)s on failure, so
the profile runtime's atexit dump never runs and those invocations
counted nothing. -C llvm-args=-runtime-counter-relocation (same xtask
gate as instrument-coverage) plus %c in LLVM_PROFILE_FILE switches to
continuous mode: counters are mmap-written as they increment, so the
profile is already on disk when the image is replaced.

Verified locally: jailer _exit run writes a full profraw (was 0-byte),
103 jailer.rs lines covered from one run; version run stderr-clean.
@markovejnovic
markovejnovic merged commit 252a05f into main Jul 9, 2026
7 checks passed
@markovejnovic
markovejnovic deleted the test/native-cov branch July 9, 2026 00:52
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