refactor(images): split OCI extractor and fix containment bugs#446
Merged
Conversation
Split `archive/tar.rs` into focused modules and replace free functions
with structs that call sites interact with directly:
archive/
├── compression.rs TarballReader::open (gzip auto-detect)
├── metadata.rs EntryMetadata + builder
├── extractor.rs LayerExtractor
├── verifier.rs LayerVerifier
├── override_stat.rs (unchanged)
├── time.rs (unchanged)
└── safe_root/ containment: pathrs on Linux, scrub+std fallback
Call sites in storage.rs, blob_source.rs, object.rs and rootfs/builder.rs
now use `LayerExtractor::new(dest).extract_tarball(..)` and
`LayerVerifier::new(diff_id)?.verify_tarball(..)` directly.
Introduces SafeRoot as the single containment boundary. On Linux every
op routes through pathrs::Root (kernel-atomic openat2 RESOLVE_IN_ROOT).
On other platforms, a lexical fallback scrubs escape symlinks in the
ancestor chain before each create_dir_all.
Fixes four audit findings, each with a regression test:
* unix_time panic on crafted mtime: saturate at i64::MAX seconds so
`UNIX_EPOCH + Duration::from_secs(..)` can't overflow on a tar header
near u64::MAX.
* whiteout traversal through pre-existing escape symlinks:
handle_whiteout and apply_opaque_whiteout now route through
SafeRoot::{ensure_parent, remove_nofollow} so ancestry escape
symlinks are scrubbed before deletion.
* SafeRoot::create_hardlink accepted target_rel containing `..`
underflow on the fallback backend, letting fs::hard_link resolve
outside the extraction root: normalize both args, reject on escape.
* apply_permissions_and_times / apply_ownership mutated the target's
shared inode when processing hardlink entries: skip for
EntryType::Link.
Tests: 44/44 archive tests pass, including the GHSA-f396-4rp4-7v2j
regression plus the four new reproducers. clippy clean, fmt clean.
…lement `cargo test -p boxlite` implicitly rebuilds the boxlite-shim bin target, which wipes the ad-hoc signature that scripts/build/sign.sh puts on it. The copy that build.rs makes into the runtime dir then embeds an unsigned shim, and every VM-dependent integration test fails with "Hypervisor.framework access denied" — including the pre-push hook's zygote_integration suite. Make the embed step self-sufficient: after copying boxlite-shim into the runtime dir on macOS, codesign it ad-hoc with com.apple.security.hypervisor + disable-library-validation. Entitlements are written to a sibling plist since codesign's --entitlements flag needs a real file path.
… rustix
CI clippy on Linux failed with "cannot find module or crate `rustix`"
because safe_root::linux used `rustix::fs::makedev` without adding
`rustix` as a direct dependency. Swap to `libc::makedev`, which is
already a dependency and returns the same `u64` that
`pathrs::InodeType::{CharacterDevice, BlockDevice}` expects
(`rustix_fs::Dev = u64` on Linux). No behavior change, just fewer
transitive-crate assumptions.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
archive/tar.rs(1.7k lines, 40 tests) into focused modules —compression,metadata,extractor,verifier, andsafe_root/{mod,path,linux,fallback,tests}. Free functions (extract_layer_tarball_streaming,verify_diff_id,apply_oci_layer) are replaced withLayerExtractor/LayerVerifierstructs;storage.rs,blob_source.rs,object.rs, androotfs/builder.rsuse them directly.SafeRootas the single containment boundary. On Linux every mutation flows throughpathrs::Root(kernel-atomicopenat2(RESOLVE_IN_ROOT)). On other platforms, a lexical fallback scrubs escape symlinks in the ancestor chain before eachcreate_dir_all.unix_timepanic on craftedmtime— saturate ati64::MAXseconds beforeUNIX_EPOCH + Duration::from_secs(..).handle_whiteout/apply_opaque_whiteoutroute throughSafeRoot::{ensure_parent, remove_nofollow}, scrubbing ancestry before deletion.SafeRoot::create_hardlinkacceptedtarget_relwith..underflow on the fallback backend — normalize both paths, reject on escape.apply_permissions_and_times/apply_ownershipmutated shared inode via hardlink entries — skip forEntryType::Link.build.rsself-sign the embeddedboxlite-shimon macOS socargo test -p boxlite's implicit shim rebuild no longer drops the hypervisor entitlement and breaks every VM integration test.Test plan
cargo test -p boxlite --lib images::archive— 44/44 pass (GHSA-f396-4rp4-7v2j regression + four new bug reproducers)cargo test -p boxlite --features krun,gvproxy --test zygote_integration— previously-failing zygote tests passcargo clippy -p boxlite --tests -- -D warningscleancargo fmt --checkcleanmake test) green