Kernel release 6.18.38-rt-cuckoo+: reproducible build, guarded installer

Rebuilt with KBUILD_BUILD_USER/HOST/TIMESTAMP pinned. Without them the kernel
bakes the builder's user@hostname and wall-clock build time into /proc/version,
which publishes operator identity in a public artifact and makes the build
unreproducible. Pinned, anyone can rebuild and diff the bytes — the only trust
story available for an unsigned community kernel.

The installer refuses to run on anything but a Pi 4, refuses a mismatched
image/modules pair (vermagic failure means no /dev/pps0 on a kernel that
otherwise boots fine — silent and miserable to debug), verifies SHA256SUMS,
never touches kernel8.img, and leaves rollback as deleting one config.txt line.

Boot-tested on the Pi: Stratum 1, 273 ns offset, PPS handler confirmed NOT
threaded. Binaries ship as release assets, not in git.
This commit is contained in:
Ryan Malloy 2026-07-14 09:52:17 -06:00
parent 6b6d80ded3
commit 3b505644b2
15 changed files with 333 additions and 62 deletions

View file

@ -56,4 +56,4 @@ deploy: ## Pull main + rebuild the docs container on the prod host
@echo "==> deploying $(DEPLOY_HOST):$(DEPLOY_PATH)"
ssh -A $(DEPLOY_HOST) "cd $(DEPLOY_PATH) && git fetch origin main && git reset --hard origin/main && cd docs-site && make prod"
@echo "==> sanity check"
@curl -s -o /dev/null -w " HTTP %{http_code} %{url_effective}\n" "https://cuckoo.warehack.ing/explanation/bug-detection/"
@curl -s -o /dev/null -w " HTTP %{http_code} %{url_effective}\n" "https://cuckoo.warehack.ing/explanation/preempt-rt-made-it-worse/"

View file

