Skip to content

release: 5.7.1 - #1647

Merged
Jaro-c merged 5 commits into
mainfrom
develop
Sep 3, 2026
Merged

Jaro-c merged 5 commits into
mainfrom
develop

Conversation

@Jaro-c

@Jaro-c Jaro-c commented Sep 3, 2026

Copy link
Copy Markdown
Member

Release develop → main for 5.7.1. Merge commit, not squash: main is the released state.

What ships

The arm64 Linux binary and the arm64 .deb are static PIE (#1644, PR #1645). Through 5.7.0 they were static executables at a fixed address: rustc links aarch64-unknown-linux-musl that way by default, and nothing in the release asked otherwise, so every arm64 Linux release shipped without ASLR while the x86_64 one had it. .cargo/config.toml hands that target to rust-lld and build.rs supplies the static-PIE start files rustc ships beside the target; the first attempt, -pie alone, linked and faulted before main on the arm64 runner, which is what the new smoke run is for.

The release verifies the hardening before signing. check-hardening.sh reads static PIE, full RELRO, non-executable stack and no symbol table off the two Linux binaries and the binary inside each .deb; its test builds one control per property. Every build leg runs its binary once. linux-hardening.yml builds both Linux targets on their own runners on a pull request that touches the build configuration; its arm64 leg ran the static PIE on PR #1645.

Tests. up is held to calling its object-name preflight (PR #1643); the five controls a mutation sweep found green without them (PR #1642).

What changed under the hood, no behaviour

Verified before opening this

  • The bump (PR chore(release): bump to 5.7.1 #1646) ran release.yml's three version comparisons locally: crate=5.7.1 lock=5.7.1 deb=5.7.1, GATE OK.
  • The hardening gate, run by hand over the 5.7.0 assets, fails the arm64 pair with pie relro and passes the x86_64 pair. This release is the first the gate runs on.

Signed-off-by: Jaro-c 75870284+Jaro-c@users.noreply.github.com

## Problem

Forty-three jobs across twenty-one workflows had no `timeout-minutes`,
so a hang cost GitHub's six-hour default. The three distribution
channels got their bounds on 2026-09-01 (`standards/ci`, "Every job
carries a `timeout-minutes` tuned from its measured duration"); this
repository had two, both on the Podman lane.

## Measured

The worst of the last three successful runs per job, 2026-09-02, read
off `gh run view --json jobs`. A few that decide the shape:

| job | worst | bound |
|---|---|---|
| `release` build (Windows arm64 leg) | 347s | 60 min |
| `release` build-deb | 438s | 60 min |
| `debian / dpkg-buildpackage` | 432s | 60 min |
| `rust / Test (windows-latest)` | 205s | 35 min |
| `cargo fuzz` smoke legs | 243s | 40 min |
| `audit / cargo audit` | 194s | 30 min |
| `rust / Coverage` | 166s | 30 min |
| the ten-second watchers and gates | 4 to 32s | 10 min |

Every bound is ten times its measurement rounded up, never under ten
minutes, and the measurement is written beside the number in the
workflow. Two jobs had nothing to measure and say so: `package` and
`semver` (skipped while `publish = false`) and the scheduled
`benchmark`.

## Change

`timeout-minutes` on every job that declares `runs-on`. A caller job
that `uses:` a reusable cannot carry one, so the bound lives in the
reusable's own jobs, where the work runs.

## Test plan

Every workflow parses; `workflow_audit_strictness`,
`workflow_toolchain_pin` and `workflow_debian_image` pass; no trailing
whitespace. This pull request touches workflow files, so the lane boots
both legs.

Signed-off-by: Jaro-c <75870284+Jaro-c@users.noreply.github.com>

Signed-off-by: Jaro-c <75870284+Jaro-c@users.noreply.github.com>
…em (#1642)

## What I measured

`cargo mutants` (27.1.0) over the security-relevant modules:
`internal/update`, `internal/libpod/validate.rs`,
`internal/engine/{secrets,copy,copy.rs,names.rs,staging.rs,container/security.rs}`,
`internal/quadlet/unit`. Test command: the unit and binary suites plus
`secret_safety`, `project_name_safety`, `quadlet`,
`keyring_bootstrap_order`, `cli_flags`.

| | mutants |
|---|---|
| tested | 154 |
| caught | 117 |
| unviable | 4 |
| survived | 33 |

The 33, read one by one: 21 in `copy.rs`, whose behaviour the
integration suite exercises against a real Podman (a second pass with
that suite is running); 3 on Windows-only lines of `staging_base` that
Linux never compiles; 2 equivalent mutants (`|` to `^` over the disjoint
setuid/setgid/sticky bits); 2 `Engine` wrappers reachable only through a
live engine (`Engine::validate_object_names`, `pre_validate_spec`); and
**5 gaps**.

## The five, and the tests that close them

- `is_safe_project_name`: the suite refused 129 characters and never
accepted 128, so `> 128` could become `>= 128` with every test green.
One case asserts 128 in and 129 out.
- `staging_base` (three mutants: the absolute-path guard forced to
`true`, forced to `false`, and the function returning a default path):
no test set `XDG_RUNTIME_DIR`. `staging_base_tests.rs` pins the
decision: an absolute private runtime dir is used and the base is
created `0700` under it; a relative value and an unset value fall back
to the per-uid temp dir; a group- or world-accessible runtime dir is
refused for that reason and nothing is created under it. The environment
is process-wide, so the cases take one lock and restore the variable on
drop.
- `allowed_namespace_modes`: deleting the `!` in the separator logic
emitted `, ` before the first item and every `contains` stayed green.
The list is now asserted to start with `one of ` and not with `one of
,`, and to carry a separator between items.

## `copy.rs`, second pass

A second pass ran the six cp integration tests against a real Podman
5.7.0 over `copy.rs` (40 mutants, 17 caught by those alone). Intersected
with the unit pass, ten mutants were reached by no local test: three in
`put_archive_verified`'s verification path, which only runs when libpod
closes the archive PUT without a response, the Podman 6 behaviour the
lane's Podman 6 leg exercises and a Podman 5 host cannot; four on
`cfg(windows)` lines Linux never compiles (`rust / Test
(windows-latest)` is where those run); and the three `CpOptions`
builders, which nothing in the tree called. Two more, the `*` in the
one-gibibyte archive cap, survived both passes because every copy in the
suite was a few bytes.

The second commit covers the last five: a unit test reads each builder's
field (the type derives `Clone`, `Debug` and `PartialEq` for it,
additive), and an integration test copies a two-mebibyte file out of a
container and asserts the size, which a cap shrunk to about one mebibyte
refuses.

## Test plan

- `cargo test --lib staging` 17 passed (4 new), `--lib validate` 66
passed; `cargo clippy --all-targets --features test-helpers -D warnings`
and `cargo fmt --check` clean.
- Re-run of `cargo mutants` on `staging.rs`, `validate.rs`, `names.rs`
with the new tests: 106 mutants, 97 caught, 2 unviable, 7 survived, and
each of the seven is one of the four kinds above (three Windows-only,
two equivalent, two wrappers).

Signed-off-by: Jaro-c <75870284+Jaro-c@users.noreply.github.com>

---------

Signed-off-by: Jaro-c <75870284+Jaro-c@users.noreply.github.com>
…1643)

## What

One integration case: `up -d` with a volume whose `name:` is `"bad
name"` is refused by podup's own preflight (`invalid volume name "bad
name"`), with no HTTP 500 in the output.

## Why

The validator is unit-tested; the wiring was not. The mutation sweep
behind #1642 replaced `Engine::validate_object_names` with `Ok(())` and
every suite stayed green, including the integration suite: nothing had
asked whether `up` calls it. The comment on the call site says the
preflight exists so a bad name surfaces as a clear client-side error
with nothing created; this case holds `up` to that.

A volume, not a container. My first draft used `container_name: "bad
name"` and the mutants survived it: container names are checked a second
time at create (`container/mod.rs`, for the `run --name` path), so a bad
container name is refused with the preflight gone. Volumes and networks
are checked only by the preflight.

## Test plan

Green against Podman 5.7.0. `cargo mutants` on `names.rs` with this case
as the test command: both `validate_object_names` mutants caught (the
run before it reported both missed).

Signed-off-by: Jaro-c <75870284+Jaro-c@users.noreply.github.com>

Signed-off-by: Jaro-c <75870284+Jaro-c@users.noreply.github.com>
…e signing (#1645)

## What I found

Measured on the 5.7.0 assets with `readelf` (#1644):
`podup-linux-x86_64` is a static PIE with full RELRO, NX stack and no
symbol table. `podup-linux-arm64` and the binary inside
`podup_5.7.0_arm64.deb` are plain static executables (`Type: EXEC`, no
dynamic section) at a fixed address. rustc links
`aarch64-unknown-linux-musl` that way by default (the target spec lacks
the static-PIE flag that `x86_64-unknown-linux-musl` has), so every
arm64 Linux release so far shipped without ASLR, and nothing in the
release asked otherwise. I had measured only x86_64 when I opened the
issue and wrote "both"; the script caught the arm64 binary the first
time I ran it over the real assets.

## What changes

- `.cargo/config.toml` links the aarch64 musl target with `rust-lld` and
`-pie`. Verified locally on a hello crate with rustc 1.97: `Type: DYN`,
`FLAGS_1: NOW PIE`, `BIND_NOW`, no `PT_INTERP`. I cannot build podup
itself for aarch64 on this machine (`ring` needs an aarch64 musl C
compiler), which is what the new job is for.
- `.github/scripts/check-hardening.sh` reads static PIE, full RELRO, NX
stack and stripped off an ELF. `tests/shell/check-hardening.test.sh`
builds one control per property with `gcc`, one linker flag away from a
good one, and asserts each is failed for its own property (19 cases).
Deleting the stripped check from the script turns two of them red.
- The `release` job runs the script over the two Linux binaries and the
binary inside each `.deb` before anything is signed. Run by hand over
the 5.7.0 assets: x86_64 and the amd64 deb pass, arm64 and the arm64 deb
fail with `pie relro`.
- Every build leg runs its binary once (`--version`), since each builds
on the architecture it ships for.
- `linux-hardening.yml`: on a pull request touching the build
configuration, builds both Linux targets on `ubuntu-latest` and
`ubuntu-24.04-arm`, runs the binary and reads the same properties. This
PR is its first run and the arm64 leg is the proof the config works on
the real binary.
- `docs/threat-model.md` carries the row with the measurement, including
that arm64 shipped without ASLR through 5.7.0.

## Test plan

- `bash tests/shell/check-hardening.test.sh`: 19 passed. Sabotage
control: with the `.symtab` check replaced by `if false`, `unstripped
fails` and `unstripped is failed for 'stripped'` go red.
- `ci-runs-every-test` passes with the new test wired in
`lint-shell.yml`.
- The `aarch64-unknown-linux-musl` leg of `linux-hardening` on this PR
must be green: that is the only place the full arm64 binary gets built
and run before the release does it.

Signed-off-by: Jaro-c <75870284+Jaro-c@users.noreply.github.com>

---------

Signed-off-by: Jaro-c <75870284+Jaro-c@users.noreply.github.com>
Bump for the patch release carrying #1645: the arm64 Linux binary and
the arm64 `.deb` become static PIE, and the release verifies the
hardening of every Linux binary before signing.

## Verified

`release.yml`'s three version comparisons run locally: `crate=5.7.1
lock=5.7.1 deb=5.7.1`, GATE OK; `cargo metadata --locked` accepts the
lockfile.

Signed-off-by: Jaro-c <75870284+Jaro-c@users.noreply.github.com>

Signed-off-by: Jaro-c <75870284+Jaro-c@users.noreply.github.com>
@Jaro-c Jaro-c added prio:P2 Medium priority effort:XS Extra small status:review In review type:chore Maintenance with no product impact release area:release Subsystem: release labels Sep 3, 2026
@Jaro-c
Jaro-c merged commit 4f3edcd into main Sep 3, 2026
78 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:release Subsystem: release effort:XS Extra small prio:P2 Medium priority release status:review In review type:chore Maintenance with no product impact

Development

Successfully merging this pull request may close these issues.

1 participant