fix(build): config-parity assertion selected the -dbg package #40

Merged
zach merged 1 commit from fix/config-parity-picks-dbg-deb into main 2026-07-26 00:53:54 +00:00
Owner

The v7.1.5 build failed in the assertion I added in #35, not in anything it was guarding. Its own message named the culprit:

the .config shipped in linux-image-7.1.5-...-amd64-dbg_..._amd64.deb differs
from the captured final.config.

The -dbg package. image_debs globs linux-image-*_${ARCH}.deb, which matches both the concrete image package and the -dbg variant — and because - (0x2D) sorts before _ (0x5F), the -dbg package expands first, so head -n1 picked exactly the wrong one every time. The -dbg package ships no /boot/config-* at all (its payload is under usr/lib/debug/), so the extraction was empty and the comparison could never succeed.

Verified the ordering claim directly:

linux-image-7.1.5-hardened-unredacted-amd64-dbg_7.1.5-hardened1_amd64.deb   <- head -n1 took this
linux-image-7.1.5-hardened-unredacted-amd64_7.1.5-hardened1_amd64.deb

The kernelrelease derivation further down the same file already skips the -dbg variant, with the comment "Skip the -dbg variant." My assertion needed the same skip and didn't have it.

Fix, plus three hardenings the failure prompted

  • Reuse the existing non-dbg selection, and assert a non-dbg image .deb was actually found.
  • Extract to a file and cmp files, rather than piping through a command substitution — substitution strips trailing newlines, and this is a byte-exact comparison.
  • Refuse to compare an empty extraction. cmp on two empty inputs succeeds, so a wrong package selection or a renamed path would have made this assertion pass vacuously — precisely the failure class this whole change set exists to remove, reintroduced by me in the check meant to prevent it. It now fails with "cannot be evaluated".
  • On a real mismatch, print the leading diff, so the next failure says what differs rather than only that something does.

What the failed build did establish

Worth recording, because it validates the riskiest assumption in #35. The two assertions before this one passed on real output:

embedded cert matches certs/unredacted-signing.pem
no .deb contains unredacted-signing.pem

The first is the load-bearing kbuild assumption behind the entire in-tree-key change: that a non-default CONFIG_MODULE_SIG_KEY filename is not regenerated by certs/Makefile's gen_key rule, so the certificate linked into .builtin_trusted_keys is ours. That could not be tested without a real build. It holds. The second confirms no private key reached a package.

So the compile, the key installation, the cert identity and the leak guard are all confirmed good on real output; only my package selection was wrong.

Verification

Tested against hand-built .deb fixtures (an image package with /boot/config-*, a -dbg package with only usr/lib/debug/), using GNU tar to match debian:trixie — macOS bsdtar has no --wildcards, which is why this could not be caught locally before:

case result
correct build, config matches exit 0, names the package it checked
/tmp path in final.config exit 1 — the regression the assertion exists for still fires
only a -dbg package present exit 1, "no non-dbg linux-image .deb"
empty vs empty exit 1, refuses rather than passing

shellcheck and bash -n clean.

After merging

main moves, so the existing v7.1.5-hardened1 tag becomes stale and the guard will reject it. Delete and re-cut:

git switch main && git pull --ff-only && git push origin :refs/tags/v7.1.5-hardened1 && git tag -d v7.1.5-hardened1 && git tag v7.1.5-hardened1 && git push origin v7.1.5-hardened1

🤖 Generated with Claude Code