@ -1,6 +1,6 @@
---
title: Downloads — prebuilt RT kernel
description: A patched PREEMPT_RT kernel for the Raspberry Pi 4, so you don't need a cross-compile toolchain.
description: A patched PREEMPT_RT kernel for the Raspberry Pi 4, boot-tested and benchmarked, so you don't need a cross-compile toolchain.
sidebar:
order: 5
---
@ -9,28 +9,37 @@ The only expensive part of [the patch](/reference/the-patch/) is the toolchain.
Building natively on a Pi 4 takes hours; cross-compiling needs an x86 box and a
setup session. So here's the artifact.
**Current release: `6.18.38-rt-cuckoo+`** — Raspberry Pi's `rpi-6.18.y`,
`bcm2711_defconfig`, `PREEMPT_RT`, plus the `IRQF_NO_THREAD` patch.
:::danger[Read this before you download]
- **Raspberry Pi 4 / arm64 only.** `bcm2711_defconfig`. It will not boot a Pi 5 or
a Pi 3.
- **Unsigned, community-built.** We built this on a workstation. There is no chain
of trust here beyond "we published the exact recipe and the checksums." If that
isn't good enough for your environment — and for some environments it correctly
isn't — [build it yourself](/how-to/cross-compile-rt-kernel/). It's forty
minutes.
- **Verify the checksums.** They're in `SHA256SUMS`.
- **Raspberry Pi 4 / arm64 only.** The installer refuses to run on anything else,
because a `bcm2711` kernel on a Pi 5 doesn't boot — and a black screen on a
headless box is exactly the outcome we're trying to prevent.
- **Unsigned, community-built.** There is no chain of trust here beyond published
checksums and a reproducible recipe. For some environments that is correctly
not good enough. [Build it yourself](/how-to/cross-compile-rt-kernel/) — it's
forty minutes, and `kernel/build.sh` in the repo does the whole thing.
- **Verify the checksums.** The installer does it for you if `SHA256SUMS` is
present. Don't skip it.
:::
## Artifacts
Published on the [releases page](https://git.supported.systems/warehack.ing/cuckoo-escapement/releases):
On the [releases page](https://git.supported.systems/warehack.ing/cuckoo-escapement/releases):
| File | What |
| File | |
|---|---|
| `kernel-rt-<ver>.img.gz` | The kernel image, gzipped (Pi OS's own format) |
| `rt-modules-<ver>.tar.gz` | Matching modules — **must** be installed with the image |
| `install-rt-kernel.sh` | Installer. Adds a *new* image, never replaces `kernel8.img` |
| `kernel-rt-6.18.38-rt-cuckoo+.img.gz` | The kernel, gzipped (Pi OS's own format) — 9.8 MB |
| `rt-modules-6.18.38-rt-cuckoo+.tar.gz` | Matching modules — 22 MB. **Must** be installed with the image. |
| `install-rt-kernel.sh` | Installer. Adds a *new* image; never replaces `kernel8.img`. |
| `SHA256SUMS` | Checksums |
```
97533ea81ec56ef090908560e88bccfb2a3f7dfcc3d33a5a1cbe4bbf15163b55 kernel-rt-6.18.38-rt-cuckoo+.img.gz
6af126711eef2ae1651ac48e19353e6830f12fb0ef9a7e265f54c27206a4f0eb rt-modules-6.18.38-rt-cuckoo+.tar.gz
```
## Install
```bash
@ -39,34 +48,68 @@ sudo ./install-rt-kernel.sh
sudo reboot
```
The installer:
Then confirm the patch is actually live. This is the check that matters:
1. Untars the modules into `/lib/modules/`
2. Writes the image as `/boot/firmware/kernel-rt.img`**`kernel8.img` is left
alone**
3. Appends one line, `kernel=kernel-rt.img`, to `config.txt`
```console
$ uname -a
Linux … 6.18.38-rt-cuckoo+ #1 SMP PREEMPT_RT … aarch64
**Rollback is deleting that one line.** Mount the SD card's FAT partition on any
machine, remove it, and the stock kernel boots. That's deliberate: you should never
have to make a physical trip to a headless box because of a kernel you got from a
website.
## What's in it
Raspberry Pi's `rpi-6.12.y` tree, `bcm2711_defconfig`, plus exactly two changes:
```bash
scripts/config --enable PREEMPT_RT
# + the IRQF_NO_THREAD patch in drivers/pps/clients/pps-gpio.c
$ ps -eo comm | grep irq/.*pps
(nothing)
```
Nothing else. The full recipe is in
[Cross-compile an RT kernel](/how-to/cross-compile-rt-kernel/), and you should be
able to reproduce this byte-for-byte modulo build timestamps.
**That empty output is the whole point.** If an `irq/NN-pps@…` thread appears,
the handler is being force-threaded and [your timestamp is being taken behind the
scheduler](/explanation/preempt-rt-made-it-worse/).
## Rollback is one line
The installer appends exactly one line to `config.txt` and never touches
`kernel8.img`. If the Pi doesn't come back: pull the SD card, open `config.txt`
on any machine (it's a FAT partition — a Windows laptop will do), delete the
`kernel=kernel-rt.img` line, put the card back. The stock kernel boots.
It also leaves a `config.txt.before-rt` next to it.
## What we actually tested
Not "it compiled." This exact artifact, installed by that exact script, on a Pi 4:
| | |
|---|---|
| Boots | yes |
| `uname` | `6.18.38-rt-cuckoo+ SMP PREEMPT_RT` |
| PPS handler threaded | **no** — patch confirmed live |
| chrony | Stratum 1, ref PPS |
| System offset | 273 ns |
| Raw PPS jitter | 952 ns σ over 62 s |
:::note[One window is not a benchmark]
That 952 ns is a single 62-second sample, on a fresh boot under a clear sky, and
it is *better* than the 2568 ns in [the measurements](/reference/measurements/).
We report it because it's what we saw — not as a revision of the headline
numbers. Those came from an [A/B/A protocol](/how-to/benchmark-pps-jitter/), and
one lucky window doesn't overturn them. Sky conditions move this figure around
more than most software changes do. Measure your own board.
:::
## Reproducing it
```bash
git clone https://git.supported.systems/warehack.ing/cuckoo-escapement
cd cuckoo-escapement/kernel && ./build.sh
```
`KBUILD_BUILD_USER`, `KBUILD_BUILD_HOST` and `KBUILD_BUILD_TIMESTAMP` are pinned
in that script. Without them a kernel bakes the builder's `user@hostname` and the
wall-clock build time into `/proc/version` — which publishes whoever built it and
makes the output unreproducible. Pinned, you should be able to rebuild this and
compare bytes. For an unsigned kernel from a website, that's the only trust story
we can honestly offer.
:::note[Pinned to a tested version]
The published download always points at a kernel we have **actually booted and
benchmarked** on a Pi 4 — not simply the newest upstream. Shipping a stranger an
unvalidated kernel for a machine they may not be able to physically reach is not
something we're willing to do.
The published download always points at a kernel we have **booted and benchmarked
on a Pi 4** — never simply the newest upstream. Shipping a stranger an unvalidated
kernel, for a machine they may not be able to physically reach, is not something
we're willing to do.
:::