diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5442ad17..e1b912f1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -78,17 +78,10 @@ jobs: - name: Install protoc-gen-elixir run: mix escript.install hex protobuf 0.17.0 --force - # The `:guest_agent_build` Mix compiler builds the static musl guest agent - # (native/guest-agent) into priv/ on every `mix compile`; the host-arch - # build is a hard requirement, so the runner's default toolchain needs the - # musl std. Cross arches are best-effort and stay skipped here. - - name: Install musl target for the guest-agent build - run: rustup target add x86_64-unknown-linux-musl - - - name: Cache guest-agent cargo build + - name: Cache native cargo build uses: Swatinem/rust-cache@v2 with: - workspaces: native/guest-agent + workspaces: native - name: Compile (warnings as errors) # --force matches the `mix check` alias: re-surfaces warnings even on a @@ -173,54 +166,37 @@ jobs: - name: ShellCheck run: git ls-files -z '*.sh' | xargs -0 -r shellcheck - # The guest agent is otherwise only built by the `:guest_agent_build` Mix - # compiler, where cargo warnings scroll past without failing the build. - guest-agent: - name: Rust (guest-agent) - runs-on: ubuntu-latest - defaults: - run: - working-directory: native/guest-agent - steps: - - uses: actions/checkout@v7 - - # build.rs compiles agent.proto with prost-build, which shells out to protoc. - - name: Install protoc - run: sudo apt-get update && sudo apt-get install -y protobuf-compiler - - - uses: Swatinem/rust-cache@v2 - with: - workspaces: native/guest-agent - - - name: rustfmt - run: cargo fmt --check - - - name: clippy - run: cargo clippy --all-targets --all-features -- -D warnings - + # One job for the whole native/ workspace: guest-agent, hyperrs-elfsum, + # suidhelper and xtask. The guest agent and xtask are otherwise only built by + # Mix compilers, where cargo warnings scroll past without failing the build. rust: - name: Rust (suidhelper) + name: Rust (native workspace) runs-on: ubuntu-latest defaults: run: - working-directory: native/suidhelper + working-directory: native steps: - uses: actions/checkout@v7 - # rustup reads native/suidhelper/rust-toolchain.toml on the first cargo - # call and installs the pinned nightly toolchain, components, and targets. + # rustup reads native/rust-toolchain.toml on the first cargo call and + # installs the pinned nightly toolchain, components, and targets. - name: Show toolchain run: rustup show + # guest-agent's build.rs compiles agent.proto with prost-build, which + # shells out to protoc. + - name: Install protoc + run: sudo apt-get update && sudo apt-get install -y protobuf-compiler + - uses: Swatinem/rust-cache@v2 with: - workspaces: native/suidhelper + workspaces: native - name: rustfmt - run: cargo fmt --check + run: cargo fmt --all --check - name: clippy - run: cargo clippy --all-targets --all-features -- -D warnings + run: cargo clippy --workspace --all-targets --all-features -- -D warnings - name: Install cargo-llvm-cov and nextest uses: taiki-e/install-action@v2 @@ -231,7 +207,7 @@ jobs: # `nextest` runs the suite while llvm-cov collects coverage AND nextest # writes target/nextest/ci/junit.xml (see .config/nextest.toml). One run, # two artifacts: lcov.info for coverage, junit.xml for Test Analytics. - run: cargo llvm-cov nextest --profile ci --all-features --lcov --output-path lcov.info + run: cargo llvm-cov nextest --workspace --profile ci --all-features --lcov --output-path lcov.info # !cancelled() uploads on pass OR fail (see elixir job). The action runs at # repo root, so the path is fully qualified -- working-directory only @@ -242,25 +218,25 @@ jobs: uses: codecov/codecov-action@v7 with: token: ${{ secrets.CODECOV_TOKEN }} - files: native/suidhelper/target/nextest/ci/junit.xml + files: native/target/nextest/ci/junit.xml flags: rust report_type: test_results # Repo-relative path: upload-artifact is an action, so the rust job's - # `working-directory: native/suidhelper` default does NOT apply. + # `working-directory: native` default does NOT apply. - name: Upload Rust test results artifact if: ${{ !cancelled() }} uses: actions/upload-artifact@v7 with: name: rust-test-results - path: native/suidhelper/target/nextest/ci/junit.xml + path: native/target/nextest/ci/junit.xml - name: Upload coverage to Codecov uses: codecov/codecov-action@v7 with: token: ${{ secrets.CODECOV_TOKEN }} # working-directory only applies to `run:` steps, not actions. - files: native/suidhelper/lcov.info + files: native/lcov.info flags: rust fail_ci_if_error: true @@ -273,14 +249,14 @@ jobs: steps: - uses: actions/checkout@v7 - # rustup reads native/suidhelper/rust-toolchain.toml and installs the pinned + # rustup reads native/rust-toolchain.toml and installs the pinned # nightly on the first cargo call. - name: Show toolchain run: rustup show - uses: Swatinem/rust-cache@v2 with: - workspaces: native/suidhelper + workspaces: native - name: Install cargo-llvm-cov and nextest uses: taiki-e/install-action@v2 @@ -401,15 +377,10 @@ jobs: - name: Install protoc-gen-elixir run: mix escript.install hex protobuf 0.17.0 --force - - name: Install musl target for the guest-agent build - run: rustup target add x86_64-unknown-linux-musl - - name: Cache rust builds uses: Swatinem/rust-cache@v2 with: - workspaces: | - native/guest-agent - native/suidhelper + workspaces: native - name: Compile (warnings as errors) run: mix compile --warnings-as-errors @@ -528,7 +499,7 @@ jobs: -o suidhelper.profdata "$TOOLS/llvm-cov" export --format=lcov \ --instr-profile=suidhelper.profdata \ - target/hyper-suidhelper \ + ../target/hyper-suidhelper \ "$GITHUB_WORKSPACE/native/suidhelper/src" \ > lcov-e2e.info sed -i "s|^SF:$GITHUB_WORKSPACE/|SF:|" lcov-e2e.info diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ea48d6bc..ba47b906 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -42,14 +42,14 @@ jobs: # manifest. `0,/re/` rewrites only the first match, which for each # Cargo.toml is its own `[package] version` (xtask is intentionally # left at 0.0.0 — it is publish = false). - for m in native/suidhelper/Cargo.toml native/suidhelper/meta/Cargo.toml native/guest-agent/Cargo.toml; do + for m in native/suidhelper/Cargo.toml native/hyperrs-elfsum/Cargo.toml native/guest-agent/Cargo.toml; do sed -i -E "0,/^version = \"[^\"]*\"/s//version = \"${version}\"/" "$m" done sed -i -E "0,/version: \"[^\"]*\"/s//version: \"${version}\"/" mix.exs git config user.name "github-actions[bot]" git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - git add native/suidhelper/Cargo.toml native/suidhelper/meta/Cargo.toml native/guest-agent/Cargo.toml mix.exs + git add native/suidhelper/Cargo.toml native/hyperrs-elfsum/Cargo.toml native/guest-agent/Cargo.toml mix.exs # Idempotent: a re-run after the bump already landed has nothing to commit. if git diff --cached --quiet; then @@ -80,14 +80,14 @@ jobs: with: ref: ${{ needs.prepare-release.outputs.sha }} - # rustup reads native/suidhelper/rust-toolchain.toml on the first cargo + # rustup reads native/rust-toolchain.toml on the first cargo # call and installs the pinned nightly + both musl targets. - name: Show toolchain run: rustup show - uses: Swatinem/rust-cache@v2 with: - workspaces: native/suidhelper + workspaces: native - name: Build release binary run: cargo build --release --target ${{ matrix.target }} @@ -97,7 +97,7 @@ jobs: v="${{ needs.prepare-release.outputs.version }}" out="hyper-suidhelper-${v}-${{ matrix.target }}" mkdir -p dist - cp "target/${{ matrix.target }}/release/hyper-suidhelper" "dist/${out}" + cp "../target/${{ matrix.target }}/release/hyper-suidhelper" "dist/${out}" - uses: actions/upload-artifact@v7 with: diff --git a/AGENTS.md b/AGENTS.md index 8f54be44..c46ceedd 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -27,7 +27,8 @@ mix test --only integration --only external # live E2E: boots real Firecracker VMs. Needs a provisioned # KVM host (docs/cookbook/install.md) + passwordless sudo. # CI runs this in ci.yml's `integration` job on ubuntu-latest. -cargo nextest run # Rust suite (run inside native/suidhelper/) +cargo nextest run --workspace --all-features + # Rust suite (run inside native/; one workspace for every crate) ``` Pure tests under `test/unit` and `test/controls` need neither Postgres nor @@ -47,6 +48,10 @@ run in CI's `integration` job, which provisions the GitHub-hosted runner via `lib/hyper/redist/` — **pure cores**: units algebra, strict number parsing, EWMA/rate controls, `/proc` parsers, hashing. No processes, no I/O. These are where property tests pay off most. +- `native/` — one Cargo workspace (shared `Cargo.lock`, `target/`, + `rust-toolchain.toml`, release profiles) for every Rust crate below. +- `native/hyperrs-elfsum/` — ELF self-checksum slot and stamping, shared by the + suidhelper and its xtask. - `native/suidhelper/` — the privileged Rust helper. Source in `src/`, tests inline in `src/` or in `tests/` (see Rust rules below). - Generated, do not hand-edit: `lib/hyper/firecracker/api/{operations,schemas}` @@ -168,7 +173,7 @@ If a case needs its own assertions, it is a separate test, not a row. (`tests/` subdirectories are not auto-discovered) — copy an existing `[[test]]` block when adding one. - `proptest` is the dev-dependency; use `proptest!{ #[test] fn ... }` with - `prop_assert!`. CI runs `cargo nextest run --profile ci` (one retry to damp + `prop_assert!`. CI runs `cargo nextest run --workspace --profile ci` (one retry to damp flakes — do not write tests that *rely* on the retry). ## Other conventions diff --git a/lib/mix/tasks/suidhelper.install.ex b/lib/mix/tasks/suidhelper.install.ex index d5164193..7ec8622f 100644 --- a/lib/mix/tasks/suidhelper.install.ex +++ b/lib/mix/tasks/suidhelper.install.ex @@ -22,7 +22,7 @@ defmodule Mix.Tasks.Suidhelper.Install do This is the privileged counterpart to `mix suidhelper.stamp`, which stamps only. `cargo` and the helper's toolchain (see - `native/suidhelper/rust-toolchain.toml`) must be installed. + `native/rust-toolchain.toml`) must be installed. """ use Mix.Task @@ -31,7 +31,7 @@ defmodule Mix.Tasks.Suidhelper.Install do # dependency: there the crate lives under deps/hypervm/native/suidhelper, not # under the consumer project's root. @helper_dir Path.expand("../../../native/suidhelper", __DIR__) - @source Path.join(@helper_dir, "target/hyper-suidhelper") + @source Path.expand("../target/hyper-suidhelper", @helper_dir) # Must match `Hyper.Cfg.Tools.suidhelper/0`'s default path and the xtask's # `INSTALL_PATH`: a `PATH` location the unprivileged node can exec. @install_path "/usr/local/bin/hyper-suidhelper" diff --git a/mix.exs b/mix.exs index dc6e7f05..685c42e0 100644 --- a/mix.exs +++ b/mix.exs @@ -218,14 +218,14 @@ defmodule Hyper.MixProject do files: ~w( lib priv/firecracker priv/repo priv/vmlinux proto config mix.exs README.md LICENSE + native/Cargo.toml native/Cargo.lock native/rust-toolchain.toml native/.config native/guest-agent/src native/guest-agent/tests native/guest-agent/build.rs - native/guest-agent/Cargo.toml native/guest-agent/Cargo.lock - native/guest-agent/.cargo - native/suidhelper/src native/suidhelper/tests native/suidhelper/meta + native/guest-agent/Cargo.toml native/guest-agent/.cargo + native/hyperrs-elfsum/src native/hyperrs-elfsum/Cargo.toml + native/suidhelper/src native/suidhelper/tests native/suidhelper/xtask native/suidhelper/build.rs - native/suidhelper/Cargo.toml native/suidhelper/Cargo.lock - native/suidhelper/rust-toolchain.toml native/suidhelper/README.md - native/suidhelper/.cargo native/suidhelper/.config + native/suidhelper/Cargo.toml native/suidhelper/README.md + native/suidhelper/.cargo ), # `lib` is included wholesale, so on a dev box the gitignored generated # outputs (bindings, expected.ex) exist on disk and would leak into the @@ -501,7 +501,7 @@ defmodule Mix.Tasks.Compile.SuidhelperStamp do use Mix.Task.Compiler @helper_dir "native/suidhelper" - @binary "native/suidhelper/target/hyper-suidhelper" + @binary "native/target/hyper-suidhelper" @out "lib/hyper/suid_helper/expected.ex" @impl Mix.Task.Compiler @@ -522,7 +522,7 @@ defmodule Mix.Tasks.Compile.SuidhelperStamp do `cargo xtask stamp` failed (exit #{code}) building the suidhelper: #{output} - Ensure `cargo`, the helper's toolchain and its musl target (see #{@helper_dir}/rust-toolchain.toml) + Ensure `cargo`, the helper's toolchain and its musl target (see native/rust-toolchain.toml) are installed. """) end @@ -619,7 +619,7 @@ defmodule Mix.Tasks.Compile.GuestAgentBuild do end defp build!(arch, triple, required: required?) do - case System.cmd("cargo", ["build", "--release", "--target", triple], + case System.cmd("cargo", ["build", "--profile", "release-guest", "--target", triple], cd: @agent_dir, stderr_to_stdout: true ) do @@ -644,7 +644,7 @@ defmodule Mix.Tasks.Compile.GuestAgentBuild do end defp install(arch, triple) do - src = Path.join(@agent_dir, "target/#{triple}/release/hyper-guest-agent") + src = Path.join("native/target", "#{triple}/release-guest/hyper-guest-agent") dest = Path.join(@out_dir, "hyper-guest-agent-#{arch}") File.cp!(src, dest) File.chmod!(dest, 0o755) @@ -665,7 +665,10 @@ defmodule Mix.Tasks.Compile.GuestAgentBuild do end defp installed_targets do - case System.cmd("rustup", ["target", "list", "--installed"], stderr_to_stdout: true) do + case System.cmd("rustup", ["target", "list", "--installed"], + cd: @agent_dir, + stderr_to_stdout: true + ) do {out, 0} -> out |> String.split("\n", trim: true) |> Enum.map(&String.trim/1) # No rustup / unknown: build only the host arch (never attempt a cross build). _ -> [] diff --git a/native/suidhelper/.config/nextest.toml b/native/.config/nextest.toml similarity index 100% rename from native/suidhelper/.config/nextest.toml rename to native/.config/nextest.toml diff --git a/native/.gitignore b/native/.gitignore new file mode 100644 index 00000000..264973ef --- /dev/null +++ b/native/.gitignore @@ -0,0 +1,3 @@ +/target +/lcov.info +*.profraw diff --git a/native/guest-agent/Cargo.lock b/native/Cargo.lock similarity index 65% rename from native/guest-agent/Cargo.lock rename to native/Cargo.lock index 17406419..421b1e6a 100644 --- a/native/guest-agent/Cargo.lock +++ b/native/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + [[package]] name = "aho-corasick" version = "1.1.4" @@ -11,12 +17,89 @@ dependencies = [ "memchr", ] +[[package]] +name = "anstream" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + +[[package]] +name = "anstyle-parse" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys", +] + [[package]] name = "anyhow" version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3" +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + +[[package]] +name = "arrayvec" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f02882884d3e1bc524fb12c79f107f6ad0e1cfd498c536ffb494301740995dfe" + +[[package]] +name = "assert_cmd" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2aa3a22042e45de04255c7bf3626e239f450200fd0493c1e382263544b20aea6" +dependencies = [ + "anstyle", + "bstr", + "libc", + "predicates", + "predicates-core", + "predicates-tree", + "wait-timeout", +] + [[package]] name = "async-trait" version = "0.1.89" @@ -110,12 +193,72 @@ version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" +[[package]] +name = "blake3" +version = "1.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0aa83c34e62843d924f905e0f5c866eb1dd6545fc4d719e803d9ba6030371fce" +dependencies = [ + "arrayref", + "arrayvec", + "cc", + "cfg-if", + "constant_time_eq", + "cpufeatures", +] + +[[package]] +name = "bon" +version = "3.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a602c73c7b0148ec6d12af6fd5cc7a46e2eacc8878271a999abac56eed12f561" +dependencies = [ + "bon-macros", + "rustversion", +] + +[[package]] +name = "bon-macros" +version = "3.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dee98b0db6a962de883bf5d20362dee4d7ca0d12fe39a7c6c73c844e1cd7c1f" +dependencies = [ + "darling", + "ident_case", + "prettyplease", + "proc-macro2", + "quote", + "rustversion", + "syn", +] + +[[package]] +name = "bstr" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bb31b46c14244e20ee9984b11bf5c992b91fb6939fea616e3512c8baecdbe5f" +dependencies = [ + "memchr", + "regex-automata", + "serde_core", +] + [[package]] name = "bytes" version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ae3f5d315924270530207e2a68396c3cc547f6dca3fbdca317cfb1a51edb593" +[[package]] +name = "cc" +version = "1.2.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e228eec9be7c17ccb640b59b36a5cd805ea2a564a4c5e162c2f659fea30d3b96" +dependencies = [ + "find-msvc-tools", + "shlex", +] + [[package]] name = "cfg-if" version = "1.0.4" @@ -128,6 +271,148 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +[[package]] +name = "clap" +version = "4.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" + +[[package]] +name = "colorchoice" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" + +[[package]] +name = "constant_time_eq" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b" + +[[package]] +name = "convert_case" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "darling" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" +dependencies = [ + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "derive_more" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn", + "unicode-xid", +] + +[[package]] +name = "difflib" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" + [[package]] name = "either" version = "1.16.0" @@ -156,12 +441,37 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + [[package]] name = "fixedbitset" version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" +[[package]] +name = "flate2" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "float-cmp" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b09cf3155332e944990140d967ff5eceb70df778b34f77d8075db46e4704e6d8" +dependencies = [ + "num-traits", +] + [[package]] name = "fnv" version = "1.0.7" @@ -408,6 +718,28 @@ dependencies = [ "tonic-prost-build", ] +[[package]] +name = "hyper-suidhelper" +version = "0.1.0" +dependencies = [ + "assert_cmd", + "bon", + "clap", + "derive_more", + "hyperrs-elfsum", + "nix 0.29.0", + "predicates", + "proptest", + "quick-xml", + "ref-cast", + "serde", + "serde_json", + "smart-default", + "tempfile", + "thiserror 1.0.69", + "toml", +] + [[package]] name = "hyper-timeout" version = "0.5.2" @@ -441,6 +773,22 @@ dependencies = [ "tracing", ] +[[package]] +name = "hyperrs-elfsum" +version = "0.1.0" +dependencies = [ + "blake3", + "object", + "proptest", + "thiserror 1.0.69", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "indexmap" version = "2.14.0" @@ -451,6 +799,12 @@ dependencies = [ "hashbrown 0.17.1", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + [[package]] name = "itertools" version = "0.14.0" @@ -511,6 +865,16 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + [[package]] name = "mio" version = "1.2.1" @@ -553,6 +917,12 @@ dependencies = [ "memoffset", ] +[[package]] +name = "normalize-line-endings" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" + [[package]] name = "num-traits" version = "0.2.19" @@ -562,12 +932,32 @@ dependencies = [ "autocfg", ] +[[package]] +name = "object" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +dependencies = [ + "crc32fast", + "flate2", + "hashbrown 0.15.5", + "indexmap", + "memchr", + "ruzstd", +] + [[package]] name = "once_cell" version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + [[package]] name = "percent-encoding" version = "2.3.2" @@ -593,7 +983,7 @@ checksum = "51c83a25a484a726b9f20ca9098d12ff32e429b5a37193a63b9308f141b25360" dependencies = [ "nix 0.31.3", "signal-hook", - "thiserror", + "thiserror 2.0.18", ] [[package]] @@ -631,6 +1021,36 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "predicates" +version = "3.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ada8f2932f28a27ee7b70dd6c1c39ea0675c55a36879ab92f3a715eaa1e63cfe" +dependencies = [ + "anstyle", + "difflib", + "float-cmp", + "normalize-line-endings", + "predicates-core", + "regex", +] + +[[package]] +name = "predicates-core" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cad38746f3166b4031b1a0d39ad9f954dd291e7854fcc0eed52ee41a0b50d144" + +[[package]] +name = "predicates-tree" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0de1b847b39c8131db0467e9df1ff60e6d0562ab8e9a16e568ad0fdb372e2f2" +dependencies = [ + "predicates-core", + "termtree", +] + [[package]] name = "prettyplease" version = "0.2.37" @@ -748,11 +1168,21 @@ version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" +[[package]] +name = "quick-xml" +version = "0.37.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb" +dependencies = [ + "memchr", + "serde", +] + [[package]] name = "quote" -version = "1.0.46" +version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" dependencies = [ "proc-macro2", ] @@ -807,6 +1237,26 @@ dependencies = [ "rand_core", ] +[[package]] +name = "ref-cast" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "regex" version = "1.12.4" @@ -836,6 +1286,15 @@ version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + [[package]] name = "rustix" version = "1.1.4" @@ -849,6 +1308,12 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "rustversion" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" + [[package]] name = "rusty-fork" version = "0.3.1" @@ -861,6 +1326,31 @@ dependencies = [ "wait-timeout", ] +[[package]] +name = "ruzstd" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fad02996bfc73da3e301efe90b1837be9ed8f4a462b6ed410aa35d00381de89f" +dependencies = [ + "twox-hash", +] + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + [[package]] name = "serde_core" version = "1.0.228" @@ -881,6 +1371,34 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_json" +version = "1.0.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_spanned" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +dependencies = [ + "serde", +] + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + [[package]] name = "signal-hook" version = "0.4.4" @@ -901,6 +1419,12 @@ dependencies = [ "libc", ] +[[package]] +name = "simd-adler32" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" + [[package]] name = "slab" version = "0.4.12" @@ -913,6 +1437,17 @@ version = "1.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" +[[package]] +name = "smart-default" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eb01866308440fc64d6c44d9e86c5cc17adfe33c4d6eed55da9145044d0ffc1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "socket2" version = "0.6.4" @@ -923,6 +1458,18 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "syn" version = "2.0.118" @@ -953,13 +1500,39 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "termtree" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683" + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + [[package]] name = "thiserror" version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" dependencies = [ - "thiserror-impl", + "thiserror-impl 2.0.18", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -1038,6 +1611,40 @@ dependencies = [ "vsock", ] +[[package]] +name = "toml" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + [[package]] name = "tonic" version = "0.14.6" @@ -1174,6 +1781,16 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "twox-hash" +version = "1.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" +dependencies = [ + "cfg-if", + "static_assertions", +] + [[package]] name = "unarray" version = "0.1.4" @@ -1192,6 +1809,24 @@ version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" +[[package]] +name = "unicode-segmentation" +version = "1.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + [[package]] name = "vsock" version = "0.5.4" @@ -1250,12 +1885,30 @@ dependencies = [ "windows-link", ] +[[package]] +name = "winnow" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" +dependencies = [ + "memchr", +] + [[package]] name = "wit-bindgen" version = "0.57.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" +[[package]] +name = "xtask" +version = "0.0.0" +dependencies = [ + "clap", + "hyperrs-elfsum", + "object", +] + [[package]] name = "zerocopy" version = "0.8.52" @@ -1275,3 +1928,9 @@ dependencies = [ "quote", "syn", ] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/native/Cargo.toml b/native/Cargo.toml new file mode 100644 index 00000000..efa46d20 --- /dev/null +++ b/native/Cargo.toml @@ -0,0 +1,14 @@ +[workspace] +resolver = "2" +members = ["guest-agent", "hyperrs-elfsum", "suidhelper", "suidhelper/xtask"] +default-members = ["guest-agent", "hyperrs-elfsum", "suidhelper"] + +[profile.release] +strip = true +opt-level = "z" + +[profile.release-guest] +inherits = "release" +lto = true +codegen-units = 1 +panic = "abort" diff --git a/native/guest-agent/.gitignore b/native/guest-agent/.gitignore index 6b3e3929..bb5d957d 100644 --- a/native/guest-agent/.gitignore +++ b/native/guest-agent/.gitignore @@ -1,2 +1 @@ -/target *.profraw diff --git a/native/guest-agent/Cargo.toml b/native/guest-agent/Cargo.toml index a1d38c78..43e067e8 100644 --- a/native/guest-agent/Cargo.toml +++ b/native/guest-agent/Cargo.toml @@ -37,10 +37,3 @@ path = "tests/exec.rs" [[test]] name = "init" path = "tests/init.rs" - -[profile.release] -opt-level = "z" -lto = true -codegen-units = 1 -panic = "abort" -strip = true diff --git a/native/hyperrs-elfsum/Cargo.toml b/native/hyperrs-elfsum/Cargo.toml new file mode 100644 index 00000000..b409d399 --- /dev/null +++ b/native/hyperrs-elfsum/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "hyperrs-elfsum" +version = "0.1.0" +edition = "2021" +license = "MIT" + +[features] +stamp = ["dep:blake3", "dep:object", "dep:thiserror"] + +[dependencies] +blake3 = { version = "1", optional = true } +object = { version = "0.36", optional = true } +thiserror = { version = "1", optional = true } + +[dev-dependencies] +blake3 = "1" +object = { version = "0.36", features = ["write"] } +proptest = "1" diff --git a/native/hyperrs-elfsum/src/lib.rs b/native/hyperrs-elfsum/src/lib.rs new file mode 100644 index 00000000..d8acf83d --- /dev/null +++ b/native/hyperrs-elfsum/src/lib.rs @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: MIT + +#[cfg(feature = "stamp")] +mod stamp; + +#[cfg(feature = "stamp")] +pub use stamp::{Elf, Error}; + +const LEN: usize = 32; + +pub type Digest = [u8; LEN]; + +#[repr(transparent)] +pub struct Slot(Digest); + +impl Slot { + #[doc(hidden)] + pub const fn zeroed() -> Self { + Self([0; LEN]) + } + + pub fn get(&self) -> Digest { + unsafe { std::ptr::read_volatile(&self.0) } + } + + pub fn hex(&self) -> String { + self.get().iter().map(|b| format!("{b:02x}")).collect() + } +} + +#[macro_export] +macro_rules! checksum { + () => {{ + #[link_section = ".note.sum"] + #[used] + static SLOT: $crate::Slot = $crate::Slot::zeroed(); + &SLOT + }}; +} + +#[cfg(test)] +mod tests { + use super::*; + use proptest::prelude::*; + + proptest! { + #[test] + fn hex_round_trips_to_lowercase(digest in any::()) { + let rendered = Slot(digest).hex(); + prop_assert_eq!(rendered.len(), 2 * LEN); + prop_assert!(rendered.bytes().all(|b| matches!(b, b'0'..=b'9' | b'a'..=b'f'))); + + let decoded: Vec = (0..LEN) + .map(|i| u8::from_str_radix(&rendered[2 * i..2 * i + 2], 16).unwrap()) + .collect(); + prop_assert_eq!(decoded.as_slice(), digest.as_slice()); + } + } + + #[cfg(all(target_os = "linux", feature = "stamp"))] + #[test] + fn checksum_macro_lands_in_the_stamped_section() { + let slot: &'static Slot = crate::checksum!(); + + let exe = std::fs::read("/proc/self/exe").unwrap(); + let elf = Elf::read(exe).expect("macro must emit SECTION with LEN bytes"); + assert_eq!(*elf.stored(), slot.get()); + } +} diff --git a/native/hyperrs-elfsum/src/stamp.rs b/native/hyperrs-elfsum/src/stamp.rs new file mode 100644 index 00000000..07d92b35 --- /dev/null +++ b/native/hyperrs-elfsum/src/stamp.rs @@ -0,0 +1,180 @@ +// SPDX-License-Identifier: MIT + +use object::{Object, ObjectSection}; + +use crate::{Digest, LEN}; + +const SECTION: &str = ".note.sum"; + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("not a parseable object file: {0}")] + Parse(#[from] object::Error), + #[error("{SECTION} section not found")] + MissingSection, + #[error("{SECTION} section has no file range")] + NoFileRange, + #[error("{SECTION} section is {0} bytes, expected {LEN}")] + WrongSize(u64), +} + +pub struct Elf { + bytes: B, + slot: usize, +} + +impl> Elf { + pub fn read(bytes: B) -> Result { + let slot = slot_offset(bytes.as_ref())?; + Ok(Self { bytes, slot }) + } + + fn parts(&self) -> (&[u8], &Digest, &[u8]) { + let (before, rest) = self.bytes.as_ref().split_at(self.slot); + let (slot, after) = rest.split_first_chunk().expect("read validated the slot"); + (before, slot, after) + } + + #[cfg(test)] + pub(crate) fn stored(&self) -> &Digest { + self.parts().1 + } + + fn digest(&self) -> Digest { + let (before, _, after) = self.parts(); + let mut hasher = blake3::Hasher::new(); + hasher.update(before); + hasher.update(&[0; LEN]); + hasher.update(after); + *hasher.finalize().as_bytes() + } +} + +impl + AsMut<[u8]>> Elf { + pub fn stamp(&mut self) -> Digest { + let digest = self.digest(); + let slot = self.slot; + self.bytes.as_mut()[slot..slot + LEN].copy_from_slice(&digest); + digest + } +} + +fn slot_offset(elf: &[u8]) -> Result { + let file = object::File::parse(elf)?; + let section = file.section_by_name(SECTION).ok_or(Error::MissingSection)?; + <&Digest>::try_from(section.data()?).map_err(|_| Error::WrongSize(section.size()))?; + let (offset, _) = section.file_range().ok_or(Error::NoFileRange)?; + Ok(usize::try_from(offset).expect("data() bounds-checked the section against the file")) +} + +#[cfg(test)] +mod tests { + use object::write::Object as Builder; + use object::{Architecture, BinaryFormat, Endianness, SectionKind}; + use proptest::prelude::*; + use proptest::sample::Index; + + use super::*; + + fn image(before: &[u8], slot: &[u8], after: &[u8]) -> Vec { + let mut obj = Builder::new(BinaryFormat::Elf, Architecture::X86_64, Endianness::Little); + for (name, data, kind) in [ + (&b".data.before"[..], before, SectionKind::Data), + (SECTION.as_bytes(), slot, SectionKind::Note), + (&b".data.after"[..], after, SectionKind::Data), + ] { + let id = obj.add_section(Vec::new(), name.to_vec(), kind); + obj.append_section_data(id, data, 1); + } + obj.write().unwrap() + } + + fn any_image() -> impl Strategy> { + ( + prop::collection::vec(any::(), 0..256), + any::(), + prop::collection::vec(any::(), 0..256), + ) + .prop_map(|(before, slot, after)| image(&before, &slot, &after)) + } + + proptest! { + #[test] + fn stamp_stores_the_digest_of_the_stamped_image(bytes in any_image()) { + let mut elf = Elf::read(bytes).unwrap(); + let digest = elf.stamp(); + prop_assert_eq!(*elf.stored(), digest); + prop_assert_eq!(elf.digest(), digest); + } + + #[test] + fn stamp_is_idempotent(bytes in any_image()) { + let mut elf = Elf::read(bytes).unwrap(); + elf.stamp(); + let once = elf.bytes.clone(); + elf.stamp(); + prop_assert_eq!(elf.bytes, once); + } + + #[test] + fn digest_equals_hash_of_zeroed_image(bytes in any_image()) { + let elf = Elf::read(&bytes[..]).unwrap(); + let mut zeroed = bytes.clone(); + zeroed[elf.slot..elf.slot + LEN].fill(0); + prop_assert_eq!(elf.digest(), *blake3::hash(&zeroed).as_bytes()); + } + + #[test] + fn digest_ignores_slot_contents( + before in prop::collection::vec(any::(), 0..256), + after in prop::collection::vec(any::(), 0..256), + a in any::(), + b in any::(), + ) { + let a = Elf::read(image(&before, &a, &after)).unwrap(); + let b = Elf::read(image(&before, &b, &after)).unwrap(); + prop_assert_eq!(a.digest(), b.digest()); + } + + #[test] + fn tampering_outside_the_slot_breaks_the_stamp( + bytes in any_image(), + at in any::(), + flip in 1..=u8::MAX, + ) { + let mut elf = Elf::read(bytes).unwrap(); + elf.stamp(); + let (slot, mut bytes) = (elf.slot, elf.bytes); + let mut i = at.index(bytes.len() - LEN); + if i >= slot { + i += LEN; + } + bytes[i] ^= flip; + let still_matches = Elf::read(&bytes[..]).map(|elf| elf.digest() == *elf.stored()); + prop_assert!(!matches!(still_matches, Ok(true))); + } + + #[test] + fn rejects_any_slot_that_is_not_digest_sized(len in (0..128usize).prop_filter("not LEN", |&n| n != LEN)) { + let bytes = image(b"before", &vec![0; len], b"after"); + prop_assert!(matches!(Elf::read(bytes), Err(Error::WrongSize(n)) if n == len as u64)); + } + } + + #[test] + fn rejects_an_image_without_the_section() { + let mut obj = Builder::new(BinaryFormat::Elf, Architecture::X86_64, Endianness::Little); + let id = obj.add_section(Vec::new(), b".data".to_vec(), SectionKind::Data); + obj.append_section_data(id, b"payload", 1); + + assert!(matches!( + Elf::read(obj.write().unwrap()), + Err(Error::MissingSection) + )); + } + + #[test] + fn rejects_bytes_that_are_not_an_object_file() { + assert!(matches!(Elf::read(b"not an elf"), Err(Error::Parse(_)))); + } +} diff --git a/native/suidhelper/rust-toolchain.toml b/native/rust-toolchain.toml similarity index 100% rename from native/suidhelper/rust-toolchain.toml rename to native/rust-toolchain.toml diff --git a/native/suidhelper/.gitignore b/native/suidhelper/.gitignore index 34721db3..0e2baff8 100644 --- a/native/suidhelper/.gitignore +++ b/native/suidhelper/.gitignore @@ -1,4 +1,3 @@ -/target /lcov.info # LLVM source-based coverage drops these at the crate root during instrumented # test runs (see the xtask coverage pipeline); they are never committed. diff --git a/native/suidhelper/Cargo.lock b/native/suidhelper/Cargo.lock deleted file mode 100644 index 3612cb67..00000000 --- a/native/suidhelper/Cargo.lock +++ /dev/null @@ -1,1107 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 4 - -[[package]] -name = "adler2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" - -[[package]] -name = "aho-corasick" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba" -dependencies = [ - "memchr", -] - -[[package]] -name = "anstream" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "is_terminal_polyfill", - "utf8parse", -] - -[[package]] -name = "anstyle" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" - -[[package]] -name = "anstyle-parse" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" -dependencies = [ - "windows-sys", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" -dependencies = [ - "anstyle", - "once_cell_polyfill", - "windows-sys", -] - -[[package]] -name = "arrayref" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" - -[[package]] -name = "arrayvec" -version = "0.7.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f02882884d3e1bc524fb12c79f107f6ad0e1cfd498c536ffb494301740995dfe" - -[[package]] -name = "assert_cmd" -version = "2.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aa3a22042e45de04255c7bf3626e239f450200fd0493c1e382263544b20aea6" -dependencies = [ - "anstyle", - "bstr", - "libc", - "predicates", - "predicates-core", - "predicates-tree", - "wait-timeout", -] - -[[package]] -name = "autocfg" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" - -[[package]] -name = "bit-set" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" -dependencies = [ - "bit-vec", -] - -[[package]] -name = "bit-vec" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" - -[[package]] -name = "bitflags" -version = "2.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" - -[[package]] -name = "blake3" -version = "1.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0aa83c34e62843d924f905e0f5c866eb1dd6545fc4d719e803d9ba6030371fce" -dependencies = [ - "arrayref", - "arrayvec", - "cc", - "cfg-if", - "constant_time_eq", - "cpufeatures", -] - -[[package]] -name = "bon" -version = "3.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a602c73c7b0148ec6d12af6fd5cc7a46e2eacc8878271a999abac56eed12f561" -dependencies = [ - "bon-macros", - "rustversion", -] - -[[package]] -name = "bon-macros" -version = "3.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dee98b0db6a962de883bf5d20362dee4d7ca0d12fe39a7c6c73c844e1cd7c1f" -dependencies = [ - "darling", - "ident_case", - "prettyplease", - "proc-macro2", - "quote", - "rustversion", - "syn", -] - -[[package]] -name = "bstr" -version = "1.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bb31b46c14244e20ee9984b11bf5c992b91fb6939fea616e3512c8baecdbe5f" -dependencies = [ - "memchr", - "regex-automata", - "serde_core", -] - -[[package]] -name = "cc" -version = "1.2.65" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e228eec9be7c17ccb640b59b36a5cd805ea2a564a4c5e162c2f659fea30d3b96" -dependencies = [ - "find-msvc-tools", - "shlex", -] - -[[package]] -name = "cfg-if" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" - -[[package]] -name = "cfg_aliases" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" - -[[package]] -name = "clap" -version = "4.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" -dependencies = [ - "clap_builder", - "clap_derive", -] - -[[package]] -name = "clap_builder" -version = "4.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" -dependencies = [ - "anstream", - "anstyle", - "clap_lex", - "strsim", -] - -[[package]] -name = "clap_derive" -version = "4.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "clap_lex" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" - -[[package]] -name = "colorchoice" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" - -[[package]] -name = "constant_time_eq" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b" - -[[package]] -name = "convert_case" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "cpufeatures" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" -dependencies = [ - "libc", -] - -[[package]] -name = "crc32fast" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "darling" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" -dependencies = [ - "darling_core", - "darling_macro", -] - -[[package]] -name = "darling_core" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" -dependencies = [ - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn", -] - -[[package]] -name = "darling_macro" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" -dependencies = [ - "darling_core", - "quote", - "syn", -] - -[[package]] -name = "derive_more" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" -dependencies = [ - "derive_more-impl", -] - -[[package]] -name = "derive_more-impl" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" -dependencies = [ - "convert_case", - "proc-macro2", - "quote", - "rustc_version", - "syn", - "unicode-xid", -] - -[[package]] -name = "difflib" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" - -[[package]] -name = "equivalent" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" - -[[package]] -name = "errno" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" -dependencies = [ - "libc", - "windows-sys", -] - -[[package]] -name = "fastrand" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" - -[[package]] -name = "find-msvc-tools" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" - -[[package]] -name = "flate2" -version = "1.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" -dependencies = [ - "crc32fast", - "miniz_oxide", -] - -[[package]] -name = "float-cmp" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b09cf3155332e944990140d967ff5eceb70df778b34f77d8075db46e4704e6d8" -dependencies = [ - "num-traits", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "getrandom" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" -dependencies = [ - "cfg-if", - "libc", - "r-efi 5.3.0", - "wasip2", -] - -[[package]] -name = "getrandom" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" -dependencies = [ - "cfg-if", - "libc", - "r-efi 6.0.0", -] - -[[package]] -name = "hashbrown" -version = "0.17.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" - -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - -[[package]] -name = "hyper-suidhelper" -version = "0.1.0" -dependencies = [ - "assert_cmd", - "bon", - "clap", - "derive_more", - "hyper-suidhelper-meta", - "nix", - "predicates", - "proptest", - "quick-xml", - "ref-cast", - "serde", - "serde_json", - "smart-default", - "tempfile", - "thiserror", - "toml", -] - -[[package]] -name = "hyper-suidhelper-meta" -version = "0.1.0" - -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - -[[package]] -name = "indexmap" -version = "2.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" -dependencies = [ - "equivalent", - "hashbrown", -] - -[[package]] -name = "is_terminal_polyfill" -version = "1.70.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" - -[[package]] -name = "itoa" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" - -[[package]] -name = "libc" -version = "0.2.186" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" - -[[package]] -name = "linux-raw-sys" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" - -[[package]] -name = "memchr" -version = "2.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" - -[[package]] -name = "miniz_oxide" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" -dependencies = [ - "adler2", - "simd-adler32", -] - -[[package]] -name = "nix" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" -dependencies = [ - "bitflags", - "cfg-if", - "cfg_aliases", - "libc", -] - -[[package]] -name = "normalize-line-endings" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" - -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", -] - -[[package]] -name = "object" -version = "0.36.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" -dependencies = [ - "flate2", - "memchr", - "ruzstd", -] - -[[package]] -name = "once_cell" -version = "1.21.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" - -[[package]] -name = "once_cell_polyfill" -version = "1.70.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" - -[[package]] -name = "ppv-lite86" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" -dependencies = [ - "zerocopy", -] - -[[package]] -name = "predicates" -version = "3.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ada8f2932f28a27ee7b70dd6c1c39ea0675c55a36879ab92f3a715eaa1e63cfe" -dependencies = [ - "anstyle", - "difflib", - "float-cmp", - "normalize-line-endings", - "predicates-core", - "regex", -] - -[[package]] -name = "predicates-core" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cad38746f3166b4031b1a0d39ad9f954dd291e7854fcc0eed52ee41a0b50d144" - -[[package]] -name = "predicates-tree" -version = "1.0.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0de1b847b39c8131db0467e9df1ff60e6d0562ab8e9a16e568ad0fdb372e2f2" -dependencies = [ - "predicates-core", - "termtree", -] - -[[package]] -name = "prettyplease" -version = "0.2.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" -dependencies = [ - "proc-macro2", - "syn", -] - -[[package]] -name = "proc-macro2" -version = "1.0.106" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "proptest" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744" -dependencies = [ - "bit-set", - "bit-vec", - "bitflags", - "num-traits", - "rand", - "rand_chacha", - "rand_xorshift", - "regex-syntax", - "rusty-fork", - "tempfile", - "unarray", -] - -[[package]] -name = "quick-error" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" - -[[package]] -name = "quick-xml" -version = "0.37.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb" -dependencies = [ - "memchr", - "serde", -] - -[[package]] -name = "quote" -version = "1.0.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "r-efi" -version = "5.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" - -[[package]] -name = "r-efi" -version = "6.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" - -[[package]] -name = "rand" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" -dependencies = [ - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" -dependencies = [ - "getrandom 0.3.4", -] - -[[package]] -name = "rand_xorshift" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" -dependencies = [ - "rand_core", -] - -[[package]] -name = "ref-cast" -version = "1.0.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" -dependencies = [ - "ref-cast-impl", -] - -[[package]] -name = "ref-cast-impl" -version = "1.0.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "regex" -version = "1.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" - -[[package]] -name = "rustc_version" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" -dependencies = [ - "semver", -] - -[[package]] -name = "rustix" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" -dependencies = [ - "bitflags", - "errno", - "libc", - "linux-raw-sys", - "windows-sys", -] - -[[package]] -name = "rustversion" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" - -[[package]] -name = "rusty-fork" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc6bf79ff24e648f6da1f8d1f011e9cac26491b619e6b9280f2b47f1774e6ee2" -dependencies = [ - "fnv", - "quick-error", - "tempfile", - "wait-timeout", -] - -[[package]] -name = "ruzstd" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fad02996bfc73da3e301efe90b1837be9ed8f4a462b6ed410aa35d00381de89f" -dependencies = [ - "twox-hash", -] - -[[package]] -name = "semver" -version = "1.0.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" - -[[package]] -name = "serde" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" -dependencies = [ - "serde_core", - "serde_derive", -] - -[[package]] -name = "serde_core" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_json" -version = "1.0.150" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" -dependencies = [ - "itoa", - "memchr", - "serde", - "serde_core", - "zmij", -] - -[[package]] -name = "serde_spanned" -version = "0.6.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" -dependencies = [ - "serde", -] - -[[package]] -name = "shlex" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" - -[[package]] -name = "simd-adler32" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" - -[[package]] -name = "smart-default" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eb01866308440fc64d6c44d9e86c5cc17adfe33c4d6eed55da9145044d0ffc1" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "strsim" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" - -[[package]] -name = "syn" -version = "2.0.118" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "tempfile" -version = "3.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" -dependencies = [ - "fastrand", - "getrandom 0.4.3", - "once_cell", - "rustix", - "windows-sys", -] - -[[package]] -name = "termtree" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683" - -[[package]] -name = "thiserror" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "toml" -version = "0.8.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit", -] - -[[package]] -name = "toml_datetime" -version = "0.6.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" -dependencies = [ - "serde", -] - -[[package]] -name = "toml_edit" -version = "0.22.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" -dependencies = [ - "indexmap", - "serde", - "serde_spanned", - "toml_datetime", - "winnow", -] - -[[package]] -name = "twox-hash" -version = "1.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" -dependencies = [ - "cfg-if", - "static_assertions", -] - -[[package]] -name = "unarray" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" - -[[package]] -name = "unicode-ident" -version = "1.0.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" - -[[package]] -name = "unicode-segmentation" -version = "1.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" - -[[package]] -name = "unicode-xid" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" - -[[package]] -name = "utf8parse" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" - -[[package]] -name = "wait-timeout" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11" -dependencies = [ - "libc", -] - -[[package]] -name = "wasip2" -version = "1.0.4+wasi-0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" -dependencies = [ - "wit-bindgen", -] - -[[package]] -name = "windows-link" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" - -[[package]] -name = "windows-sys" -version = "0.61.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" -dependencies = [ - "windows-link", -] - -[[package]] -name = "winnow" -version = "0.7.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" -dependencies = [ - "memchr", -] - -[[package]] -name = "wit-bindgen" -version = "0.57.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" - -[[package]] -name = "xtask" -version = "0.0.0" -dependencies = [ - "blake3", - "clap", - "hyper-suidhelper-meta", - "object", -] - -[[package]] -name = "zerocopy" -version = "0.8.52" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce1022995ff5ff5d841ad7d994facc23098cd40152f2c1d11cd607c6f530653f" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.8.52" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "zmij" -version = "1.0.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/native/suidhelper/Cargo.toml b/native/suidhelper/Cargo.toml index 7167ccf4..b929efc6 100644 --- a/native/suidhelper/Cargo.toml +++ b/native/suidhelper/Cargo.toml @@ -1,9 +1,3 @@ -[workspace] -members = ["meta", "xtask"] -# Plain `cargo build`/`cargo test` only touch the helper itself; xtask is a -# build tool, built on demand via the `cargo xtask` alias. -default-members = ["."] - [package] name = "hyper-suidhelper" version = "0.1.0" @@ -124,7 +118,7 @@ redundant_field_names = "allow" bon = "3.9.3" clap = { version = "4", features = ["derive"] } derive_more = { version = "2.1.1", features = ["display", "as_ref", "constructor", "from", "into", "add", "debug"] } -hyper-suidhelper-meta = { path = "meta" } +hyperrs-elfsum = { path = "../hyperrs-elfsum" } nix = { version = "0.29", features = ["user", "fs", "dir", "process", "ioctl", "poll"] } quick-xml = { version = "0.37", features = ["serialize"] } ref-cast = "1.0.25" @@ -139,7 +133,3 @@ assert_cmd = "2.2.2" predicates = "3.1.4" proptest = "1" tempfile = "3" - -[profile.release] -strip = true -opt-level = "z" diff --git a/native/suidhelper/README.md b/native/suidhelper/README.md index 67c734a5..4bf9368e 100644 --- a/native/suidhelper/README.md +++ b/native/suidhelper/README.md @@ -7,7 +7,7 @@ root wholesale. ## Build & install Static **musl** binaries for both server arches. The toolchain and targets are -pinned in `rust-toolchain.toml` (nightly + both musl targets), and aarch64 +pinned in `native/rust-toolchain.toml` (nightly + both musl targets), and aarch64 cross-links via the toolchain's bundled `rust-lld` — no external cross toolchain is required: @@ -22,7 +22,7 @@ one matching the host, setuid root: ```sh sudo install -o root -g root -m 4755 \ - target/x86_64-unknown-linux-musl/release/hyper-suidhelper /usr/local/bin/hyper-suidhelper + ../target/x86_64-unknown-linux-musl/release/hyper-suidhelper /usr/local/bin/hyper-suidhelper ``` Then point Hyper at it (runtime config). The tool paths must be absolute (the diff --git a/native/suidhelper/meta/Cargo.toml b/native/suidhelper/meta/Cargo.toml deleted file mode 100644 index fc180436..00000000 --- a/native/suidhelper/meta/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "hyper-suidhelper-meta" -version = "0.1.0" -edition = "2021" -license = "MIT" -description = "Shared constants for the hyper-suidhelper checksum section (binary + xtask)." - -[dependencies] diff --git a/native/suidhelper/meta/src/lib.rs b/native/suidhelper/meta/src/lib.rs deleted file mode 100644 index 8e23306b..00000000 --- a/native/suidhelper/meta/src/lib.rs +++ /dev/null @@ -1,14 +0,0 @@ -//! Constants shared between the suidhelper binary, which declares the checksum -//! section, and xtask, which stamps it. Kept in a tiny stable leaf crate so -//! neither side has to depend on the other (the binary is a nightly crate). -//! -//! Note: `#[link_section = ...]` requires a string literal, so the binary still -//! repeats the section name in that attribute — [`CHECKSUM_SECTION`] is the -//! shared source of truth for every *non-attribute* use (the xtask lookup, the -//! array length, docs). - -/// ELF section carrying the binary's BLAKE3 self-checksum. -pub const CHECKSUM_SECTION: &str = ".note.sum"; - -/// BLAKE3 digest length, in bytes. -pub const CHECKSUM_LEN: usize = 32; diff --git a/native/suidhelper/src/checksum.rs b/native/suidhelper/src/checksum.rs deleted file mode 100644 index 4dfddb5c..00000000 --- a/native/suidhelper/src/checksum.rs +++ /dev/null @@ -1,26 +0,0 @@ -//! Module storing the checksum of the ELF file. -//! -//! This is populated during the build process by taking the BLAKE3 sum of the -//! ELF file with the checksum zeroed out and placing it in this value. A fresh -//! (unstamped) build leaves it all-zero. -//! -//! The section name and length live in `hyper_suidhelper_meta` so the xtask that -//! stamps this section shares one source of truth. The `#[link_section]` -//! attribute below must repeat the literal — the compiler rejects a `const` -//! there — so it is the one spot that has to track `CHECKSUM_SECTION` by hand. - -use hyper_suidhelper_meta::CHECKSUM_LEN; - -#[link_section = ".note.sum"] -#[used] -static CHECKSUM_BLAKE3: [u8; CHECKSUM_LEN] = [0u8; CHECKSUM_LEN]; - -/// The raw BLAKE3 checksum of this suidhelper build. -pub fn get() -> &'static [u8; CHECKSUM_LEN] { - &CHECKSUM_BLAKE3 -} - -/// The checksum rendered as a lowercase hex string. -pub fn hex() -> String { - get().iter().map(|b| format!("{b:02x}")).collect() -} diff --git a/native/suidhelper/src/lib.rs b/native/suidhelper/src/lib.rs index ec1af487..ebff57f9 100644 --- a/native/suidhelper/src/lib.rs +++ b/native/suidhelper/src/lib.rs @@ -7,7 +7,6 @@ //! exercised by integration tests under `tests/`; `src/main.rs` is a thin entry //! point over these modules. -pub mod checksum; pub mod config; pub mod fs; pub mod insecure_mode; diff --git a/native/suidhelper/src/main.rs b/native/suidhelper/src/main.rs index 300d1dfb..bb5e703c 100644 --- a/native/suidhelper/src/main.rs +++ b/native/suidhelper/src/main.rs @@ -67,7 +67,7 @@ impl Version { fn render() -> Self { Self { version: env!("CARGO_PKG_VERSION"), - checksum_blake3: hyper_suidhelper::checksum::hex(), + checksum_blake3: hyperrs_elfsum::checksum!().hex(), } } } diff --git a/native/suidhelper/xtask/Cargo.toml b/native/suidhelper/xtask/Cargo.toml index ca5273ec..5b8a6aff 100644 --- a/native/suidhelper/xtask/Cargo.toml +++ b/native/suidhelper/xtask/Cargo.toml @@ -5,7 +5,6 @@ edition = "2021" publish = false [dependencies] -blake3 = "1" clap = { version = "4", features = ["derive"] } -hyper-suidhelper-meta = { path = "../meta" } +hyperrs-elfsum = { path = "../../hyperrs-elfsum", features = ["stamp"] } object = "0.36" diff --git a/native/suidhelper/xtask/src/main.rs b/native/suidhelper/xtask/src/main.rs index 78b063b4..786e19fc 100644 --- a/native/suidhelper/xtask/src/main.rs +++ b/native/suidhelper/xtask/src/main.rs @@ -37,10 +37,11 @@ fn main() { } } -/// The workspace `target/` dir (xtask's manifest sits one level below the root). +/// The workspace `target/` dir (xtask's manifest sits two levels below the root). fn target_dir() -> PathBuf { Path::new(env!("CARGO_MANIFEST_DIR")) - .parent() - .expect("xtask has no parent dir") + .ancestors() + .nth(2) + .expect("xtask sits two levels below the workspace root") .join("target") } diff --git a/native/suidhelper/xtask/src/stamp.rs b/native/suidhelper/xtask/src/stamp.rs index 0adfcfe3..2a010a57 100644 --- a/native/suidhelper/xtask/src/stamp.rs +++ b/native/suidhelper/xtask/src/stamp.rs @@ -7,10 +7,8 @@ use std::path::{Path, PathBuf}; -use hyper_suidhelper_meta::{CHECKSUM_LEN, CHECKSUM_SECTION as SECTION}; use object::elf::PT_INTERP; use object::read::elf::ProgramHeader; -use object::{Object, ObjectSection}; use crate::{target_dir, BIN}; @@ -49,19 +47,13 @@ pub fn run() -> PathBuf { built.display() ); - let (offset, size) = section_range(&bytes); - assert_eq!(size, CHECKSUM_LEN, "{SECTION} must be {CHECKSUM_LEN} bytes"); - - // Zero the slot before hashing so the digest is reproducible whether or not - // the binary was already stamped (re-running stamp is idempotent). - bytes[offset..offset + size].fill(0); - let digest = blake3::hash(&bytes); - bytes[offset..offset + size].copy_from_slice(digest.as_bytes()); + hyperrs_elfsum::Elf::read(&mut bytes[..]) + .unwrap_or_else(|err| panic!("cannot stamp {}: {err}", built.display())) + .stamp(); let path = target_dir().join(BIN); std::fs::copy(&built, &path).expect("failed to copy release binary"); std::fs::write(&path, &bytes).expect("failed to write stamped binary"); - println!("stamped {} -> {}", path.display(), digest.to_hex()); path } @@ -92,15 +84,3 @@ fn has_interpreter(bytes: &[u8]) -> bool { .iter() .any(|header| header.p_type(elf.endian()) == PT_INTERP) } - -/// File offset and size of the checksum section in the given ELF image. -fn section_range(bytes: &[u8]) -> (usize, usize) { - let elf = object::File::parse(bytes).expect("failed to parse ELF"); - let section = elf - .section_by_name(SECTION) - .unwrap_or_else(|| panic!("{SECTION} section not found — is checksum.rs linked in?")); - let (offset, size) = section - .file_range() - .expect("checksum section has no file range"); - (offset as usize, size as usize) -}