The v7.1.5 build failed **in the assertion I added in #35**, not in anything it was guarding. Its own message named the culprit: ``` the .config shipped in linux-image-7.1.5-...-amd64-dbg_..._amd64.deb differs from the captured final.config. ``` The **-dbg** package. `image_debs` globs `linux-image-*_${ARCH}.deb`, which matches both the concrete image package and the `-dbg` variant — and because `-` (0x2D) sorts before `_` (0x5F), the `-dbg` package expands **first**, so `head -n1` picked exactly the wrong one every time. The `-dbg` package ships no `/boot/config-*` at all (its payload is under `usr/lib/debug/`), so the extraction was empty and the comparison could never succeed. Verified the ordering claim directly: ``` linux-image-7.1.5-hardened-unredacted-amd64-dbg_7.1.5-hardened1_amd64.deb <- head -n1 took this linux-image-7.1.5-hardened-unredacted-amd64_7.1.5-hardened1_amd64.deb ``` The kernelrelease derivation **further down the same file** already skips the `-dbg` variant, with the comment *"Skip the -dbg variant."* My assertion needed the same skip and didn't have it. ## Fix, plus three hardenings the failure prompted - Reuse the existing non-dbg selection, and assert a non-dbg image `.deb` was actually found. - Extract to a file and `cmp` files, rather than piping through a command substitution — substitution strips trailing newlines, and this is a byte-exact comparison. - **Refuse to compare an empty extraction.** `cmp` on two empty inputs *succeeds*, so a wrong package selection or a renamed path would have made this assertion pass **vacuously** — precisely the failure class this whole change set exists to remove, reintroduced by me in the check meant to prevent it. It now fails with "cannot be evaluated". - On a real mismatch, print the leading diff, so the next failure says *what* differs rather than only that something does. ## What the failed build did establish Worth recording, because it validates the riskiest assumption in #35. The two assertions **before** this one passed on real output: ``` embedded cert matches certs/unredacted-signing.pem no .deb contains unredacted-signing.pem ``` The first is the load-bearing kbuild assumption behind the entire in-tree-key change: that a **non-default** `CONFIG_MODULE_SIG_KEY` filename is not regenerated by `certs/Makefile`'s `gen_key` rule, so the certificate linked into `.builtin_trusted_keys` is ours. That could not be tested without a real build. **It holds.** The second confirms no private key reached a package. So the compile, the key installation, the cert identity and the leak guard are all confirmed good on real output; only my package selection was wrong. ## Verification Tested against hand-built `.deb` fixtures (an image package with `/boot/config-*`, a `-dbg` package with only `usr/lib/debug/`), using **GNU tar** to match `debian:trixie` — macOS `bsdtar` has no `--wildcards`, which is why this could not be caught locally before: | case | result | |---|---| | correct build, config matches | exit 0, names the package it checked | | `/tmp` path in `final.config` | exit 1 — **the regression the assertion exists for still fires** | | only a `-dbg` package present | exit 1, "no non-dbg linux-image .deb" | | empty vs empty | exit 1, refuses rather than passing | `shellcheck` and `bash -n` clean. ## After merging `main` moves, so the existing `v7.1.5-hardened1` tag becomes stale and the guard will reject it. Delete and re-cut: ```bash git switch main && git pull --ff-only && git push origin :refs/tags/v7.1.5-hardened1 && git tag -d v7.1.5-hardened1 && git tag v7.1.5-hardened1 && git push origin v7.1.5-hardened1 ``` 🤖 Generated with [Claude Code](https://claude.com/claude-code)
fix(build): config-parity assertion selected the -dbg package
All checks were successful
validate / shellcheck (pull_request) Successful in 13s
validate / yamllint (pull_request) Successful in 13s
validate / workflow-expressions (pull_request) Successful in 4s
validate / pycompile (pull_request) Successful in 5s
validate / intent-matches-policy (pull_request) Successful in 5s
validate / dep-list-parity (pull_request) Successful in 4s
validate / seed-pins-well-formed (pull_request) Successful in 9s
validate / workflow-shell (pull_request) Successful in 13s
validate / escape-hatches-are-plumbed (pull_request) Successful in 4s
validate / container-digests-pinned (pull_request) Successful in 4s
fe20fdc8c9
The v7.1.5 build failed in the assertion I added, not in anything it was
guarding. Its own message named the culprit:

  the .config shipped in linux-image-7.1.5-...-amd64-dbg_..._amd64.deb differs
  from the captured final.config.

The -dbg package. `image_debs` globs `linux-image-*_${ARCH}.deb`, which matches
both the concrete image package and the -dbg variant, and because '-' (0x2D)
sorts before '_' (0x5F) the -dbg package expands FIRST -- so selecting it with
`head -n1` picked exactly the wrong one every time. The -dbg package ships no
/boot/config-* at all (its payload is under usr/lib/debug/), so the extraction
was empty and the comparison could never succeed.

The kernelrelease derivation further down this same file already skips the -dbg
variant, with the comment "Skip the -dbg variant." My assertion needed the same
skip and did not have it. Fixed by reusing that selection, and asserting a
non-dbg image .deb was actually found.

Two hardenings while here, both prompted by the failure:

  - Extract to a file and cmp files, rather than piping through a command
    substitution: substitution strips trailing newlines and this is a byte-exact
    comparison.
  - Refuse to compare an EMPTY extraction. `cmp` on two empty inputs succeeds,
    so a wrong package selection or a renamed path would have made this
    assertion pass vacuously -- precisely the failure class this change set
    exists to remove, reintroduced by me in the check meant to prevent it. It
    now fails with "cannot be evaluated" instead.
  - On a real mismatch, print the leading diff, so the next failure says WHAT
    differs rather than only that something does.

What the failed build did establish, and worth recording: the two assertions
before this one PASSED on real output --

  embedded cert matches certs/unredacted-signing.pem
  no .deb contains unredacted-signing.pem

The first is the load-bearing kbuild assumption behind the whole in-tree key
change: that a NON-default CONFIG_MODULE_SIG_KEY filename is not regenerated by
certs/Makefile's gen_key rule, so the cert linked into .builtin_trusted_keys is
ours. That could not be tested without a real build, and it holds. The second
confirms no private key reached a package.

Tested against hand-built .deb fixtures (image with /boot/config-*, -dbg with
only usr/lib/debug/) using GNU tar to match debian:trixie, since macOS bsdtar
has no --wildcards: correct build passes; a /tmp path in final.config fails
(the regression the assertion is for); a -dbg-only directory and an
empty-vs-empty comparison both refuse rather than pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
zach merged commit 48a6533188 into main 2026-07-26 00:53:54 +00:00
zach deleted branch fix/config-parity-picks-dbg-deb 2026-07-26 00:53:54 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
unredacted/linux-hardened-unredacted!40
No description provided.