diff --git a/Makefile b/Makefile index 1dd5ee76..212e007d 100644 --- a/Makefile +++ b/Makefile @@ -58,8 +58,8 @@ # buildroot-showsig` transcription remains the procedure for a hand bump, and # the fallback whenever the sync run skipped (the PR then stays red at `make # buildroot-verify`, which is the safe failure mode). -BUILDROOT_VERSION ?= 2026.05.2 -BUILDROOT_SHA256 ?= f1c8157570bdc452163db2ff109978246cd25b4e93d3c4c9fefaeee3c4d6982d +BUILDROOT_VERSION ?= 2026.08 +BUILDROOT_SHA256 ?= d678e810abf877d04513e03ca2c99f992dd49118b9c2e18d6e25f5f58fa8c5cd BUILDROOT_URL = https://buildroot.org/downloads/buildroot-$(BUILDROOT_VERSION).tar.gz BUILDROOT_SIG_URL = $(BUILDROOT_URL).sign diff --git a/PLAN.md b/PLAN.md index 522deab4..cf342bcd 100644 --- a/PLAN.md +++ b/PLAN.md @@ -10,7 +10,7 @@ against the *source*, and a number did not survive. Every correction is marked Target board: Terasic DE10-Nano (Cyclone V SoC, `armv7-a` Cortex-A9) > **Phase 0 headline:** the central bet holds. All 12 SONAMEs the stock `MiSTer` binary -> needs survive at the same major version in Buildroot 2026.05 (`docs/package-manifest.md`), +> needs survive at the same major version in Buildroot 2026.08 (`docs/package-manifest.md`), > so §1's "nothing needs rebuilding" premise is now *confirmed* rather than assumed. > > **All five Phase 0 open questions were decided on 2026-07-12** — see @@ -31,7 +31,7 @@ with **glibc 2.31**, running **Linux 5.15.1** — a kernel forked in November 20 **never merged a single 5.15.y stable release**. The Buildroot configuration that produces it is not published anywhere. -This plan replaces that image with one built from **Buildroot 2026.05** and a +This plan replaces that image with one built from **Buildroot 2026.08** and a **mainline 6.18 LTS kernel**, in a public repository, with CI, with release artifacts published as GitHub Release assets rather than committed blobs. @@ -57,7 +57,7 @@ hash-verified update channel. **No permission, no fork of the cores, no fork of |-|-| |G1|A `linux.img` + `zImage_dtb` that boots the **unmodified, stock** `MiSTer` binary| |G2|Modern kernel on a supported LTS with a real security-update path| -|G3|Modern package set (Buildroot 2026.05) with a real security-update path| +|G3|Modern package set (Buildroot 2026.08) with a real security-update path| |G4|**No separate kernel repo.** All kernel patches live as `.patch` files in the Buildroot external tree and are applied to a pristine kernel.org tarball| |G5|Fully reproducible: pinned Buildroot, pinned kernel + hash, checked-in `.config`, published SBOM| |G6|Release artifacts published as **GitHub Release assets**. No binaries in git. Ever.| @@ -786,7 +786,7 @@ binaries (67 MB) with no CI at all**. ### Reproducibility checklist -* \[x] Buildroot version pinned (2026.05.x) +* \[x] Buildroot version pinned (2026.08.x) * \[x] Kernel version + upstream hash pinned; patches in-tree * \[x] `BR2_DOWNLOAD_DIR` populated from upstream; no vendored tarballs * \[x] `buildroot.config` and `linux.config` published with every release @@ -802,7 +802,7 @@ builds twice and compares Once the pipeline is stable and trusted, **Renovate** keeps every moving part current automatically. This mechanizes the sustainability commitment of §13: -* Buildroot 2026.05.x tarball version + SHA-256 (custom/regex manager over the pin file) +* Buildroot 2026.08.x tarball version + SHA-256 (custom/regex manager over the pin file) * Kernel 6.18.y version + hash (custom datasource over kernel.org's `releases.json`) * morrownr driver packages and other commit pins (git datasource) * CI container image digests and GitHub Actions versions @@ -928,7 +928,7 @@ measured against stock would fail on stock itself.]** |-|-|-| |**P0 — Recon**|Write `docs/abi-contract.md`. Triage all \~60 kernel commits into classes A–F with provenance. Derive the Buildroot package set from the shipped image.|Patch triage table is complete and reviewed| |**P1 — Kernel**|Buildroot builds 6.18 LTS from kernel.org + `linux-patches/`. Forward-port `MiSTer_fb`, audio-spi, cpufreq. Replace `loop=` with the initramfs (§5).|Boots to a serial console on real hardware| -|**P2 — Rootfs**|Buildroot 2026.05 rootfs, glibc, SONAME parity. Read-only root preserved.|**Stock `MiSTer` binary reaches the menu.**| +|**P2 — Rootfs**|Buildroot 2026.08 rootfs, glibc, SONAME parity. Read-only root preserved.|**Stock `MiSTer` binary reaches the menu.**| |**P3 — Parity**|WiFi, Bluetooth, Samba, FTP, SSH, MIDI. CI + release artifacts + SBOM.|Hardware matrix (§11) green| |**P4 — Beta**|Publish `db.json`. Recruit testers. Document rollback. Final pipeline hardening: Renovate dependency automation (§9).|Sustained opt-in use, no P1 bugs| |**P5 — Full SD image + U-Boot from source**|*Optional.* Build `uboot.img` from the pinned `u-boot_MiSTer` submodule; produce a flashable `sdcard.img` — kernel, `linux.img`, bootloader, mr-fusion-parity payload + `update_all.sh` (§8, ADR 0017).|Fresh card flashed from `sdcard.img` boots to menu; built U-Boot passes behavioural parity + hardware matrix; recovery procedure drilled| diff --git a/README.md b/README.md index 1369579d..b8e1c8e1 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,7 @@ MiSTer's operating system ships as an opaque archive containing a **375 MiB ext4 (93% full) built from **Buildroot 2021.02.4** with **glibc 2.31**, running **Linux 5.15.1** — a kernel forked in November 2021 that has **never merged a single 5.15.y stable release**. There is no public build recipe, no CI, no SBOM, and no update path for -any of it. This project rebuilds the whole thing from **Buildroot 2026.05.2** and a +any of it. This project rebuilds the whole thing from **Buildroot 2026.08** and a **mainline 6.18 LTS kernel** in a public repository, with reproducible builds, a signed-hash supply chain, a nine-workflow CI pipeline, and a per-commit reconciliation of the entire kernel fork — then ships it through the same update channel users already @@ -87,11 +87,11 @@ mainline can hold it. |---|---|---| | **Kernel** | 5.15.1, forked Nov 2021, **zero** `5.15.y` stable updates ever merged; 5.15 EOL Oct 2026 | **6.18 LTS**, on a live `.y` line with security backports | | **Kernel delta** | 110 commits on a squashed-import fork with no shared ancestry with mainline — so no `merge-base`, and no per-commit disposition | **37 patch files** against a pristine tarball, each with provenance, upstream status, and an evidence-backed record | -| **Buildroot** | 2021.02.4 | **2026.05.2** (~5 years of upstream work) | -| **glibc / gcc** | 2.31 / gcc 10-era | **2.43 / 14.4.0** | -| **OpenSSL** | **1.1.1 — EOL since 2023-09-11**, no upstream fixes since | **3.6.3** | +| **Buildroot** | 2021.02.4 | **2026.08** (~5 years of upstream work) | +| **glibc / gcc** | 2.31 / gcc 10-era | **2.44 / 15.3.0** | +| **OpenSSL** | **1.1.1 — EOL since 2023-09-11**, no upstream fixes since | **3.6.4** | | **OpenSSH** | 8.6p1 | **10.5p1** | -| **Samba** | ~4.14 | **4.24.5** | +| **Samba** | ~4.14 | **4.24.6** | | **Python** | 3.9 | **3.14.7** | | **SSH host keys** | **Identical on every MiSTer on Earth**, baked into the public download, dated 2016 | **Generated per device on first boot**, persisted to the FAT card ([ADR 0015](docs/decisions/0015-per-device-ssh-host-keys.md)) | | **Timezone on a fresh card** | Unset — `/media/fat/linux/timezone` does not exist, so glibc falls back to **UTC** and stays there until the user finds the community `timezone.sh` script | **Detected once**, from the box's public IP, using the same provider and the same destination file as `timezone.sh` so the two are interchangeable. It is one dhcpcd hook, firing on the first connection the box makes — so a card that was offline on its first boot still gets it once Wi-Fi is set up, and a box with no network does nothing at all. Never overwrites a timezone you set, and it is opt-out-able before it ever runs ([ADR 0025](docs/decisions/0025-first-boot-timezone-autodetect.md)) | @@ -133,7 +133,7 @@ re-read most recently), [`docs/package-manifest.md`](docs/package-manifest.md) ( |---|---|---| | **0 — Recon & decisions** | ✅ Complete | Patch triage, ABI-contract verification, five open questions decided (ADRs 0010–0014) | | **1 — Kernel & initramfs** | ✅ Complete | 6.18 LTS pinned; all 37 patches apply cleanly; `zImage_dtb` builds warning-free, boots under QEMU **and on real hardware** — from the **CI-built artifact**, not a local build | -| **2 — Rootfs & testing** | ✅ Complete | Buildroot 2026.05.2, glibc 2.43, reproducible ext4 image with full SBOM; menu and cores load on hardware — the ABI contract holds *in practice*, not just on paper | +| **2 — Rootfs & testing** | ✅ Complete | Buildroot 2026.08, glibc 2.44, reproducible ext4 image with full SBOM; menu and cores load on hardware — the ABI contract holds *in practice*, not just on paper | | **3 — Module packages & HW matrix** | ✅ Complete | Wi-Fi, Bluetooth, controllers and special devices packaged; hardware-validated **for the chips actually present on the one test board**. The v10/v10.1/v10.2 driver + firmware expansion (Broadcom, Wi-Fi 6/6E, MediaTek, Atheros USB, Redpine) is packaged and mostly CI-asserted but **not** hardware-validated — see the [ledger](#hardware-validation-ledger) and the [chipset table](#wi-fi-and-bluetooth-hardware-support). The remaining matrix rows (Samba, MIDI) are build/CI-verified only | | **4 — Release & sustainability** | 🔄 In progress | CI/CD, `db.json` distribution, beta program, governance, publication gate | | **5 — Full SD image & U-Boot** | 🔄 Partially landed | `sdcard.img` builds, and `release.yml` verifies it with `scripts/check-sdcard.sh` ([ADR 0020](docs/decisions/0020-sdcard-exfat-reformat-installer.md)); U-Boot-from-source is planned but not started — now targeting **mainline U-Boot**, not the 2017.03 fork ([ADR 0024](docs/decisions/0024-mainline-uboot-capability-artifact.md), [plan](docs/uboot-mainline-port.md), [tasks](docs/uboot-tasks.md)) — and the SD image has not been flashed to a fresh card on hardware (P5.4) | @@ -359,7 +359,7 @@ Full write-up with the reasoning for each: [`docs/patch-provenance.md` §10](doc ### 3. Security posture -- **OpenSSL 1.1.1 → 3.6.3.** Stock ships a TLS library that has been **end-of-life since +- **OpenSSL 1.1.1 → 3.6.4.** Stock ships a TLS library that has been **end-of-life since 2023-09-11** and has received no upstream fixes since. This is the single strongest security argument for the whole project, and it is a plain, checkable fact. - **Per-device SSH host keys.** Every stock MiSTer ships the *same* host keys, baked into @@ -369,7 +369,17 @@ Full write-up with the reasoning for each: [`docs/patch-provenance.md` §10](doc This image generates unique keys on first boot and persists them to an ext4 image on the FAT partition, **reusing stock's own proven mechanism** for Bluetooth pairing keys. ([ADR 0015](docs/decisions/0015-per-device-ssh-host-keys.md)) -- **OpenSSH 8.6p1 → 10.5p1**, **Samba ~4.14 → 4.24.5**, **BlueZ → 5.79**, +- **SSH key login that survives an update.** An OS update replaces `linux.img` wholesale, + and `/root/.ssh/authorized_keys` — the usual place for a key — lives inside it, so every + update discards it. (The root filesystem is read-only at boot and only becomes writable + once you log in, so putting a key there also means logging in first, which is circular + when the key *is* the login method.) `sshd` here also + reads **`/media/fat/linux/authorized_keys`**, on the exFAT partition an update never + touches: drop your `.pub` file there from any PC with a card reader and key login keeps + working across every future update. No shell access, no script to edit, and + `StrictModes` stays on — the initramfs mounts that partition `fmask=0022,dmask=0022`, + which is exactly what `sshd` requires. ([FAQ](docs/user/faq.md#ssh-key-persist)) +- **OpenSSH 8.6p1 → 10.5p1**, **Samba ~4.14 → 4.24.6**, **BlueZ → 5.86**, **wpa_supplicant 2.9 → 2.12** — the network-facing surface, several release cycles of hardening each. - **Python 3.9 → 3.14.7** — the on-device interpreter that runs the Downloader and @@ -571,7 +581,15 @@ shipped **byte-identical to stock's**, fetched by hash. which **broke every SSH connection on the shipped image** and forced a release to be pulled. Buildroot classified that bump as `openssh: (no CVE assigned)`. Both parity documents now say so at the top, with the specific unchecked question named. The - defconfig and `Makefile` pins are the ground truth; the prose is a dated reading of it. + **2026.08 line bump** then moved the compiler itself — **gcc 14.4.0 → 15.3.0**, glibc + 2.43 → 2.44, gdb 15.2 → 16.3 — plus **bluez 5.79 → 5.86**, Samba 4.24.5 → 4.24.6 and + OpenSSL 3.6.3 → 3.6.4. Two of those had consequences a version table cannot show: bluez + 5.86 already contains the CablePairing series this repo had been backporting, so + `board/mister/de10nano/patches/bluez5_utils/` was deleted outright; and Buildroot + **retired the 7.0 kernel-headers series**, which silently collapsed the DE25 toolchain + from glibc to uClibc until the headers pin was moved to 7.1 (see + [`buildroot-config`](docs/buildroot-config.md) §6.2 and the fragment's own comment). + The defconfig and `Makefile` pins are the ground truth; the prose is a dated reading of it. For the kernel this is handled by not writing the number down: narrative prose says "6.18 LTS", and `BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE` in the defconfig is the only place the `.y` lives. @@ -884,7 +902,7 @@ Start here if you want to run it: [**one-command install**](#install-it-on-a-rea |---|------| | G1 | A `linux.img` + `zImage_dtb` that boots the **unmodified, stock** `MiSTer` binary | | G2 | Modern kernel on a supported LTS with a real security-update path | -| G3 | Modern package set (Buildroot 2026.05) with a real security-update path | +| G3 | Modern package set (Buildroot 2026.08) with a real security-update path | | G4 | **No separate kernel repo.** All kernel patches live as `.patch` files in the Buildroot external tree, applied to a pristine kernel.org tarball | | G5 | Fully reproducible: pinned Buildroot, pinned kernel + hash, checked-in `.config`, published SBOM | | G6 | Release artifacts published as **GitHub Release assets**. No binaries in git. Ever. | diff --git a/TASKS.md b/TASKS.md index 44b78a2d..85bfd565 100644 --- a/TASKS.md +++ b/TASKS.md @@ -918,13 +918,15 @@ Exit criterion: beta users successfully opt in via `db.json` and can roll back ( - [ ] **P4.6 — Renovate onboarding (final pipeline-hardening step)** — [SONNET] — Size M — Depends: P4.1, P4.4 **Sequence after P4.8/P4.9, immediately before beta launch** — automate a pipeline only once it is stable and trusted (PLAN.md §9). Onboard Renovate and configure - `renovate.json` to manage: the Buildroot 2026.05.x tarball version + SHA-256 + `renovate.json` to manage: the Buildroot 2026.08.x tarball version + SHA-256 (custom/regex manager over the pin file from P1.1), morrownr package commit pins (git datasource), CI container image digests, and GitHub Actions versions. Every Renovate PR must trigger the full CI suite (build, patch-apply, ABI checks, reproducibility). Automerge stays OFF — a human reviews green PRs. **Reference:** `/mnt/source/sb-enema/renovate.json` — a working custom regex manager for - `BUILDROOT_VERSION` (github-tags datasource, `allowedVersions` pinned to `2026.05.x`); + `BUILDROOT_VERSION` (github-tags datasource, `allowedVersions` pinned to the current + line — `2026.08.x` today; a line bump moves that regex by hand, which is why a line + bump never arrives as a Renovate PR); use it as the template. **Done when:** a real or synthetic Renovate PR for a Buildroot point release opens with passing CI; the pin file's regex manager is covered by a Renovate config test. diff --git a/board/mister/de10nano/patches/bluez5_utils/0001-src-add-cablepairing-property.patch b/board/mister/de10nano/patches/bluez5_utils/0001-src-add-cablepairing-property.patch deleted file mode 100644 index 3491b730..00000000 --- a/board/mister/de10nano/patches/bluez5_utils/0001-src-add-cablepairing-property.patch +++ /dev/null @@ -1,180 +0,0 @@ -From 20ea34e729b230ee4f3c8671bbc8255b65825d6b Mon Sep 17 00:00:00 2001 -From: Ludovico de Nittis -Date: Thu, 24 Apr 2025 18:29:28 +0200 -Subject: [PATCH] src: Add new CablePairing property - -This adds initial support for a new CablePairing property. -The property can be used for devices that are paired using a cable and -don't support the expected bonding (with pairing/encryption), for -example like the Sixaxis gamepads. - -Provenance ----------- -Origin: 20ea34e729b230ee4f3c8671bbc8255b65825d6b - "src: Add new CablePairing property" - bluez/bluez, 2025-04-24, first released in BlueZ 5.83. - https://github.com/bluez/bluez/commit/20ea34e729b230ee4f3c8671bbc8255b65825d6b -Author: Ludovico de Nittis (Collabora) -Upstream: YES -- this IS upstream code, carried here only because Buildroot - 2026.05.1 pins bluez5_utils 5.79 - (work/buildroot/package/bluez5_utils/bluez5_utils.mk:8), which - predates it. DELETE THIS WHOLE DIRECTORY on the first Buildroot - bump whose bluez already contains these -- it is not a MiSTer - delta and must not outlive the version gap it closes. That - happens LOUDLY and on its own: the patches stop applying and the - build goes red at .stamp_patched. -Backport: -> 5.79, Michael C. Ferguson, 2026-08-13. Applied verbatim, no - rewriting for the older tree. - - HUNK OFFSETS ARE EXPECTED and are not a defect -- these are - backports across several releases. Do not "fix" a header to claim - otherwise: an earlier revision of this series said "no offsets" - because it was reading the exit status of `patch --dry-run`, - which is 0 whether or not hunks moved. What IS asserted, and was - checked with the exact flags support/scripts/apply-patches.sh:119 - uses (`patch -F0 -g0 -p1 -t -N`): no fuzz, no rejects, applied in - Buildroot's real order -- i.e. AFTER package/bluez5_utils/'s own - four patches, not against a pristine tarball. -Why: Patch 1 of 5, and 1 of the 4-commit CablePairing series that is - the whole reason this directory exists. Adds the D-Bus property - recording "this device was cable paired and cannot do canonical - bonding with encryption". Inert alone. See - docs/bluetooth-parity.md §10. ---- - doc/org.bluez.Device.rst | 7 +++++++ - src/device.c | 41 ++++++++++++++++++++++++++++++++++++++++ - src/device.h | 2 ++ - 3 files changed, 50 insertions(+) - -diff --git a/doc/org.bluez.Device.rst b/doc/org.bluez.Device.rst -index 13328249b0..80501eddd0 100644 ---- a/doc/org.bluez.Device.rst -+++ b/doc/org.bluez.Device.rst -@@ -279,6 +279,13 @@ boolean LegacyPairing [readonly] - Bluetooth 2.1 (or newer) devices that have disabled Extended Inquiry - Response support. - -+boolean CablePairing [readonly] -+``````````````````````````````` -+ -+ Set to true if the device was cable paired and it doesn't support the -+ canonical bonding with encryption, e.g. the Sixaxis gamepad. -+ If true, BlueZ will establish a connection without enforcing encryption. -+ - string Modalias [readonly, optional] - ```````````````````````````````````` - -diff --git a/src/device.c b/src/device.c -index b82a905f99..7d0b35cb11 100644 ---- a/src/device.c -+++ b/src/device.c -@@ -239,6 +239,7 @@ struct btd_device { - GSList *watches; /* List of disconnect_data */ - bool temporary; - bool connectable; -+ bool cable_pairing; - unsigned int disconn_timer; - unsigned int discov_timer; - unsigned int temporary_timer; /* Temporary/disappear timer */ -@@ -507,6 +508,9 @@ static gboolean store_device_info_cb(gpointer user_data) - g_key_file_set_boolean(key_file, "General", "Blocked", - device->blocked); - -+ g_key_file_set_boolean(key_file, "General", "CablePairing", -+ device->cable_pairing); -+ - if (device->wake_override != WAKE_FLAG_DEFAULT) { - g_key_file_set_boolean(key_file, "General", "WakeAllowed", - device->wake_override == -@@ -908,6 +912,11 @@ bool btd_device_is_trusted(struct btd_device *device) - return device->trusted; - } - -+bool device_is_cable_pairing(struct btd_device *device) -+{ -+ return device->cable_pairing; -+} -+ - static gboolean dev_property_get_address(const GDBusPropertyTable *property, - DBusMessageIter *iter, void *data) - { -@@ -1153,6 +1162,18 @@ static gboolean dev_property_get_legacy(const GDBusPropertyTable *property, - return TRUE; - } - -+static gboolean -+dev_property_get_cable_pairing(const GDBusPropertyTable *property, -+ DBusMessageIter *iter, void *data) -+{ -+ struct btd_device *device = data; -+ dbus_bool_t val = device->cable_pairing; -+ -+ dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &val); -+ -+ return TRUE; -+} -+ - static gboolean dev_property_get_rssi(const GDBusPropertyTable *property, - DBusMessageIter *iter, void *data) - { -@@ -3483,6 +3504,7 @@ static const GDBusPropertyTable device_properties[] = { - { "Trusted", "b", dev_property_get_trusted, dev_property_set_trusted }, - { "Blocked", "b", dev_property_get_blocked, dev_property_set_blocked }, - { "LegacyPairing", "b", dev_property_get_legacy }, -+ { "CablePairing", "b", dev_property_get_cable_pairing }, - { "RSSI", "n", dev_property_get_rssi, NULL, dev_property_exists_rssi }, - { "Connected", "b", dev_property_get_connected }, - { "UUIDs", "as", dev_property_get_uuids }, -@@ -4062,6 +4084,9 @@ static void load_info(struct btd_device *device, const char *local, - if (blocked) - device_block(device, FALSE); - -+ device->cable_pairing = g_key_file_get_boolean(key_file, "General", -+ "CablePairing", NULL); -+ - /* Load device profile list */ - uuids = g_key_file_get_string_list(key_file, "General", "Services", - NULL, NULL); -@@ -6416,6 +6441,22 @@ void device_set_legacy(struct btd_device *device, bool legacy) - DEVICE_INTERFACE, "LegacyPairing"); - } - -+void device_set_cable_pairing(struct btd_device *device, bool cable_pairing) -+{ -+ if (!device) -+ return; -+ -+ if (device->cable_pairing == cable_pairing) -+ return; -+ -+ DBG("setting cable pairing %d", cable_pairing); -+ -+ device->cable_pairing = cable_pairing; -+ -+ g_dbus_emit_property_changed(dbus_conn, device->path, -+ DEVICE_INTERFACE, "CablePairing"); -+} -+ - void device_store_svc_chng_ccc(struct btd_device *device, uint8_t bdaddr_type, - uint16_t value) - { -diff --git a/src/device.h b/src/device.h -index 2e4a9771d5..a35bb1386d 100644 ---- a/src/device.h -+++ b/src/device.h -@@ -94,6 +94,7 @@ bool device_is_connectable(struct btd_device *device); - bool device_is_paired(struct btd_device *device, uint8_t bdaddr_type); - bool device_is_bonded(struct btd_device *device, uint8_t bdaddr_type); - bool btd_device_is_trusted(struct btd_device *device); -+bool device_is_cable_pairing(struct btd_device *device); - void device_set_paired(struct btd_device *dev, uint8_t bdaddr_type); - void device_set_unpaired(struct btd_device *dev, uint8_t bdaddr_type); - void btd_device_set_temporary(struct btd_device *device, bool temporary); -@@ -101,6 +102,7 @@ void btd_device_set_trusted(struct btd_device *device, gboolean trusted); - void btd_device_set_connectable(struct btd_device *device, bool connectable); - void device_set_bonded(struct btd_device *device, uint8_t bdaddr_type); - void device_set_legacy(struct btd_device *device, bool legacy); -+void device_set_cable_pairing(struct btd_device *device, bool cable_pairing); - void device_set_rssi_with_delta(struct btd_device *device, int8_t rssi, - int8_t delta_threshold); - void device_set_rssi(struct btd_device *device, int8_t rssi); diff --git a/board/mister/de10nano/patches/bluez5_utils/0002-sixaxis-set-cablepairing-on-usb-pairing.patch b/board/mister/de10nano/patches/bluez5_utils/0002-sixaxis-set-cablepairing-on-usb-pairing.patch deleted file mode 100644 index 5d6da74b..00000000 --- a/board/mister/de10nano/patches/bluez5_utils/0002-sixaxis-set-cablepairing-on-usb-pairing.patch +++ /dev/null @@ -1,68 +0,0 @@ -From 56516d6cc8d967b1480fc31a1ac74fe61e04f270 Mon Sep 17 00:00:00 2001 -From: Ludovico de Nittis -Date: Thu, 24 Apr 2025 18:29:30 +0200 -Subject: [PATCH] sixaxis: Set CablePairing when pairing a Sixaxis with USB - -Sixaxis gamepads don't support encryption. When doing the USB cable -pairing, set the CablePairing property to keep the connection to the -device unencrypted. - -Provenance ----------- -Origin: 56516d6cc8d967b1480fc31a1ac74fe61e04f270 - "sixaxis: Set CablePairing when pairing a Sixaxis with USB" - bluez/bluez, 2025-04-24, first released in BlueZ 5.83. - https://github.com/bluez/bluez/commit/56516d6cc8d967b1480fc31a1ac74fe61e04f270 -Author: Ludovico de Nittis (Collabora) -Upstream: YES -- this IS upstream code, carried here only because Buildroot - 2026.05.1 pins bluez5_utils 5.79 - (work/buildroot/package/bluez5_utils/bluez5_utils.mk:8), which - predates it. DELETE THIS WHOLE DIRECTORY on the first Buildroot - bump whose bluez already contains these -- it is not a MiSTer - delta and must not outlive the version gap it closes. That - happens LOUDLY and on its own: the patches stop applying and the - build goes red at .stamp_patched. -Backport: -> 5.79, Michael C. Ferguson, 2026-08-13. Applied verbatim, no - rewriting for the older tree. - - HUNK OFFSETS ARE EXPECTED and are not a defect -- these are - backports across several releases. Do not "fix" a header to claim - otherwise: an earlier revision of this series said "no offsets" - because it was reading the exit status of `patch --dry-run`, - which is 0 whether or not hunks moved. What IS asserted, and was - checked with the exact flags support/scripts/apply-patches.sh:119 - uses (`patch -F0 -g0 -p1 -t -N`): no fuzz, no rejects, applied in - Buildroot's real order -- i.e. AFTER package/bluez5_utils/'s own - four patches, not against a pristine tarball. -Why: Patch 2 of 5. Makes the sixaxis plugin (enabled here, - BR2_PACKAGE_BLUEZ5_UTILS_PLUGINS_SIXAXIS=y) stamp the property on - a DS3/SIXAXIS during USB cable pairing. - - MIGRATION TRAP: plugins/sixaxis.c's setup_device() short-circuits - on an already-trusted device, so a DS3 cable paired under an - older BlueZ never acquires the property and still fails to - connect. Such a pad must be re-paired: - `bluetoothctl remove `, then cable-pair again. ---- - plugins/sixaxis.c | 5 ++++- - 1 file changed, 4 insertions(+), 1 deletion(-) - -diff --git a/plugins/sixaxis.c b/plugins/sixaxis.c -index 3e69f1dd2f..ea160c65d3 100644 ---- a/plugins/sixaxis.c -+++ b/plugins/sixaxis.c -@@ -296,10 +296,13 @@ static void agent_auth_cb(DBusError *derr, void *user_data) - remove_device = false; - btd_device_set_temporary(closure->device, false); - -- if (closure->type == CABLE_PAIRING_SIXAXIS) -+ if (closure->type == CABLE_PAIRING_SIXAXIS) { - btd_device_set_record(closure->device, HID_UUID, - SIXAXIS_HID_SDP_RECORD); - -+ device_set_cable_pairing(closure->device, true); -+ } -+ - ba2str(&closure->bdaddr, device_addr); - ba2str(¢ral_bdaddr, central_addr); - ba2str(adapter_bdaddr, adapter_addr); diff --git a/board/mister/de10nano/patches/bluez5_utils/0003-adapter-add-btd-adapter-has-cable-pairing-devices.patch b/board/mister/de10nano/patches/bluez5_utils/0003-adapter-add-btd-adapter-has-cable-pairing-devices.patch deleted file mode 100644 index aadf783e..00000000 --- a/board/mister/de10nano/patches/bluez5_utils/0003-adapter-add-btd-adapter-has-cable-pairing-devices.patch +++ /dev/null @@ -1,94 +0,0 @@ -From c5dffe0ce2fea65134d90b30bb27093134c12213 Mon Sep 17 00:00:00 2001 -From: Ludovico de Nittis -Date: Thu, 24 Apr 2025 18:29:31 +0200 -Subject: [PATCH] adapter: Add btd_adapter_has_cable_pairing_devices() - -Add a function that can be used to know if any of the known devices have -the `CablePaired` property set. - -Provenance ----------- -Origin: c5dffe0ce2fea65134d90b30bb27093134c12213 - "adapter: Add btd_adapter_has_cable_pairing_devices()" - bluez/bluez, 2025-04-24, first released in BlueZ 5.83. - https://github.com/bluez/bluez/commit/c5dffe0ce2fea65134d90b30bb27093134c12213 -Author: Ludovico de Nittis (Collabora) -Upstream: YES -- this IS upstream code, carried here only because Buildroot - 2026.05.1 pins bluez5_utils 5.79 - (work/buildroot/package/bluez5_utils/bluez5_utils.mk:8), which - predates it. DELETE THIS WHOLE DIRECTORY on the first Buildroot - bump whose bluez already contains these -- it is not a MiSTer - delta and must not outlive the version gap it closes. That - happens LOUDLY and on its own: the patches stop applying and the - build goes red at .stamp_patched. -Backport: -> 5.79, Michael C. Ferguson, 2026-08-13. Applied verbatim, no - rewriting for the older tree. - - HUNK OFFSETS ARE EXPECTED and are not a defect -- these are - backports across several releases. Do not "fix" a header to claim - otherwise: an earlier revision of this series said "no offsets" - because it was reading the exit status of `patch --dry-run`, - which is 0 whether or not hunks moved. What IS asserted, and was - checked with the exact flags support/scripts/apply-patches.sh:119 - uses (`patch -F0 -g0 -p1 -t -N`): no fuzz, no rejects, applied in - Buildroot's real order -- i.e. AFTER package/bluez5_utils/'s own - four patches, not against a pristine tarball. -Why: Patch 3 of 5, and THE ONE THAT WAS ORIGINALLY MISSED. Direct - parent of ba101f47 (patch 0004); defines the - btd_adapter_has_cable_pairing_devices() helper that 0004's - profiles/input/manager.c hunk calls. - - WHY THIS WARNING IS HERE: the first cut of this series carried - only three commits and omitted this one. It APPLIED cleanly -- - `patch` was perfectly happy -- while leaving manager.c calling a - function with no definition and no declaration anywhere in the - tree, an unconditional compile failure under gcc 14.4's - -Wimplicit-function-declaration. "The series applies" and "the - series builds" are different claims and only the second matters. - Do not re-derive this series from the issue thread without - walking the parent chain. ---- - src/adapter.c | 17 +++++++++++++++++ - src/adapter.h | 1 + - 2 files changed, 18 insertions(+) - -diff --git a/src/adapter.c b/src/adapter.c -index c21b38095e..fd425e6d2f 100644 ---- a/src/adapter.c -+++ b/src/adapter.c -@@ -412,6 +412,23 @@ uint16_t btd_adapter_get_index(struct btd_adapter *adapter) - return adapter->dev_id; - } - -+bool btd_adapter_has_cable_pairing_devices(struct btd_adapter *adapter) -+{ -+ GSList *l; -+ -+ if (!adapter) -+ return false; -+ -+ for (l = adapter->devices; l; l = l->next) { -+ struct btd_device *device = l->data; -+ -+ if (device_is_cable_pairing(device)) -+ return true; -+ } -+ -+ return false; -+} -+ - static gboolean process_auth_queue(gpointer user_data); - - static void dev_class_changed_callback(uint16_t index, uint16_t length, -diff --git a/src/adapter.h b/src/adapter.h -index 8dfbe762ef..6b2bc28f6d 100644 ---- a/src/adapter.h -+++ b/src/adapter.h -@@ -30,6 +30,7 @@ struct queue; - struct btd_adapter *btd_adapter_get_default(void); - bool btd_adapter_is_default(struct btd_adapter *adapter); - uint16_t btd_adapter_get_index(struct btd_adapter *adapter); -+bool btd_adapter_has_cable_pairing_devices(struct btd_adapter *adapter); - - typedef void (*adapter_cb) (struct btd_adapter *adapter, gpointer user_data); - diff --git a/board/mister/de10nano/patches/bluez5_utils/0004-input-sec-level-low-for-cable-paired-devices.patch b/board/mister/de10nano/patches/bluez5_utils/0004-input-sec-level-low-for-cable-paired-devices.patch deleted file mode 100644 index 1af7caba..00000000 --- a/board/mister/de10nano/patches/bluez5_utils/0004-input-sec-level-low-for-cable-paired-devices.patch +++ /dev/null @@ -1,240 +0,0 @@ -From ba101f47dfbad6d4f6fd439570ac4a1093df2ceb Mon Sep 17 00:00:00 2001 -From: Ludovico de Nittis -Date: Thu, 24 Apr 2025 18:29:32 +0200 -Subject: [PATCH] input: Automatically use sec level low when using a cable - paired device - -BT_IO_SEC_LOW is the only way to allow devices that use cable pairing to -establish a connection. - -This adds the ability to start the listening input server with -BT_IO_SEC_LOW to avoid breaking support for these devices, and then, -in `hidp_add_connection()`, we check if either `classic_bonded_only` was -disabled or if this device has `CablePairing`. If neither are true, we -bump the security back to BT_IO_SEC_MEDIUM, i.e. enforcing encryption. - -This allows supporting these devices without having to change the -classic bonded only option. - -Provenance ----------- -Origin: ba101f47dfbad6d4f6fd439570ac4a1093df2ceb - "input: Automatically use sec level low when using a cable paired device" - bluez/bluez, 2025-04-24, first released in BlueZ 5.83. - https://github.com/bluez/bluez/commit/ba101f47dfbad6d4f6fd439570ac4a1093df2ceb -Author: Ludovico de Nittis (Collabora) -Upstream: YES -- this IS upstream code, carried here only because Buildroot - 2026.05.1 pins bluez5_utils 5.79 - (work/buildroot/package/bluez5_utils/bluez5_utils.mk:8), which - predates it. DELETE THIS WHOLE DIRECTORY on the first Buildroot - bump whose bluez already contains these -- it is not a MiSTer - delta and must not outlive the version gap it closes. That - happens LOUDLY and on its own: the patches stop applying and the - build goes red at .stamp_patched. -Backport: -> 5.79, Michael C. Ferguson, 2026-08-13. Applied verbatim, no - rewriting for the older tree. - - HUNK OFFSETS ARE EXPECTED and are not a defect -- these are - backports across several releases. Do not "fix" a header to claim - otherwise: an earlier revision of this series said "no offsets" - because it was reading the exit status of `patch --dry-run`, - which is 0 whether or not hunks moved. What IS asserted, and was - checked with the exact flags support/scripts/apply-patches.sh:119 - uses (`patch -F0 -g0 -p1 -t -N`): no fuzz, no rejects, applied in - Buildroot's real order -- i.e. AFTER package/bluez5_utils/'s own - four patches, not against a pristine tarball. -Why: Patch 4 of 5, and the one that actually fixes DS3 pairing. The - input server listens at BT_IO_SEC_LOW and hidp_add_connection() - re-raises to BT_IO_SEC_MEDIUM unless classic_bonded_only is off - OR the device carries CablePairing. Net effect: a cable-paired - DS3 connects while every other BR/EDR HID device keeps encryption - enforced. - - THIS IS WHY WE DO NOT SET ClassicBondedOnly=false, the fix every - retro-gaming distro reaches for. That option drops the encryption - requirement for ALL BR/EDR HID devices, re-exposing - CVE-2023-45866 (unauthenticated HID injection). Upstream closed - bluez#688 with this series precisely to avoid it. - - Applies on BOTH transports: hidp_add_connection() is the shared - entry point, dispatching to uhid_connadd() or ioctl_connadd() - afterwards -- so the cable_pairing guard covers the kernel-HIDP - path this image selects via UserspaceHID=false. ---- - profiles/input/device.c | 19 ++++++++++-- - profiles/input/manager.c | 3 +- - profiles/input/server.c | 65 ++++++++++++++++++++++++++++++++++++++-- - profiles/input/server.h | 3 +- - 4 files changed, 83 insertions(+), 7 deletions(-) - -diff --git a/profiles/input/device.c b/profiles/input/device.c -index 3627573e79..a7bc4d44fa 100644 ---- a/profiles/input/device.c -+++ b/profiles/input/device.c -@@ -47,6 +47,7 @@ - - #include "device.h" - #include "hidp_defs.h" -+#include "server.h" - - #define INPUT_INTERFACE "org.bluez.Input1" - -@@ -1065,6 +1066,7 @@ static gboolean encrypt_notify(GIOChannel *io, GIOCondition condition, - static int hidp_add_connection(struct input_device *idev) - { - struct hidp_connadd_req *req; -+ bool cable_pairing; - GError *gerr = NULL; - int err; - -@@ -1088,8 +1090,11 @@ static int hidp_add_connection(struct input_device *idev) - if (device_name_known(idev->device)) - device_get_name(idev->device, req->name, sizeof(req->name)); - -+ cable_pairing = device_is_cable_pairing(idev->device); -+ - /* Make sure the device is bonded if required */ -- if (classic_bonded_only && !input_device_bonded(idev)) { -+ if (!cable_pairing && classic_bonded_only && -+ !input_device_bonded(idev)) { - error("Rejected connection from !bonded device %s", idev->path); - goto cleanup; - } -@@ -1098,7 +1103,10 @@ static int hidp_add_connection(struct input_device *idev) - /* Some platforms may choose to require encryption for all devices */ - /* Note that this only matters for pre 2.1 devices as otherwise the */ - /* device is encrypted by default by the lower layers */ -- if (classic_bonded_only || idev->type == BT_UHID_KEYBOARD) { -+ /* Don't enforce encryption for cable paired devices because they */ -+ /* don't support it */ -+ if (!cable_pairing && (classic_bonded_only || -+ idev->type == BT_UHID_KEYBOARD)) { - if (!bt_io_set(idev->intr_io, &gerr, - BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, - BT_IO_OPT_INVALID)) { -@@ -1546,6 +1554,13 @@ int input_device_register(struct btd_service *service) - btd_service_set_user_data(service, idev); - device_set_wake_support(device, true); - -+ if (device_is_cable_pairing(device)) { -+ struct btd_adapter *adapter = device_get_adapter(device); -+ const bdaddr_t *bdaddr = btd_adapter_get_address(adapter); -+ -+ server_set_cable_pairing(bdaddr, true); -+ } -+ - return 0; - } - -diff --git a/profiles/input/manager.c b/profiles/input/manager.c -index d1accc24f8..95ca0a7ee5 100644 ---- a/profiles/input/manager.c -+++ b/profiles/input/manager.c -@@ -33,7 +33,8 @@ - - static int hid_server_probe(struct btd_profile *p, struct btd_adapter *adapter) - { -- return server_start(btd_adapter_get_address(adapter)); -+ return server_start(btd_adapter_get_address(adapter), -+ btd_adapter_has_cable_pairing_devices(adapter)); - } - - static void hid_server_remove(struct btd_profile *p, -diff --git a/profiles/input/server.c b/profiles/input/server.c -index 79cf08a66b..c8be445c73 100644 ---- a/profiles/input/server.c -+++ b/profiles/input/server.c -@@ -266,12 +266,26 @@ static void confirm_event_cb(GIOChannel *chan, gpointer user_data) - g_io_channel_shutdown(chan, TRUE, NULL); - } - --int server_start(const bdaddr_t *src) -+static BtIOSecLevel get_necessary_sec_level(bool device_cable_pairing) -+{ -+ /* Use lower security to allow the cable paired devices to connect. */ -+ /* Unless classic bonded only mode is disabled, the security level */ -+ /* will be bumped again for non cable paired devices in */ -+ /* hidp_add_connection() */ -+ if (device_cable_pairing) -+ return BT_IO_SEC_LOW; -+ -+ return input_get_classic_bonded_only() ? BT_IO_SEC_MEDIUM : -+ BT_IO_SEC_LOW; -+} -+ -+int server_start(const bdaddr_t *src, bool device_sixaxis_cable_pairing) - { - struct input_server *server; - GError *err = NULL; -- BtIOSecLevel sec_level = input_get_classic_bonded_only() ? -- BT_IO_SEC_MEDIUM : BT_IO_SEC_LOW; -+ BtIOSecLevel sec_level; -+ -+ sec_level = get_necessary_sec_level(device_sixaxis_cable_pairing); - - server = g_new0(struct input_server, 1); - bacpy(&server->src, src); -@@ -308,6 +322,51 @@ int server_start(const bdaddr_t *src) - return 0; - } - -+int server_set_cable_pairing(const bdaddr_t *src, bool device_cable_pairing) -+{ -+ struct input_server *server; -+ GSList *l; -+ BtIOSecLevel sec_level; -+ BtIOSecLevel new_sec_level; -+ GError *err = NULL; -+ -+ l = g_slist_find_custom(servers, src, server_cmp); -+ if (!l) -+ return -1; -+ -+ server = l->data; -+ -+ bt_io_get(server->ctrl, &err, BT_IO_OPT_SEC_LEVEL, &sec_level, -+ BT_IO_OPT_INVALID); -+ if (err) { -+ error("%s", err->message); -+ g_error_free(err); -+ return -1; -+ } -+ -+ new_sec_level = get_necessary_sec_level(device_cable_pairing); -+ if (sec_level == new_sec_level) -+ return -1; -+ -+ DBG("Applying the new security level to the listening input server"); -+ -+ if (!bt_io_set(server->ctrl, &err, BT_IO_OPT_SEC_LEVEL, new_sec_level, -+ BT_IO_OPT_INVALID)) { -+ error("bt_io_set(OPT_SEC_LEVEL): %s", err->message); -+ g_error_free(err); -+ return -1; -+ } -+ -+ if (!bt_io_set(server->intr, &err, BT_IO_OPT_SEC_LEVEL, new_sec_level, -+ BT_IO_OPT_INVALID)) { -+ error("bt_io_set(OPT_SEC_LEVEL): %s", err->message); -+ g_error_free(err); -+ return -1; -+ } -+ -+ return 0; -+} -+ - void server_stop(const bdaddr_t *src) - { - struct input_server *server; -diff --git a/profiles/input/server.h b/profiles/input/server.h -index 50f4b61352..4ad82c10e9 100644 ---- a/profiles/input/server.h -+++ b/profiles/input/server.h -@@ -8,5 +8,6 @@ - * - */ - --int server_start(const bdaddr_t *src); -+int server_start(const bdaddr_t *src, bool device_cable_pairing); -+int server_set_cable_pairing(const bdaddr_t *src, bool device_cable_pairing); - void server_stop(const bdaddr_t *src); diff --git a/board/mister/de10nano/patches/bluez5_utils/0005-input-fix-off-by-one-report-descriptor-size.patch b/board/mister/de10nano/patches/bluez5_utils/0005-input-fix-off-by-one-report-descriptor-size.patch deleted file mode 100644 index 714a4ec2..00000000 --- a/board/mister/de10nano/patches/bluez5_utils/0005-input-fix-off-by-one-report-descriptor-size.patch +++ /dev/null @@ -1,116 +0,0 @@ -From e2324f7a87cdbc4c4f95882f841e560bd331327a Mon Sep 17 00:00:00 2001 -From: Andrey Smirnov -Date: Fri, 5 Dec 2025 17:52:56 -0800 -Subject: [PATCH] input/device: Fix off by one report descriptor size error - -Due to the way we handle SDP strings internally BlueZ ends up -including and artifically added 0x00 into the report descriptor it -passes on to UHID. This results in kernel error messages like - -[371225.240843] microsoft 0005:045E:02FD.0019: unknown main item tag 0x0 - -or - -[367200.458679] playstation 0005:054C:0CE6.0014: unknown main item tag 0x0 - -The error is ignored by the kernel's report parser, so this is benign, -but for the sake of correctness, let's not supply bogus data in the -first place. - -Provenance ----------- -Origin: e2324f7a87cdbc4c4f95882f841e560bd331327a - "input/device: Fix off by one report descriptor size error" - bluez/bluez, 2025-12-05, first released in BlueZ 5.87. - https://github.com/bluez/bluez/commit/e2324f7a87cdbc4c4f95882f841e560bd331327a -Author: Andrey Smirnov -Upstream: YES -- this IS upstream code, carried here only because Buildroot - 2026.05.1 pins bluez5_utils 5.79 - (work/buildroot/package/bluez5_utils/bluez5_utils.mk:8), which - predates it. DELETE THIS WHOLE DIRECTORY on the first Buildroot - bump whose bluez already contains these -- it is not a MiSTer - delta and must not outlive the version gap it closes. That - happens LOUDLY and on its own: the patches stop applying and the - build goes red at .stamp_patched. -Backport: -> 5.79, Michael C. Ferguson, 2026-08-13. Applied verbatim, no - rewriting for the older tree. - - HUNK OFFSETS ARE EXPECTED and are not a defect -- these are - backports across several releases. Do not "fix" a header to claim - otherwise: an earlier revision of this series said "no offsets" - because it was reading the exit status of `patch --dry-run`, - which is 0 whether or not hunks moved. What IS asserted, and was - checked with the exact flags support/scripts/apply-patches.sh:119 - uses (`patch -F0 -g0 -p1 -t -N`): no fuzz, no rejects, applied in - Buildroot's real order -- i.e. AFTER package/bluez5_utils/'s own - four patches, not against a pristine tarball. -Why: Patch 5 of 5, and the only one NOT part of the CablePairing - series -- bluez#1710. extract_hid_desc_data() included the zero - delimiter that extract_str() appends to an SDP string in the HID - report descriptor it hands to the kernel, making the descriptor - one byte too long. Upstream's own commit message names the - DualSense as an observed case: - playstation 0005:054C:0CE6.0014: unknown main item tag 0x0 - - SCOPE, stated honestly: upstream calls this benign -- the - kernel's report parser ignores the stray item -- so this is a - correctness fix, not a repair for a user-visible fault. Carried - because it is small, upstream, and squarely in the path every - Bluetooth pad on this image takes. - - The SIZING FIX applies regardless of transport despite the commit - message mentioning UHID: the bug is in the SDP extraction that - fills struct hidp_connadd_req, which happens BEFORE the - uhid/kernel-HIDP branch, and rd_data/rd_size are consumed by both. - - ONE UPSTREAM WART CARRIED WITH IT, and it is transport-specific - in a way the sizing fix is not. The patch moves the rd_size - assignment ABOVE the g_try_malloc0(), so an allocation failure now - leaves rd_size != 0 with rd_data == NULL and still returns - success. Downstream, uhid_connadd() -> bt_uhid_create() memcpys - rd_size bytes from that NULL pointer (src/shared/uhid.c) -- - a bluetoothd crash; ioctl_connadd() merely gets EFAULT. Reachable - only under OOM, and this image selects kernel HIDP for BR/EDR - (UserspaceHID=false, see the overlay's input.conf), so the crash - path is not the one our pads take. Carried verbatim rather than - locally "fixed" because diverging from upstream on an unrelated - hardening point is how a backport series starts rotting; if - upstream addresses it, that commit is the thing to pull in. - - Applies with fuzz-free offsets on 5.79. Checked that the -1 is - safe against THIS tree, not just against 5.87: both producers of - unitSize for a text SDP datum agree -- extract_str() - (lib/sdp.c:1205, unitSize = n + 1) and - sdp_data_alloc_with_length() (lib/sdp.c:428, unitSize = 1 + - length), the latter being the path the sixaxis plugin's - hard-coded XML SDP record takes -- so the -1 removes the NUL - delimiter and can never truncate a real descriptor byte. ---- - profiles/input/device.c | 13 ++++++++++--- - 1 file changed, 10 insertions(+), 3 deletions(-) - -diff --git a/profiles/input/device.c b/profiles/input/device.c -index 02a9ad43d6..6bdc5ee3aa 100644 ---- a/profiles/input/device.c -+++ b/profiles/input/device.c -@@ -863,10 +863,17 @@ static int extract_hid_desc_data(const sdp_record_t *rec, - if (!d || !SDP_IS_TEXT_STR(d->dtd)) - goto invalid_desc; - -- req->rd_data = g_try_malloc0(d->unitSize); -+ /* -+ * Report descriptor data is parsed by extract_str() which -+ * will allocate N + 1 bytes for the incoming string to -+ * include a zero delimiter. Since that zero delimiter isn't a -+ * part of a report descriptor we adjust the size here to -+ * account for that. -+ */ -+ req->rd_size = d->unitSize - 1; -+ req->rd_data = g_try_malloc0(req->rd_size); - if (req->rd_data) { -- memcpy(req->rd_data, d->val.str, d->unitSize); -- req->rd_size = d->unitSize; -+ memcpy(req->rd_data, d->val.str, req->rd_size); - epox_endian_quirk(req->rd_data, req->rd_size); - } - diff --git a/board/mister/de10nano/rootfs-overlay/etc/bluetooth/input.conf b/board/mister/de10nano/rootfs-overlay/etc/bluetooth/input.conf index dc266131..a73e5d1f 100644 --- a/board/mister/de10nano/rootfs-overlay/etc/bluetooth/input.conf +++ b/board/mister/de10nano/rootfs-overlay/etc/bluetooth/input.conf @@ -69,24 +69,27 @@ UserspaceHID=false # Defaults to true for security. # # PINNED TO true ON PURPOSE -- do NOT "fix" DS3/SIXAXIS pairing by setting -# this false. It is already BlueZ 5.79's compiled-in default -# (profiles/input/device.c:93); pinning it explicitly is a guard, because +# this false. It is already BlueZ's compiled-in default +# (profiles/input/device.c), and pinning it explicitly is a guard, because # this is exactly the line that every retro-gaming distro flips to false to # make the PS3 pad work, and doing so drops the encryption requirement for # EVERY BR/EDR HID device on the system, re-exposing CVE-2023-45866 # (unauthenticated HID injection). # # DS3 support is instead provided by the upstream CablePairing series -- -# four commits, first released in BlueZ 5.83, backported in -# board/mister/de10nano/patches/bluez5_utils/ because Buildroot 2026.05.1 -# pins 5.79. It makes the input server listen at BT_IO_SEC_LOW and re-raise -# security to BT_IO_SEC_MEDIUM for everything EXCEPT a device carrying the -# CablePairing property -- so the DS3 connects and every other pad keeps -# encryption enforced. Upstream closed bluez#688 with that series, explicitly -# noting DS3 now works "without changing ClassicBondedOnly=false". +# four commits first released in BlueZ 5.83, and part of the bluez that +# Buildroot pins since 2026.08 (5.86). It makes the input server listen at +# BT_IO_SEC_LOW and re-raise security to BT_IO_SEC_MEDIUM for everything +# EXCEPT a device carrying the CablePairing property -- so the DS3 connects +# and every other pad keeps encryption enforced. Upstream closed bluez#688 +# with that series, explicitly noting DS3 now works "without changing +# ClassicBondedOnly=false". # -# (That directory carries a fifth, unrelated patch: bluez#1710's HID -# report-descriptor off-by-one fix from 5.87. See its own header.) +# Those commits were carried here as backports under +# board/mister/de10nano/patches/bluez5_utils/ while Buildroot pinned 5.79; +# that directory is GONE as of the 2026.08 bump, because 5.86 already +# contains them. scripts/ci-tests.sh still asserts the series is present in +# the built tree, so a bluez that ever loses it fails loudly. ClassicBondedOnly=true # LE upgrade security diff --git a/board/mister/de10nano/rootfs-overlay/etc/ssh/sshd_config b/board/mister/de10nano/rootfs-overlay/etc/ssh/sshd_config index 939dac3b..2f7dda56 100644 --- a/board/mister/de10nano/rootfs-overlay/etc/ssh/sshd_config +++ b/board/mister/de10nano/rootfs-overlay/etc/ssh/sshd_config @@ -45,7 +45,46 @@ PermitRootLogin yes # The default is to check both .ssh/authorized_keys and .ssh/authorized_keys2 # but this is overridden so installations will only check .ssh/authorized_keys -AuthorizedKeysFile .ssh/authorized_keys +# +# SECOND PATH ADDED: /media/fat/linux/authorized_keys -- a user's key that +# SURVIVES AN IMAGE UPDATE. An update replaces linux.img wholesale, so anything +# under /root/.ssh -- which lives inside that file -- is destroyed by it. That +# is the durable reason for this second path, and it holds regardless of how +# the root filesystem happens to be mounted at the time. +# +# Getting a key into the first path is awkward besides: / is mounted READ-ONLY +# at boot (`ro` on the cmdline; inittab's remount-rw line is deliberately left +# commented, ADR 0011) and a fresh image ships NO /root/.ssh at all. It does +# become writable later -- /etc/profile ends with `mount -o remount,rw /` on +# interactive login, which is how / ever becomes writable at all (stock parity, +# docs/init-parity.md) -- so a key CAN be placed there by hand. It just needs a +# login first, which is circular when the key IS the login method, and it does +# not survive the next update either way. The FAT partition is not reflashed, +# so a key here persists across every update. This +# is the same "persist it on /media/fat" principle as ADR 0015's host keys, +# and sshd's native multi-path support means it needs no init script, no +# bind-mount and no user-startup.sh hook. +# +# WHY NOT ssh.ext4 (ADR 0015's mechanism)? Considered and rejected. That is an +# ext4 image INSIDE a file, right for HOST keys because the DEVICE writes them +# -- but an authorized_keys file is written by the USER, and an ext4-in-a-file +# cannot be opened from Windows or macOS with a card reader, while editing it +# on the box needs the very shell access the key is meant to grant. The split +# is who writes the file: machine-written state in ssh.ext4, user-supplied +# state on exFAT. +# +# StrictModes STAYS ON (default yes) and this path satisfies it: the initramfs +# mounts the FAT partition with fmask=0022,dmask=0022 and no uid/gid options +# (board/mister/de10nano/initramfs-overlay/init:27), so the file lands +# root-owned 0755 and its parents 0755 -- owner-writable only, which is what +# sshd requires. Those mount options are OURS and fixed, not user-tunable, so +# this cannot be silently invalidated by a differently-mounted card. Verified +# end-to-end on hardware (exFAT, StrictModes=yes, key auth from this path +# alone). +# +# ORDER MATTERS ONLY FOR PRECEDENCE, NOT FUNCTION: sshd tries every listed +# file, so a key in either location works. +AuthorizedKeysFile .ssh/authorized_keys /media/fat/linux/authorized_keys #AuthorizedPrincipalsFile none diff --git a/configs/fragments/de25nano.fragment b/configs/fragments/de25nano.fragment index 98199fa3..9d51d04d 100644 --- a/configs/fragments/de25nano.fragment +++ b/configs/fragments/de25nano.fragment @@ -13,10 +13,23 @@ # "simplify" it to BR2_cortex_a76. No NEON/VFP knobs exist on AArch64. BR2_aarch64=y BR2_cortex_a76_a55=y -# WARNING: headers SERIES pin, same trap as the DE10's (§3.2). 7_0 is the -# newest series Buildroot 2026.05.2 offers below our 7.2 kernel; re-check on +# WARNING: headers SERIES pin, same trap as the DE10's (§3.2). 7_1 is the +# newest series Buildroot 2026.08 offers below our 7.2 kernel; re-check on # every Buildroot bump and move to a 7.2 series the day one exists. -BR2_KERNEL_HEADERS_7_0=y +# +# WAS 7_0 UNTIL THE 2026.08 BUMP, and the way it broke is the reason this +# WARNING exists. Buildroot 2026.08 REMOVED the 7.0 series outright -- it is +# now a Config.in.legacy stub ("kernel headers version 7.0.x are no longer +# supported", selecting BR2_LEGACY). A retired symbol does not error out at +# merge time: `BR2_KERNEL_HEADERS_7_0=y` still SET fine, so +# check-config-fragments' (b) "every fragment symbol survives" assertion still +# passed with 0 dropped -- the literal line really was in the resolved config. +# What silently collapsed was everything downstream of it: the headers choice +# fell back to BR2_KERNEL_HEADERS_AS_KERNEL, and with it the libc choice fell +# from glibc to uClibc. The GOLDEN HASH is what caught it, which is exactly the +# backstop it exists to be. If you ever see BR2_TOOLCHAIN_USES_UCLIBC in a +# resolved DE25 config, look here first. +BR2_KERNEL_HEADERS_7_1=y # --- Download integrity (docs/buildroot-config.md §6.3) --- # WARNING: patches/linux/linux.hash here is a SYMLINK to the DE10's hash diff --git a/configs/fragments/golden.sha256 b/configs/fragments/golden.sha256 index 26f61e57..9b8b1660 100644 --- a/configs/fragments/golden.sha256 +++ b/configs/fragments/golden.sha256 @@ -4,6 +4,10 @@ # scripts/check-config-fragments.sh --update-golden # and say in the commit message what changed and why. Columns: # +2026.08 de10nano fccaabea951a6d8b8f65f16bf85d199046eae9bacf6ea5102f5a1842317870b6 +2026.08 de10nano-kernel b69db3e12782bfede48587acbb9549a0f2ef794acb6b8a41d5c95bc112a91104 +2026.08 de25nano 81e7f4e613aafa3114768db14eadbc66c778f310e10dd1d163acfbbe1e7289b6 +2026.08 rt 103e68329df556920a06b736bcbafcad3913ce3ce49ef75f448df2797465ad95 2026.05.2 de10nano 251ae321011945e690a32f2fbb704b619681fd26d06acf578f7691e76286f992 2026.05.2 de10nano-kernel 5b372abe43c46c4eb5f3c88d51e48ff2eb6fa713b3c02f0f337c39d0f111dc69 2026.05.2 de25nano 2adb6b4029bee4467053403fdcf290042ccd2b554cc126de9a2f7a155b433b28 diff --git a/docs/bluetooth-parity.md b/docs/bluetooth-parity.md index 9a095017..929eefb3 100644 --- a/docs/bluetooth-parity.md +++ b/docs/bluetooth-parity.md @@ -12,20 +12,19 @@ | | Stock | Ours | |---|---|---| -| BlueZ version | unknown exact upstream version (image dated 2016-12-31; `libbluetooth.so.3.19.5`, a libtool version string, not a BlueZ release number) | **5.79** (`work/buildroot/package/bluez5_utils/bluez5_utils.mk:8`, Buildroot 2026.05.1's pinned version) | +| BlueZ version | unknown exact upstream version (image dated 2016-12-31; `libbluetooth.so.3.19.5`, a libtool version string, not a BlueZ release number) | **5.86** (`work/buildroot/package/bluez5_utils/bluez5_utils.mk:8`, Buildroot 2026.08's pinned version) | | `libbluetooth` SONAME | `libbluetooth.so.3` (verified: `docs/stock-inventory/shared-libraries-full.txt`, `docs/stock-inventory/binaries-needed.md` DT_NEEDED list) | `libbluetooth.so.3` | -| `libbluetooth` real name | `libbluetooth.so.3.19.5` | `libbluetooth.so.3.19.15` | +| `libbluetooth` real name | `libbluetooth.so.3.19.5` | `libbluetooth.so.3.19.16` | **SONAME match confirmed against an actual build artifact**, not inferred: -main-checkout `output/target` (a build already run there, outside this -worktree — read-only, not built by this task) has `usr/lib/libbluetooth.so.3 --> libbluetooth.so.3.19.15`, and `readelf -d` on the real `.so` reports +this branch's own `output/target` has `usr/lib/libbluetooth.so.3 -> +libbluetooth.so.3.19.16`, and `readelf -d` on the real `.so` reports `Library soname: [libbluetooth.so.3]`. BlueZ's `libbluetooth` SONAME has been stable at major version 3 since the 4.x/5.x transition, so this was -expected, but it is now verified rather than assumed. **The orchestrator's -own integrated build should still re-check this** (see checklist below) — -the confirmation above comes from a separate, already-built tree on the -main checkout, not from a build of this branch's changes. +expected, but it is verified rather than assumed. Note the libtool revision +moves on a bluez bump (`.3.19.15` under 5.79, `.3.19.16` under 5.86) while +the SONAME does not — which is the whole point of checking the SONAME and +not the filename. ## 2. Init sequence @@ -113,20 +112,20 @@ it so it's a known, deliberate choice rather than an oversight. Auditing `/etc/bluetooth/main.conf` (stock's full verbatim text is in `docs/stock-inventory/etc-configs.md` lines 767-898) against the -bluez5_utils-5.79 package's own compiled-in default (`output/target -/etc/bluetooth/main.conf` on the main checkout's existing build) found +bluez5_utils package's own compiled-in default (`src/main.conf` in the +bluez tarball; audited against 5.79, re-checked against 5.86) found **two settings stock sets explicitly that our image was leaving at the package default**, because no `main.conf` existed in the overlay before this task: -| Setting | Stock | BlueZ 5.79 package default (uncommented → active) | Gap | +| Setting | Stock | BlueZ package default (uncommented → active) | Gap | |---|---|---|---| -| `[General] Name` | `Name = MiSTer` | `#Name = BlueZ` → adapter advertises as `BlueZ 5.79` | **User-visible**: pairing UI on a phone/controller would show "BlueZ 5.79" instead of "MiSTer". | -| `[Policy] AutoEnable` | `AutoEnable = true` (stock's own comment: "Defaults to 'false'" on stock's BlueZ version) | `#AutoEnable=true` (BlueZ 5.79's own comment: "Defaults to 'true'") | **Behaviorally probably fine either way** on 5.79, since upstream's compiled default flipped to `true` since stock's BlueZ version was released — but leaving it unset means correctness depends on an upstream default that happens to agree with stock today, not on anything we assert or would notice if it regressed. | +| `[General] Name` | `Name = MiSTer` | `#Name = BlueZ` → adapter advertises as `BlueZ X.YZ` (`BlueZ 5.86` today) | **User-visible**: pairing UI on a phone/controller would show "BlueZ 5.86" instead of "MiSTer". | +| `[Policy] AutoEnable` | `AutoEnable = true` (stock's own comment: "Defaults to 'false'" on stock's BlueZ version) | `#AutoEnable=true` (BlueZ's own comment, unchanged through 5.86: "Defaults to 'true'") | **Behaviorally probably fine either way** on current BlueZ, since upstream's compiled default flipped to `true` since stock's BlueZ version was released — but leaving it unset means correctness depends on an upstream default that happens to agree with stock today, not on anything we assert or would notice if it regressed. | **Fix:** added `board/mister/de10nano/rootfs-overlay/etc/bluetooth/main.conf` -— the full BlueZ 5.79 package-default file (kept complete, all other -options left as commented documentation, matching this repo's existing +— the full BlueZ package-default file (kept complete, all other options left +as commented documentation, matching this repo's existing `etc/ssh/sshd_config` overlay style of "keep the upstream default file, annotate the deltas") — with exactly these two lines uncommented and set to stock's values, each with a comment explaining why. @@ -134,6 +133,17 @@ to stock's values, each with a comment explaining why. No other settings in stock's `main.conf` were set (everything else was commented / default), so no further deltas exist there. +**The overlay's commented scaffolding is the 5.79 vintage**, not 5.86's. The +2026.08 bump was checked and the drift is **comment-only**: diffing our file's +*active* lines against pristine 5.86's `src/main.conf` yields exactly the five +settings this repo sets on purpose (`Name`, `FastConnectable`, `Privacy`, +`JustWorksRepairing`, `AutoEnable` — the five `ci-tests.sh` asserts). What 5.86 +adds or moves is commented documentation only (`FilterDiscoverable`, +`IdleTimeout`, `CentralAddressResolution`, `ExportClaimedServices`, a reworded +`TemporaryTimeout`/`Channels` comment, `Client` relocated to `[CSIS]`), so +nothing behavioural is stale. Re-syncing the scaffolding to 5.86 is optional +housekeeping, deliberately not done as part of the bump. + ## 5. sixaxis plugin — packaging shape changed upstream (not a gap) Stock ships PS3-controller BT pairing as a **loadable plugin**: @@ -141,16 +151,17 @@ Stock ships PS3-controller BT pairing as a **loadable plugin**: /shared-libraries.md:523`), dlopen'd by `bluetoothd` at runtime from `PLUGINDIR`. -In BlueZ 5.79, `--enable-sixaxis` (set via `BR2_PACKAGE_BLUEZ5_UTILS_ +In current BlueZ, `--enable-sixaxis` (set via `BR2_PACKAGE_BLUEZ5_UTILS_ PLUGINS_SIXAXIS=y`, already on) compiles `plugins/sixaxis.c` **directly into the `bluetoothd` binary** as a builtin plugin — confirmed by reading -the generated `Makefile` in the main checkout's existing build -(`plugins/bluetoothd-sixaxis.o` linked into `src_bluetoothd`) and by -`strings` on the built `bluetoothd`, which contains the `sixaxis_init` / -`sixaxis_exit` / `sixaxis_sdp_cb` symbols and plugin-descriptor string -directly. **No `usr/lib/bluetooth/plugins/` directory exists in the built +the generated `Makefile` (`plugins/bluetoothd-sixaxis.o` linked into +`src_bluetoothd`) and by `strings` on the built +`usr/libexec/bluetooth/bluetoothd`, which contains the sixaxis log strings +and plugin descriptor directly (re-verified on the 5.86 build: 24 matches, +including `plugins/sixaxis.c` and `sixaxis: setting up new device`). **No `usr/lib/bluetooth/plugins/` directory exists in the built image at all** — there's nothing to put there anymore; upstream BlueZ -moved (some time between stock's version and 5.79) toward compiling +moved (some time between stock's version and 5.79, and still so in 5.86) +toward compiling "internal" plugins straight into the daemon rather than shipping them as separate `.so` files. Builtin plugins register themselves automatically unless explicitly disabled (`DisablePlugins=` in `main.conf`, which we @@ -162,8 +173,9 @@ looking for a missing `sixaxis.so` and conclude support was dropped. > **Later correction (see §10):** the plugin being *active* was necessary but > not sufficient — a DS3 still could not connect over Bluetooth, because -> BlueZ 5.79's `ClassicBondedOnly=true` default rejects it before the plugin -> matters. That is fixed in §10 by a backported upstream patch series, not by +> BlueZ's `ClassicBondedOnly=true` default rejects it before the plugin +> matters. That is fixed in §10 by an upstream patch series (backported while +> Buildroot pinned 5.79, and part of bluez itself since 5.83), not by > weakening the setting. ## 6. D-Bus policy — location changed upstream, verified correct @@ -324,8 +336,9 @@ connected over Bluetooth. ### What we were running before -We shipped no `input.conf` at all, so both settings sat at BlueZ 5.79's -compiled-in defaults, read from `profiles/input/device.c:92-93`: +We shipped no `input.conf` at all, so both settings sat at BlueZ's +compiled-in defaults, read from `profiles/input/device.c:92-93` under 5.79 +(`93-94` under 5.86; the values are unchanged): ```c static uhid_state_t uhid_state = UHID_ENABLED; @@ -367,7 +380,8 @@ go back: **What it does not cost: Bluetooth LE.** HIDP is BR/EDR-only, and `profiles/input/hog.c:257-264` reads `UserspaceHID` *only* to test for the `persist` value — it never disables uhid for LE. BLE HID devices keep using -uhid regardless. What is given up is `persist` mode for BR/EDR (new in 5.79) +uhid regardless. What is given up is `persist` mode for BR/EDR (new in 5.79, +still present in 5.86) and future BlueZ-side fixes to the BR/EDR HID path. Judged acceptable because our device quirks live in the `hid-*` kernel drivers, which **both** paths share. @@ -385,18 +399,16 @@ make the PS3 pad work. Doing that drops the encryption requirement for **every BR/EDR HID device** on the system, re-exposing **CVE-2023-45866** (unauthenticated HID injection). -Instead, `board/mister/de10nano/patches/bluez5_utils/` carries upstream's -**four-commit** `CablePairing` series (BlueZ 5.83, Ludovico de Nittis / -Collabora) plus one unrelated HID fix, applied through -`BR2_GLOBAL_PATCH_DIR`: +Instead, DS3 support comes from upstream's **four-commit** `CablePairing` +series (BlueZ 5.83, Ludovico de Nittis / Collabora), which the pinned bluez +now ships on its own: -| Patch | Upstream commit | Role | -|---|---|---| -| `0001` | `20ea34e7` | adds the `CablePairing` D-Bus property | -| `0002` | `56516d6c` | sixaxis plugin sets it during USB cable pairing | -| `0003` | `c5dffe0` | adds `btd_adapter_has_cable_pairing_devices()` — **the one originally missed** | -| `0004` | `ba101f47` | input server listens at `BT_IO_SEC_LOW`, re-raises to `BT_IO_SEC_MEDIUM` for everything *except* a `CablePairing` device | -| `0005` | `e2324f7a` | [bluez#1710](https://github.com/bluez/bluez/pull/1710) — HID report-descriptor off-by-one (5.87). Unrelated to DS3 | +| Commit | Role | +|---|---| +| `20ea34e7` | adds the `CablePairing` D-Bus property | +| `56516d6c` | sixaxis plugin sets it during USB cable pairing | +| `c5dffe0` | adds `btd_adapter_has_cable_pairing_devices()` | +| `ba101f47` | input server listens at `BT_IO_SEC_LOW`, re-raises to `BT_IO_SEC_MEDIUM` for everything *except* a `CablePairing` device | Net effect: a cable-paired DS3 connects, and every other BR/EDR HID device keeps encryption enforced. Upstream closed @@ -404,41 +416,78 @@ keeps encryption enforced. Upstream closed noting DS3 now works *"with limited exposure to CVE-2023-45866 and without changing `ClassicBondedOnly=false`"*. -`0005` is carried separately because it is squarely in the path every -Bluetooth pad here takes — upstream's own commit message names the DualSense -(`playstation 0005:054C:0CE6.0014: unknown main item tag 0x0`). Upstream calls -it benign (the kernel's parser ignores the stray item), so it is a correctness -fix rather than a repair for a user-visible fault. It applies **regardless of -transport** despite the commit message mentioning UHID: the bug is in the SDP -extraction that fills `struct hidp_connadd_req`, which runs *before* the -uhid/kernel-HIDP branch. - -#### The bug this series already had once - -The first cut carried **three** commits and omitted `0003`. It applied cleanly -— `patch` was perfectly happy — while leaving `profiles/input/manager.c` -calling a `btd_adapter_has_cable_pairing_devices()` that nothing defined: an +#### The retired backport directory (removed in the 2026.08 bump) + +While Buildroot pinned bluez **5.79**, which predates 5.83, these commits were +carried as five patch files in `board/mister/de10nano/patches/bluez5_utils/`, +applied through `BR2_GLOBAL_PATCH_DIR` — the four above plus `e2324f7a` +([bluez#1710](https://github.com/bluez/bluez/pull/1710)), the HID +report-descriptor off-by-one, whose upstream commit message names the +DualSense (`playstation 0005:054C:0CE6.0014: unknown main item tag 0x0`). + +**That directory is gone.** Buildroot 2026.08 pins bluez **5.86**, which +contains all five commits, so the version gap the backports existed to close +is closed. They were never a MiSTer delta, and the headers said to delete them +on exactly this event. + +It ended the way it was designed to. The patches were deliberately **not** +version-scoped under `patches/bluez5_utils/5.79/` — `pkg-patches-dirs` +(`package/pkg-utils.mk:166-170`) would have preferred such a subdirectory and +the series would then have stopped applying *silently* on any bump. Unscoped, +the bump made all five fail to apply and the build went red at +`.stamp_patched`, which is the signal that told a human to delete the +directory. Failing closed is the same posture this repo takes on the kernel +hash pins. + +Each was confirmed redundant against the **pristine 5.86 tarball**, with the +exact flags `apply-patches.sh:119` uses, rather than assumed from the release +notes — which matters, because `0005`'s own header predicted 5.87 and it is in +fact already in 5.86: + +| Patch | Result against pristine 5.86 | +|---|---| +| `0001` | `src/device.c`, `src/device.h`: *"Reversed (or previously applied)"*. Its `doc/org.bluez.Device.rst` hunk reports FAILED, but the property **is** documented there (line 374) — upstream reflowed the file after 5.83, so only the context moved | +| `0002` | Hunk FAILED, and this one is **superseded, not merely redundant**: 5.86 calls `device_set_cable_pairing()` *outside* the `CABLE_PAIRING_SIXAXIS` branch (`plugins/sixaxis.c:305`) and adds `server_set_cable_pairing()`. Upstream generalised it beyond the Sixaxis; re-applying the backport would have *narrowed* it | +| `0003` | `src/adapter.c`, `src/adapter.h`: *"Reversed (or previously applied)"* | +| `0004` | all four files: *"Reversed (or previously applied)"* | +| `0005` | `profiles/input/device.c`: *"Reversed (or previously applied)"* — `rd_size = d->unitSize - 1` is at line 873 of 5.86 | + +Also settled by the bump: `package/bluez5_utils/` in 2026.08 ships **no +patches of its own** (2026.05.x shipped four), so nothing of Buildroot's is +being suppressed or fought with either. + +#### The bug the backport series already had once + +Kept because it is the lesson, not the history: the first cut carried **three** +commits and omitted `c5dffe0`. It applied cleanly — `patch` was perfectly happy +— while leaving `profiles/input/manager.c` calling a +`btd_adapter_has_cable_pairing_devices()` that nothing defined: an unconditional compile failure under gcc 14.4's `-Wimplicit-function-declaration`. **"The series applies" and "the series -builds" are different claims**, and only the second one matters. Anyone -re-deriving this series from the issue thread must walk the parent chain. +builds" are different claims**, and only the second one matters. Anyone who +ever has to re-derive this series (a bluez downgrade below 5.83) must walk the +parent chain. + +#### Why the backport, rather than overriding the bluez version — resolved by the bump -#### Why not simply run a newer bluez? +Historical, and no longer a live decision: Buildroot's own bluez is now new +enough. Recorded because it is the reasoning any future "just pin a newer +upstream release" proposal runs into. Overriding `BLUEZ5_UTILS_VERSION` to 5.87 was implemented and then abandoned, -because it cannot build. `package/bluez5_utils/` ships **four patches of its -own**, and `pkg-patch-hash-dirs` (`pkg-utils.mk:164`) always includes -`$(PKGDIR)`, so they are applied to whatever version is built. Against 5.87, -with the exact flags `apply-patches.sh:119` uses, three report *"Reversed (or -previously applied) patch detected"* (they went upstream between 5.79 and -5.87) and one conflicts — and that script runs under `set -e`, so the build -dies at `.stamp_patched`. No override target ≥ 5.83 avoids this, because the -patches went upstream *before* the version we need. +because it could not build. `package/bluez5_utils/` shipped (in 2026.05.x) +**four patches of its own**, and `pkg-patch-hash-dirs` (`pkg-utils.mk:164`) +always includes `$(PKGDIR)`, so they were applied to whatever version was +built. Against 5.87, with the exact flags `apply-patches.sh:119` uses, three +reported *"Reversed (or previously applied) patch detected"* and one +conflicted — and that script runs under `set -e`, so the build died at +`.stamp_patched`. No override target ≥ 5.83 avoided this, because Buildroot's +patches went upstream *before* the version we needed. Two further problems came with it: `bluez5_utils-headers` (not optional — `package/python3/Config.in:13` selects it) reads -`bluez-$(BLUEZ5_UTILS_VERSION).tar.xz` **lazily from the other package**, so -it would fetch 5.87 against a hash file listing only 5.79 — invisible locally, +`bluez-$(BLUEZ5_UTILS_VERSION).tar.xz` **lazily from the other package**, so it +would fetch 5.87 against a hash file listing only 5.79 — invisible locally, since its stale `.stamp_downloaded` short-circuits the step, and fatal only on a clean CI build. And `_DL_VERSION` is `:=` (`pkg-generic.mk:498`), evaluated before `external.mk`, so `legal-info` and CVE metadata would report 5.79 for a @@ -448,76 +497,50 @@ Salvaging it needed a `_PKGDIR` redirect to suppress Buildroot's patches (which also relocates the hash lookup, requiring its `COPYING` lines to be duplicated) plus a headers hash file plus a `_DL_VERSION` override — three fights with the package machinery, and the `_PKGDIR` redirect would **silently drop** any -future Buildroot patch for bluez. `BR2_GLOBAL_PATCH_DIR` is the mechanism -Buildroot actually provides for this, it needs no workarounds, and it fails -*loudly*. Forking the package into `package/` was also considered and rejected: -it would mean owning a ~200-line `.mk` plus Config.in with every sub-option +future Buildroot patch for bluez. `BR2_GLOBAL_PATCH_DIR` was the mechanism +Buildroot actually provides for this: no workarounds, and it fails *loudly* — +which is precisely how the 2026.08 bump announced that the backports were done. +Forking the package into `package/` was also considered and rejected: it would +have meant owning a ~200-line `.mk` plus Config.in with every sub-option forever, and fixing each reverse-dependency. -**These patches are not a MiSTer delta.** They exist solely because Buildroot -2026.05.1 pins bluez5_utils 5.79 (`bluez5_utils.mk:8`), which predates 5.83. -**Delete the whole directory** on the first Buildroot bump that lands ≥ 5.83. - -**Deliberately not version-scoped.** `pkg-patches-dirs` -(`package/pkg-utils.mk:166-170`) will prefer a `$(dir)/$(VERSION)` subdirectory -if one exists, so these could have been filed under -`patches/bluez5_utils/5.79/` and would then stop applying by themselves on any -version bump. That was rejected: it converts a *loud* failure into a *silent* -one. Unscoped, a bump to ≥ 5.83 makes the patches fail to apply and the build -goes red, which is the signal that tells a human to delete this directory; a -bump to some 5.8x still below 5.83 likewise fails loudly rather than quietly -shipping an image with DS3 support removed. Failing closed is the same posture -this repo takes on the kernel hash pins. - **Migration trap.** `plugins/sixaxis.c`'s `setup_device()` short-circuits on an -already-trusted device, so a DS3 that was cable-paired under the *old* BlueZ -never acquires the property and will still fail to connect. Such a pad must be -re-paired: `bluetoothctl remove `, then cable-pair again. +already-trusted device, so a DS3 that was cable-paired under a BlueZ without +this series never acquires the property and will still fail to connect. Such a +pad must be re-paired: `bluetoothctl remove `, then cable-pair again. ### Verification status -- **[VERIFIED]** All five patches apply **in Buildroot's real order** — i.e. - *after* `package/bluez5_utils/`'s own four patches, not against a pristine - tarball — using the exact flags `apply-patches.sh:119` uses - (`patch -F0 -g0 -p1 -t -N`): no fuzz, no rejects. Hunk offsets do occur and - are expected for a backport; an earlier revision of this doc claimed "no - offsets", which was reading `patch --dry-run`'s exit status (0 whether or not - hunks moved). -- **[VERIFIED]** Every symbol the series introduces resolves afterwards — +- **[VERIFIED]** All five backports are redundant against the **pristine 5.86 + tarball** — checked with the exact flags `apply-patches.sh:119` uses + (`patch -F0 -g0 -p1 -t -N`), not inferred from release notes. Per-patch + results are in the table above; `0002` is *superseded* rather than merely + redundant, since upstream widened the call beyond the Sixaxis branch. +- **[VERIFIED]** Both claimed behaviours are present in pristine 5.86: + `server_set_cable_pairing` in `profiles/input/server.c` (line 325, alongside + `get_necessary_sec_level` at 269), and `rd_size = d->unitSize - 1` in + `profiles/input/device.c` (line 873). + + > `BT_IO_SEC_LOW` is **not** a usable marker for this. It was already in + > pristine 5.79 (`profiles/input/server.c:274`) and is in fact the line the + > series replaces, so it distinguishes nothing. `server_set_cable_pairing` + > appears nowhere in 5.79 and is the marker `ci-tests.sh` uses. +- **[VERIFIED]** Every symbol the series needs resolves in 5.86 — `btd_adapter_has_cable_pairing_devices`, `device_is_cable_pairing`, `device_set_cable_pairing`, `server_set_cable_pairing`, - `get_necessary_sec_level` each have ≥1 reference and exactly one definition. - This is the check that would have caught the missing `0003`, and a plain - apply-check never could. -- **[VERIFIED]** Both claimed behaviours are present in the patched tree: - `server_set_cable_pairing` in `profiles/input/server.c`, and - `rd_size = d->unitSize - 1` in `profiles/input/device.c`. - - > An earlier revision of this line cited `BT_IO_SEC_LOW` as the marker. That - > was **vacuous**: the string is already in pristine 5.79 - > (`profiles/input/server.c:274`) and is in fact the line patch `0004` - > replaces, so it distinguished nothing. `server_set_cable_pairing` is - > introduced only by the series and appears nowhere in the unpatched tarball. -- **[VERIFIED]** The series **compiles**. All six files it touches - (`src/device.c`, `src/adapter.c`, `plugins/sixaxis.c`, - `profiles/input/{device,manager,server}.c`) cross-compile clean for ARM with - this repo's own `arm-buildroot-linux-gnueabihf-gcc` 14.4 under - `-Wall -Werror=implicit-function-declaration` — the exact error class the - originally-missing `0003` would have tripped. Run against a copy of the real - configured build tree with Buildroot's own four patches already applied, then - ours on top. + `get_necessary_sec_level`. This is the check that caught the missing + `c5dffe0` in the backport era, and a plain apply-check never could. +- **[VERIFIED]** bluez **5.86 builds** with the backport directory removed: + `bluez5_utils-dirclean` then a full `make all` reaches + `.stamp_target_installed` on `output/build/bluez5_utils-5.86`. - **[VERIFIED]** `CONFIG_BT_HIDP=y` and `CONFIG_UHID=y` in the resolved kernel `.config`. - **[CI]** `ci-tests.sh` asserts both `input.conf` values, `CONFIG_BT_HIDP`, - and that `server_set_cable_pairing` reached the built source tree — so the - series silently ceasing to apply cannot produce a green build. - - > **`.stamp_patched` is the trap here.** Buildroot never revisits it, so - > adding or changing patches on an already-built tree is a silent no-op: an - > incremental `make all` over an `output/` that predates this branch ships a - > `bluetoothd` with no CablePairing support, indistinguishable in every - > shipped file from a correct build. Run `make bluez5_utils-dirclean` after - > touching anything in `board/mister/de10nano/patches/bluez5_utils/`. + and that `server_set_cable_pairing` is in the built bluez source tree. That + last gate outlived the backports on purpose: it asserts the *behaviour* + reached the tree, never which mechanism put it there, so it now guards + against the pinned bluez losing the series (a downgrade below 5.83, a vendor + fork) exactly as it once guarded against the patches failing to apply. - **[HW — NOT DONE]** DS3/SIXAXIS USB cable-pair, then connect over Bluetooth. This is the actual claim of the change and it has **not** been tested on hardware. diff --git a/docs/buildroot-config.md b/docs/buildroot-config.md index 0b900c44..dabecb3a 100644 --- a/docs/buildroot-config.md +++ b/docs/buildroot-config.md @@ -2136,7 +2136,7 @@ through `savedefconfig` to prove every symbol really exists — but the file itself is not the machine's output. ROUND-TRIP RESULT, re-run 2026-09-02 after the bootloader stanza landed -(Buildroot 2026.05.2). savedefconfig ADDED nothing (so no symbol is +(Buildroot 2026.05.2, re-checked at 2026.08). savedefconfig ADDED nothing (so no symbol is implied-but-unstated) and DROPPED exactly six lines as non-divergent from a kconfig default: @@ -2183,7 +2183,7 @@ something a boot artifact should depend on. (`scripts/check-config-fragments.sh` comes from `common`, §2.1). glibc is already the default C library for the internal toolchain, so it is not a line (savedefconfig drops non-divergent symbols); musl is a project-wide non-goal. -- `BR2_KERNEL_HEADERS_7_0`: pins the headers SERIES explicitly, for exactly +- `BR2_KERNEL_HEADERS_7_1`: pins the headers SERIES explicitly, for exactly the reason §3.2 spells out at length — do not "fix" it to `BR2_KERNEL_HEADERS_AS_KERNEL` to keep headers in lockstep with the kernel. Under AS_KERNEL the kernel version arrives as the free-form string @@ -2193,19 +2193,36 @@ something a boot artifact should depend on. (`scripts/check-config-fragments.sh` dead compatibility code and syscall-fallback paths for kernels this board will never run. - WHY 7_0 AND NOT 7_2. Buildroot 2026.05.2 offers NO 7.2 headers series. - `package/linux-headers/Config.in.host` tops out at `BR2_KERNEL_HEADERS_7_0` - (`:55-58`, resolving to 7.0.14 at `:477`); the series list is - 5.10/5.15/6.1/6.6/6.12/6.18/7.0. 7_0 is therefore the newest series + WHY 7_1 AND NOT 7_2. Buildroot 2026.08 offers NO 7.2 headers series. + `package/linux-headers/Config.in.host` tops out at `BR2_KERNEL_HEADERS_7_1` + (`:55`, resolving to 7.1.13 at `:481`); the series list is + 5.10/5.15/6.1/6.6/6.12/6.18/7.1. 7_1 is therefore the newest series Buildroot has that is <= our 7.2.3 kernel, and headers OLDER than the running kernel is the supported direction — the kernel's uapi is forward-compatible by guarantee. §3.2 documents the diff-the-uapi discipline that comes with this; the same discipline applies here on any - kernel or Buildroot bump, and the range to diff is 7.0.14 -> 7.2.3. + kernel or Buildroot bump, and the range to diff is 7.1.13 -> 7.2.3. + + WAS 7_0 UNTIL THE 2026.08 BUMP, AND THE WAY IT BROKE IS INSTRUCTIVE. + Buildroot 2026.08 did not merely add 7.1 — it REMOVED 7.0, moving + `BR2_KERNEL_HEADERS_7_0` into `Config.in.legacy` ("kernel headers version + 7.0.x are no longer supported", selecting `BR2_LEGACY`). A retired symbol + does not fail loudly at merge time: `BR2_KERNEL_HEADERS_7_0=y` still SET, + so `check-config-fragments.sh`'s (b) assertion — every fragment symbol + survives `olddefconfig` — passed with **0 dropped**, because the literal + line really was in the resolved config. What collapsed was everything + downstream: the headers choice fell back to `AS_KERNEL` (precisely the + outcome the paragraph above warns against), and with it the libc choice + fell from **glibc to uClibc**. Only the (d) GOLDEN HASH caught it. Two + lessons: a `Config.in.legacy` demotion is invisible to a symbol-presence + check, and the golden hash is not bookkeeping — it is the backstop that + makes a line bump reviewable at all. RE-CHECK ON EVERY BUILDROOT BUMP: a Buildroot bump moves the point release - inside a series on its own, and the day Buildroot adds a 7.2 series this - pin should move to it in a deliberate commit. + inside a series on its own, it can RETIRE a series out from under this pin + (as 2026.08 did), and the day Buildroot adds a 7.2 series this pin should + move to it in a deliberate commit. Diff the resolved configs, do not just + regenerate the golden hash. ### 6.3 Download integrity — `BR2_GLOBAL_PATCH_DIR` (DE25) and the shared hash file @@ -2468,9 +2485,10 @@ That is the answer to `de25-implementation-path.md` §8 Q6, and it is negative. `_CUSTOM_VERSION=y`, `_CUSTOM_VERSION_VALUE="v2.15.0"`, `_PLATFORM="agilex5"`, `_BL31=y`, `_IMAGES="bl31.bin"`. -Mainline TF-A v2.15.0. Buildroot 2026.05.2's newest offer is v2.12 -(`boot/arm-trusted-firmware/Config.in`), which has no Agilex 5 platform, so a -custom version is not a preference here — it is the only route. +Mainline TF-A v2.15.0. Buildroot 2026.08's newest offer is still v2.12 +(`boot/arm-trusted-firmware/Config.in:60`) — the 2026.08 bump did not move it — +and v2.12 has no Agilex 5 platform, so a custom version is not a preference +here: it is the only route. `plat/intel/soc/agilex5/` exists at v2.15.0 and its `socfpga_plat_def.h` sets `BL31_BASE 0x80000000`, which is exactly the load/entry address the SoC64 binman FIT description hardcodes for the `atf` image. Verified against the @@ -2498,7 +2516,19 @@ assertion and that line describe the same thing. `# BR2_TARGET_UBOOT_FORMAT_BIN is not set`. Mainline v2026.07 (released 2026-07-07; v2026.10 was at -rc when this was -written). Buildroot 2026.05.2 ships 2026.04, so again a custom version. +written). Buildroot 2026.05.2 shipped 2026.04, so this had to be a custom +version. + +**THE 2026.08 BUMP CLOSED THAT GAP AND THE PIN HAS NOT MOVED YET.** Buildroot +2026.08's `BR2_TARGET_UBOOT_LATEST_VERSION` is `2026.07` +(`boot/uboot/Config.in:88`) — the exact version this fragment pins by hand. So +the custom-version pin is now redundant *in version terms*, and switching to +`LATEST` would also make the build-system line below unnecessary (see why in +the next paragraph). Deliberately NOT changed as part of the 2026.08 bump: the +DE25 stack is not built or booted in this repo's CI, so swapping the U-Boot +source out from under an unbooted board belongs in its own commit, with a +build, rather than riding along on a Buildroot bump. Left as a flagged +simplification. NOTE THE BUILD-SYSTEM LINE, it is not optional. `BR2_TARGET_UBOOT_BUILD_SYSTEM` defaults to KCONFIG *only* if diff --git a/docs/package-manifest.md b/docs/package-manifest.md index 9b52a04e..62f55890 100644 --- a/docs/package-manifest.md +++ b/docs/package-manifest.md @@ -5,8 +5,9 @@ version-jump risk owners **P3.6** (Samba), **P3.7** (SSH/FTP), **P3.9** (Python) > **Which Buildroot this describes (read first).** The mapping below was *established* > against **2026.02.3** — that is the tree every "verified by reading the file" claim -> here was read from. **The image now ships Buildroot 2026.05.2** (bumped by hand in -> PR #54, then by Renovate to 2026.05.2). Rows touched by that bump have been updated in place and say so inline; the +> here was read from. **The image now ships Buildroot 2026.08** (bumped by hand to +> 2026.05.x in PR #54, by Renovate within that line, then by hand to 2026.08 on +> 2026-09-05). Rows touched by those bumps have been updated in place and say so inline; the > clearest case is PCRE1, removed upstream in 2026.05 and consequently dropped here > (see the `libpcre.so.1` / `libpcreposix.so.0` rows). Untouched rows still carry their > 2026.02.3 provenance, which is the honest thing for them to carry — a version string @@ -56,7 +57,7 @@ cross-checks for the two riskiest ones): | 9 | `libpng16.so.16` | **yes** | `BR2_PACKAGE_LIBPNG` 1.6.58 — the "16" is libpng's parallel-install branch tag, part of the package's identity | | 10 | `libz.so.1` | **yes** | `BR2_PACKAGE_ZLIB_NG` 2.3.3 in `ZLIB_COMPAT` mode (provider under the `BR2_PACKAGE_ZLIB` choice; `BR2_PACKAGE_LIBZLIB` is the Buildroot default, we select zlib-ng instead). **SONAME is unchanged** — compat mode installs `libz.so.1`, so nothing that `DT_NEEDS` it can tell the difference | | 11 | `libImlib2.so.1` | **yes** | `BR2_PACKAGE_IMLIB2` 1.12.5. **Specifically checked per the task's flag** — current Arch `imlib2` 1.12.6-1 sonames page still lists only `libImlib2.so.1` | -| 12 | `libbluetooth.so.3` | **yes** | `BR2_PACKAGE_BLUEZ5_UTILS` 5.79. **Specifically checked per the task's flag** — current Arch `bluez-libs` package `Provides: libbluetooth.so=3`; Debian/Ubuntu still name the runtime package `libbluetooth3` at recent bluez versions | +| 12 | `libbluetooth.so.3` | **yes** | `BR2_PACKAGE_BLUEZ5_UTILS` 5.86. **Specifically checked per the task's flag** — current Arch `bluez-libs` package `Provides: libbluetooth.so=3`; Debian/Ubuntu still name the runtime package `libbluetooth3` at recent bluez versions | **No project-threatening ABI break exists in this set.** This is the single most important fact this task turned up: nothing forces a redesign of the ABI-parity @@ -226,7 +227,7 @@ Columns: **SONAME** | **stock realfile** (version hint, from `shared-libraries.m | SONAME | stock realfile | Buildroot package | version | bump? | notes | |---|---|---|---|---|---| -| `libbluetooth.so.3` | `libbluetooth.so.3.19.5` | `BR2_PACKAGE_BLUEZ5_UTILS` (+`_PLUGINS_SIXAXIS`, `_DEPRECATED` for hciconfig/hcitool/sdptool/rfcomm/l2ping/hcidump parity) | 5.79 | no | **verified**: Arch's current `bluez-libs` package still `Provides: libbluetooth.so=3` — one of the 12 critical SONAMEs, confirmed safe | +| `libbluetooth.so.3` | `libbluetooth.so.3.19.5` | `BR2_PACKAGE_BLUEZ5_UTILS` (+`_PLUGINS_SIXAXIS`, `_DEPRECATED` for hciconfig/hcitool/sdptool/rfcomm/l2ping/hcidump parity) | 5.86 | no | **verified**: Arch's current `bluez-libs` package still `Provides: libbluetooth.so=3` — one of the 12 critical SONAMEs, confirmed safe | ### PAM / capabilities @@ -455,15 +456,26 @@ Several versions in this table have since moved, across two Buildroot bumps. The 2.12**. The two whose parity analyses that invalidates are flagged at the top of [`samba-parity.md`](samba-parity.md) and [`ssh-ftp-parity.md`](ssh-ftp-parity.md) — and the OpenSSH move broke SSH outright on the shipped image, which that document now -records. bluez 5.79 and dhcpcd 10.2.4 are unchanged at the current pin (verified -against `output/build/`, 2026-08-24). +records. + +The **2026.08 line bump** (2026-09-05) then moved the toolchain itself — +**gcc 14.4.0 → 15.3.0**, **glibc 2.43 → 2.44**, gdb 15.2 → 16.3 — plus +**bluez 5.79 → 5.86**, Samba 4.24.5 → 4.24.6 and OpenSSL 3.6.3 → 3.6.4. The bluez +move is the consequential one for this document: 5.86 already contains the +upstream `CablePairing` series this repo had been backporting, so +`board/mister/de10nano/patches/bluez5_utils/` was deleted (see +[`bluetooth-parity.md`](bluetooth-parity.md) §10). dhcpcd is **still 10.2.4** and +its `--with-hooks` host-probing bug is **still unfixed upstream** — Buildroot +2026.08's `package/dhcpcd/dhcpcd.mk` still passes no `--with-hooks`, so +`external.mk`'s override remains load-bearing. Versions verified against +`make show-info` on the 2026.08 build, 2026-09-05. | Role | Stock | Buildroot package | BR 2026.02.3 version | Init script (P2.3 parity) | |---|---|---|---|---| | SMB/CIFS file server | Samba 4.14.6 (`smbd`, `nmbd`) | `BR2_PACKAGE_SAMBA4` | 4.23.8 | `S91smb` | | SSH server | OpenSSH 8.6p1 (`sshd`) | `BR2_PACKAGE_OPENSSH` | 10.2p1 | `S50sshd` | | FTP server | ProFTPD (stock ships it, exact version not in IKCONFIG) | `BR2_PACKAGE_PROFTPD` | 1.3.8d | `S50proftpd` | -| Bluetooth stack | bluez 5.61 (`bluetoothd`) | `BR2_PACKAGE_BLUEZ5_UTILS` | 5.79 | `S45bluetooth` → symlink to `/bin/bluetoothd` control script (P0.3 finding) | +| Bluetooth stack | bluez 5.61 (`bluetoothd`) | `BR2_PACKAGE_BLUEZ5_UTILS` | 5.86 | `S45bluetooth` → symlink to `/bin/bluetoothd` control script (P0.3 finding) | | WiFi supplicant | wpa_supplicant 2.x | `BR2_PACKAGE_WPA_SUPPLICANT` (+`_NL80211`, +`_WEXT` — stock's `/etc/network/interfaces` passes `-D nl80211,wext`, both drivers must be built) | 2.11 | invoked from `ifupdown` `pre-up` in `/etc/network/interfaces`, not its own S-script | | DHCP client | dhcpcd | `BR2_PACKAGE_DHCPCD` | 10.2.4 | `S41dhcpcd` | | NTP daemon | `ntpd` (classic ntp.org, not chrony/openntpd) | `BR2_PACKAGE_NTP` | 4.2.8p18 | `S49ntp` | @@ -477,8 +489,8 @@ against `output/build/`, 2026-08-24). | HTTP client (Downloader, scripts) | curl 7.78.0 | `BR2_PACKAGE_LIBCURL` +`_CURL` (installs the CLI, off by default) +`_OPENSSL` (TLS backend parity — stock's `curl` links `libcrypto`/`libssl`, not GnuTLS) | 8.20.0 | n/a | | Init/shell userland | BusyBox 1.33.1 (274 applets) | `BR2_PACKAGE_BUSYBOX` | 1.37.0 | provides `rcS`/`rcK`, most of `/bin` | | Privilege elevation | sudo | `BR2_PACKAGE_SUDO` | 1.9.17p2 | n/a | -| Bluetooth legacy tools (`hciconfig`, `hcitool`, `sdptool`, `rfcomm`, `l2ping`, `hcidump`) | present in stock | `BR2_PACKAGE_BLUEZ5_UTILS_DEPRECATED=y` | 5.79 | upstream bluez gates these behind this option now | -| PS3 controller pairing | `sixaxis.so` bluez plugin | `BR2_PACKAGE_BLUEZ5_UTILS_PLUGINS_SIXAXIS=y` | 5.79 | pulls in `_PLUGINS_HID` transitively (`select`, don't set separately) | +| Bluetooth legacy tools (`hciconfig`, `hcitool`, `sdptool`, `rfcomm`, `l2ping`, `hcidump`) | present in stock | `BR2_PACKAGE_BLUEZ5_UTILS_DEPRECATED=y` | 5.86 | upstream bluez gates these behind this option now | +| PS3 controller pairing | `sixaxis.so` bluez plugin | `BR2_PACKAGE_BLUEZ5_UTILS_PLUGINS_SIXAXIS=y` | 5.86 | pulls in `_PLUGINS_HID` transitively (`select`, don't set separately) | **MT-32 / soundfont note (P3.8):** `MidiLink.INI`, `mt32-rom-data/`, and `soundfonts/` are **not** Linux userland — they are data files shipped under `files/linux/` on the @@ -614,7 +626,7 @@ for real incompatibilities is **reporting them upstream to `Downloader_MiSTer` / community script authors**, not silently pinning an EOL interpreter — Buildroot 2026.02 gives us no easy way to pin 3.9 even if we wanted to. -### bluez 5.61 → 5.79 (owner: P3.5) — confirmed safe +### bluez 5.61 → 5.86 (owner: P3.5) — confirmed safe `libbluetooth.so.3` unchanged (see headline finding). Behavior-level: bluez5's `main.conf` schema (verbatim stock config uses `FastConnectable`, `Privacy`, @@ -1123,7 +1135,7 @@ BR2_PACKAGE_BUSYBOX=y # 1.38.0 in this Buildroot (busybo - **Buildroot ref this mapping was read from**: branch `2026.02.x` @ `679b9ead7620bbf193620d1ebf56f53c1764d37a` = tag `2026.02.3`. **The image now ships - 2026.05.2** — see the note at the top of this document for what that does and does not + 2026.08** — see the note at the top of this document for what that does and does not change. - **12/12 critical ABI-contract SONAMEs (PLAN §3) confirmed at the same major version** in Buildroot 2026.02.3, including the two PLAN flagged as highest-risk diff --git a/docs/renovate.md b/docs/renovate.md index 72fd2aa9..6fde11cf 100644 --- a/docs/renovate.md +++ b/docs/renovate.md @@ -47,7 +47,7 @@ for the specific pieces most likely to need a fix on the first live run. | Pin | File(s) | Mechanism | Hash companion | |---|---|---|---| -| Buildroot release | `Makefile` (`BUILDROOT_VERSION`) | `customManagers` regex, `github-tags` datasource, `allowedVersions` locked to `2026.05.x` | `BUILDROOT_SHA256` — **auto-refreshed since 2026-08-24** by `renovate-hash-sync.yml` (`hash-sync-buildroot.sh`, case 6) from buildroot.org's GPG-signed `.sign` manifest; **manual** before that date (this row used to say so), and the `make buildroot-showsig` transcription remains the fallback — see below. **Since 2026-09-02 a second companion:** `configs/fragments/golden.sha256` — the resolved-config hashes `scripts/check-config-fragments.sh` asserts per Buildroot version — is recorded for the new version by case 8 (`hash-sync-golden.sh`) in the same PR; if that case skips, `lint-config` only *warns* on the missing lines and the manual step is `scripts/check-config-fragments.sh --update-golden` + commit | +| Buildroot release | `Makefile` (`BUILDROOT_VERSION`) | `customManagers` regex, `github-tags` datasource, `allowedVersions` locked to `2026.08.x` | `BUILDROOT_SHA256` — **auto-refreshed since 2026-08-24** by `renovate-hash-sync.yml` (`hash-sync-buildroot.sh`, case 6) from buildroot.org's GPG-signed `.sign` manifest; **manual** before that date (this row used to say so), and the `make buildroot-showsig` transcription remains the fallback — see below. **Since 2026-09-02 a second companion:** `configs/fragments/golden.sha256` — the resolved-config hashes `scripts/check-config-fragments.sh` asserts per Buildroot version — is recorded for the new version by case 8 (`hash-sync-golden.sh`) in the same PR; if that case skips, `lint-config` only *warns* on the missing lines and the manual step is `scripts/check-config-fragments.sh --update-golden` + commit | | Kernel (6.18.y longterm) | `configs/fragments/de10nano.fragment` (`BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE`) — the ONE file both DE10 stacks share since the 2026-09 fragment split | one `customManagers` regex on that file + a `customDatasources` entry over `kernel.org/releases.json`, filtered to `moniker=longterm` and the `6.18.` prefix; `allowedVersions` locked to `6.18.y` as defense in depth. Same `depName` for both files, so Renovate emits **one PR touching both** | `board/mister/de10nano/patches/linux/linux.hash` — auto-refreshed by `renovate-hash-sync.yml` from kernel.org's signed `sha256sums.asc` | | Kernel (RT/beta, the **7.2 line**) | `configs/mister_rt.fragment` (same symbol, different line) | a **separate** `customManagers` regex + its own `kernelStable72` datasource; `allowedVersions` locked to `/^7\.2(\.\d+)?$/`. Labeled `rt-kernel-pin` + `needs-manual-version-check`. **Rewritten 2026-08-17** when 7.2 released: the datasource was `kernelMainline` (`moniker=mainline`) and the depName `kernel-mainline-rt`. Both were right while 7.2 was in `-rc` and wrong the moment it shipped — mainline moves to 7.3-rc1 about two weeks later, so the old filter would have dragged the variant straight back off the line it had just reached. The filter is now **moniker-agnostic and version-scoped**, because the 7.2 line changes moniker underneath us: today 7.2 is the `mainline` entry and no 7.2.y stable release exists yet, and once 7.2.1 ships it becomes the `stable` entry instead. The matchString accepts two- *and* three-component values for the same reason | `board/mister/de10nano/patches/linux/linux.hash` — **auto-refreshed since 2026-08-17** by `renovate-hash-sync.yml` (`hash-sync-kernel.sh --pin=rt`) from kernel.org's signed `sha256sums.asc`, same as the 6.18 pin. This row says the opposite of what it said before that date, and the reason is that the pin changed sides, not that the rule loosened: an `-rc` is fetched as a cgit `.tar.gz` snapshot upstream signs in no way, so its hash could only be hand-written TOFU; a 7.2.y release is an ordinary `.tar.xz` covered by the signed manifest. The script still **refuses** any `-rc` for either pin, leaving the build to fail closed | | 10 driver commit-SHA pins | `package/{rtl8812au,rtl8814au-morrownr,rtl8821au-morrownr,rtl8821cu-morrownr,rtl8188fu,rtl8188eu-aircrack-ng,rtl88x2bu,rtl8852cu-morrownr,xone,midilink}/*.mk` | `customManagers` regex per package, `git-refs` datasource tracking the upstream default branch's HEAD via `currentDigest` | matching `.hash` file — auto-refreshed by `renovate-hash-sync.yml` | @@ -540,22 +540,46 @@ package instead), so no submodule pin is expected to land. ## Kernel/Buildroot bump scope, restated Both the Buildroot and kernel `customManagers` entries are intentionally -narrow: `allowedVersions` locks Buildroot to the `2026.05.x` line and the +narrow: `allowedVersions` locks Buildroot to the `2026.08.x` line and the kernel to `6.18.y`. Neither is meant to propose a Buildroot major/minor bump or a kernel LTS-line change — those are larger undertakings (new toolchain defaults, a fresh patch-carry audit) that deserve a deliberate, human-initiated upgrade, not a routine Renovate PR. -**This has happened once, exactly that way.** The Buildroot line moved -`2026.02.x` → `2026.05.x` by hand in PR #54, and the `allowedVersions` regex -moved with it. Note what the line number does *not* tell you: **2026.02 was -never an LTS.** Buildroot's LTS releases are the February ones on a two-year -cadence — **2025.02** was the last, **2027.02** is expected to be the next — so -`2026.05` is an interim non-LTS line, chosen for currency, not longevity. When -the next line bump comes, update four things together: `BUILDROOT_VERSION` and -`BUILDROOT_SHA256` in the `Makefile` (the hash from -`make buildroot-showsig`, never from a tarball you just downloaded), this -`allowedVersions` regex, the row in the table above, and the golden config -hashes (`scripts/check-config-fragments.sh --update-golden`, case 8 above — -read the normalised-config diff before committing; a line bump is exactly -when defaults move). +**THIS IS WHY A LINE BUMP NEVER SHOWS UP AS A RENOVATE PR**, and it is worth +stating plainly because the absence looks like a broken manager. It is not one. +The manager extracts fine, `github-tags` sees the new tag, and `loose` +versioning orders the versions correctly — `2026.08` parses to `[2026, 8]` and +`2026.05.2` to `[2026, 5, 2]`, so the element-wise compare puts `2026.08` +ahead; the differing component *count* is not what stops it, and neither is the +absence of a patch component. `allowedVersions` is what filters the candidate +out, deliberately, so that moving the line stays a human decision. + +**This has now happened twice, the same way both times.** The Buildroot line +moved `2026.02.x` → `2026.05.x` by hand in PR #54, and `2026.05.x` → +`2026.08.x` on 2026-09-05; the `allowedVersions` regex moved with it each time. +Note what the line number does *not* tell you: **neither 2026.02 nor 2026.08 is +an LTS.** Buildroot's LTS releases are the February ones on a two-year cadence +— **2025.02** was the last, **2027.02** is expected to be the next — so +`2026.08` is an interim non-LTS line, chosen for currency, not longevity. When +the next line bump comes, update **five** things together: + +1. `BUILDROOT_VERSION` and `BUILDROOT_SHA256` in the `Makefile` — the hash from + `make buildroot-showsig`, never from a tarball you just downloaded. +2. This `allowedVersions` regex. Bump it *with* the Makefile pin, never + separately: left behind it silently freezes the pin, moved ahead it invites + a line jump nobody reviewed. +3. The row in the table above. +4. The golden config hashes (`scripts/check-config-fragments.sh + --update-golden`, case 8 above). +5. **The resolved-config diff — actually read it.** A line bump is exactly when + defaults move, and the golden hash only tells you *that* something moved. + The 2026.08 bump is the cautionary tale: Buildroot retired the 7.0 + kernel-headers series into `Config.in.legacy`, and because a retired symbol + still *sets* cleanly, the fragment-survival check passed with 0 dropped + while the DE25 toolchain silently fell from glibc to uClibc. Diffing the + normalised configs against the previous line caught it; nothing else would + have. Resolve the old line's configs with + `CHECK_CONFIG_BR_DIR= scripts/check-config-fragments.sh --keep` + and diff `output-config-check//normalised.config` against the new + run's. diff --git a/docs/ssh-ftp-parity.md b/docs/ssh-ftp-parity.md index 5bb0f4db..f03911a2 100644 --- a/docs/ssh-ftp-parity.md +++ b/docs/ssh-ftp-parity.md @@ -84,7 +84,7 @@ version (`$OpenBSD: sshd_config,v 1.105` header, OpenSSH 10.2p1 per |---|---|---|---| | `PermitRootLogin` | `yes` (uncommented) | `yes` (uncommented, comment added explaining why) | **kept, parity preserved** | | `UsePAM` | `yes` | `yes` | **kept, parity preserved** | -| `AuthorizedKeysFile` | `.ssh/authorized_keys` | same | identical | +| `AuthorizedKeysFile` | `.ssh/authorized_keys` | `.ssh/authorized_keys` **+ `/media/fat/linux/authorized_keys`** | **intentional divergence, added 2026-09-05** — see §1.3 | | `PermitUserEnvironment` | `yes` | `yes` (comment added: MiSTer scripts rely on it) | identical | | `Subsystem sftp` | `/usr/libexec/sftp-server` | same | identical | | `HostKey` lines | commented defaults (`/etc/ssh/ssh_host_{rsa,dsa,ecdsa,ed25519}_key`) | uncommented, repointed at `/etc/ssh_keys/...`, **no DSA entry** | intentional divergence — ADR 0015, not new | @@ -111,6 +111,62 @@ client (year-2016-ish) may need `-oHostKeyAlgorithms=+ssh-rsa` or similar to connect. Worth a line in the user-facing FAQ (P4.8), alongside the ADR 0015 host-key-mismatch note. +### 1.3 User `authorized_keys` on the FAT partition — the second `AuthorizedKeysFile` path + +**The gap.** An update replaces `linux.img` wholesale, and `/root/.ssh` lives *inside* +that file — so a key there is destroyed by every update. That is the "persistent state +must live on `/media/fat`" trap, and it had been costing a manual re-push on every single +flash. **This is the durable reason for the second path, and it does not depend on how +`/` happens to be mounted.** + +Getting a key into the stock-parity path is awkward besides. `/` is mounted **read-only at +boot** (`ro` on the cmdline; inittab's remount-rw line is deliberately left commented, +ADR 0011) and a freshly built image ships **no `/root/.ssh` at all** — only +`/root/.config/mc/`. It *does* become writable later: `/etc/profile` ends with +`mount -o remount,rw /` on interactive login, which is how `/` ever becomes writable at +all (stock parity — see [`init-parity.md`](init-parity.md), the `/etc/profile` row). So a +key **can** be placed there by hand — it just requires logging in first, which is circular +when the key *is* the login method, and it still does not survive the next update. + +> An earlier revision of this section claimed `/root/.ssh` "cannot be written at runtime". +> That was wrong: the login-time remount makes it writable. Corrected after observing a +> booted rig report `/dev/loop0 on / type ext4 (rw,...)`. The conclusion is unchanged — +> only the reasoning needed to be right. + +**The fix.** `sshd` accepts multiple `AuthorizedKeysFile` paths and tries each in turn, so +the shipped config now lists the stock path *plus* `/media/fat/linux/authorized_keys`. +Nothing else changes: no init script, no bind-mount, no `user-startup.sh` hook, no new +persistence image. + +**Why not reuse ADR 0015's `ssh.ext4`?** It was considered and rejected. That mechanism is +right for *host* keys because the **device** writes them: an ext4 image inside a file on +the FAT partition, mounted rw at `/etc/ssh_keys`. But an `authorized_keys` file is written +by the **user**, and an ext4-image-in-a-file cannot be opened from Windows or macOS with a +card reader — and editing it on the box requires the very shell access the key is meant to +grant, which is circular for anyone setting key auth up for the first time. The exFAT +partition is writable from every OS with no tooling. So the split is *who writes the +file*: machine-written state goes in `ssh.ext4`, user-supplied state goes on exFAT. + +**`StrictModes` stays on (default `yes`), and the FAT path satisfies it.** `sshd` rejects +an `authorized_keys` whose file or parent directories are group- or world-writable. The +initramfs mounts the partition `fmask=0022,dmask=0022` with no `uid`/`gid` options +(`board/mister/de10nano/initramfs-overlay/init:27`), so the file lands root-owned `0755` +under `0755` parents — owner-writable only. Those mount options are **ours** and fixed, so +this cannot be invalidated by a card mounted differently elsewhere. Disabling +`StrictModes` was never necessary and `scripts/ci-tests.sh` now fails if someone does it. + +**Verified on hardware**, not reasoned about: a second `sshd` on port 2223 configured with +*only* the FAT path and `StrictModes yes` accepted a key login (OpenSSH 10.5p1, exFAT, +real board). The shipped config additionally passes `sshd -t` and reports both paths under +`sshd -T` on the device. + +**CI:** `scripts/ci-tests.sh` asserts the FAT path is present in the **shipped** +`sshd_config` (not the overlay source) and that `StrictModes no` is absent — dropping +either would otherwise return every user to "your key is gone after each update" with +nothing failing. User-facing instructions are in +[the FAQ](user/faq.md#ssh-key-persist). + + ## 2. FTP — the actual gap, and what turned out *not* to be one ### 2.1 Missing init script — confirmed and fixed diff --git a/docs/user/faq.md b/docs/user/faq.md index b2afca21..13cafc8c 100644 --- a/docs/user/faq.md +++ b/docs/user/faq.md @@ -86,6 +86,46 @@ running this image. --- + +## How do I log in with an SSH key, and make it survive image updates? + +Put your **public** key in a file called `authorized_keys` in the `linux` folder on the +card's main (exFAT) partition — the same partition you see when you put the card in your +PC: + +``` +/media/fat/linux/authorized_keys # on the box +\linux\authorized_keys # from Windows/macOS with a card reader +``` + +Paste in the contents of your **`.pub`** file (e.g. `~/.ssh/id_ed25519.pub`) — one key +per line, more than one key is fine. That's the whole procedure: no shell access needed, +no script to edit, and it works from any OS. Then connect normally: + +```sh +ssh -i ~/.ssh/id_ed25519 root@mister.lan +``` + +**Why that location, and why it survives an update.** An OS update replaces `linux.img` +wholesale. The usual place for a key, `/root/.ssh/authorized_keys`, lives *inside* that +file, so the update throws it away. (You *can* put a key there once you are logged in — +the root filesystem starts read-only and becomes writable when you log in — but you need +to be logged in first, which is awkward when the key is how you wanted to log in, and it +is gone again after the next update.) The exFAT partition is never reflashed, so a key +kept there is picked up again after every update. `sshd` reads both locations, so you do +not have to choose. + +This is the same principle as the per-device host keys above: anything that must outlive +an update lives on the data partition, not in the image. + +> **Note:** the key file is read by the SSH server as root, so treat the card as you would +> any machine you can log into. Put only your **public** key there — never a private key. +> Adding a key does not disable password login; if you want key-only access, set +> `PasswordAuthentication no` in `/etc/ssh/sshd_config` (that change lives in the image, +> so it is undone by an update). + +--- + ## What actually changed vs. stock? | | Stock | This project | diff --git a/docs/version-delta.md b/docs/version-delta.md index f3d2eca9..e226ac9f 100644 --- a/docs/version-delta.md +++ b/docs/version-delta.md @@ -2,7 +2,7 @@ Stock MiSTer froze its entire userland at **Buildroot 2021.02.4** (glibc 2.31, Linux 5.15.1) and has taken **no `.y` stable updates** on the kernel and few on the -packages since. This project rebases the whole stack on **Buildroot 2026.05.2** — +packages since. This project rebases the whole stack on **Buildroot 2026.08** — roughly **five years** of upstream security and correctness work, on a base with a real update path (Renovate-tracked, P4.6/P4.7). @@ -23,12 +23,12 @@ security-update path), and it belongs in the release notes. > whenever Renovate lands a bump — when in doubt, the defconfig and `Makefile` pins are > the ground truth and this table is a summary of them. -| Component | Stock (2021.02.4) | Ours (2026.05.2) | Note | +| Component | Stock (2021.02.4) | Ours (2026.08) | Note | |---|---|---|---| -| Buildroot | **2021.02.4** | **2026.05.2** | ~5 years of the whole distro | +| Buildroot | **2021.02.4** | **2026.08** | ~5 years of the whole distro | | Linux kernel | **5.15.1** (forked Nov 2021, **never merged a single 5.15.y**) | **6.18** LTS — the `.y` moves with upstream stable, so the pin (`BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE`, `configs/fragments/de10nano.fragment`) is the only place it is written down; hardware-validated at 6.18.33, 6.18.38 and 6.18.41 | on a stable `.y` line with security backports | -| glibc | **2.31** | **2.43** | backward-compatible; every stock binary still runs (proven on hardware) | -| gcc (toolchain) | 10.x era | **14.4.0** | | +| glibc | **2.31** | **2.44** | backward-compatible; every stock binary still runs (proven on hardware) | +| gcc (toolchain) | 10.x era | **15.3.0** | | The kernel jump is the sharpest: stock forked 5.15.1 and **never took any of the subsequent 5.15.y stable releases**, and 5.15 itself reaches EOL in **Oct 2026** (per kernel.org). @@ -39,10 +39,10 @@ Versions are from the *shipped* artifacts: stock from the extracted stock | Package | Stock | Ours | Why it matters | |---|---|---|---| -| **OpenSSL** | **1.1.1** (`libssl.so.1.1`) | **3.6.3** (`libssl.so.3`) | **OpenSSL 1.1.1 reached end-of-life on 2023-09-11** — stock ships a TLS library that has received **no** upstream fixes for ~2 years. This is the single strongest security argument. Major SONAME bump `1.1 → 3`. | +| **OpenSSL** | **1.1.1** (`libssl.so.1.1`) | **3.6.4** (`libssl.so.3`) | **OpenSSL 1.1.1 reached end-of-life on 2023-09-11** — stock ships a TLS library that has received **no** upstream fixes for ~2 years. This is the single strongest security argument. Major SONAME bump `1.1 → 3`. | | **OpenSSH** | **8.6p1** | **10.5p1** | the network login surface; ~4 years and several release cycles of hardening | -| **Samba** | ~4.14 | **4.24.5** | SMB file sharing, network-facing; ~9 major-minor releases | -| **BlueZ** | 5.x (`libbluetooth.so.3`) | **5.79** | Bluetooth stack | +| **Samba** | ~4.14 | **4.24.6** | SMB file sharing, network-facing; ~9 major-minor releases | +| **BlueZ** | 5.x (`libbluetooth.so.3`) | **5.86** | Bluetooth stack — 5.86 carries upstream's `CablePairing` series (DS3 over BT), which this repo backported by hand while Buildroot pinned 5.79 ([`bluetooth-parity` §10](bluetooth-parity.md)) | | **wpa_supplicant** | 2.9 | **2.12** | Wi-Fi auth | | **Python** | **3.9** | **3.14.7** | on-device interpreter (A6) — 3.9 is itself near EOL; runs the Downloader and community scripts (compatibility tested in P3.9) | | **dbus** | — | **1.14.10** | | diff --git a/renovate.json b/renovate.json index bcd25b91..a5009806 100644 --- a/renovate.json +++ b/renovate.json @@ -431,14 +431,14 @@ ], "packageRules": [ { - "description": "Constrain the Buildroot pin to the 2026.05.x line -- do not let Renovate jump to a new Buildroot major/minor; P1.1's whole toolchain/config pin assumes 2026.05. (2026.05 is NOT an LTS; 2025.02 was the last LTS and 2027.02 is expected to be the next -- see docs. This is the interim non-LTS line until 2027.02 ships.)", + "description": "Constrain the Buildroot pin to the 2026.08.x line -- do not let Renovate jump to a new Buildroot major/minor; P1.1's whole toolchain/config pin assumes the pinned line. (2026.08 is NOT an LTS; 2025.02 was the last LTS and 2027.02 is expected to be the next -- see docs. This is the interim non-LTS line until 2027.02 ships.) THIS REGEX IS WHY A LINE BUMP NEVER ARRIVES AS A RENOVATE PR, and that is deliberate, not a bug in the manager: `loose` versioning orders 2026.08 above 2026.05.2 correctly ([2026,8] vs [2026,5,2]), the github-tags datasource sees the tag, and allowedVersions then filters it out -- so a line bump stays a human decision. Moved 2026.05.x -> 2026.08.x by hand on 2026-09-05, the same way PR #54 moved 2026.02.x -> 2026.05.x. Bump this WITH the Makefile pin, never separately: leaving it behind silently freezes the pin, and moving it ahead invites a line jump nobody reviewed.", "matchManagers": [ "custom.regex" ], "matchDepNames": [ "buildroot/buildroot" ], - "allowedVersions": "/^2026\\.05(\\.\\d+)?$/", + "allowedVersions": "/^2026\\.08(\\.\\d+)?$/", "addLabels": [ "buildroot-pin" ] diff --git a/scripts/ci-tests.sh b/scripts/ci-tests.sh index 28f59fe3..58bb41f5 100755 --- a/scripts/ci-tests.sh +++ b/scripts/ci-tests.sh @@ -1362,50 +1362,52 @@ if tar_has "etc/bluetooth/input.conf"; then pass "bluetooth input.conf: UserspaceHID=false (kernel HIDP)" else fail "bluetooth input.conf: UserspaceHID=false (kernel HIDP)" \ - "not set; BlueZ 5.79 defaults to uhid, which is the opposite of stock" + "not set; BlueZ defaults to uhid, which is the opposite of stock" fi - # The security-relevant one. DS3/SIXAXIS support comes from the - # CablePairing backport in board/mister/de10nano/patches/bluez5_utils/, - # NOT from disabling this -- flipping it false would drop the encryption - # requirement for every BR/EDR HID device (CVE-2023-45866). + # The security-relevant one. DS3/SIXAXIS support comes from bluez's own + # CablePairing series (upstream in 5.83, and in the 5.86 Buildroot 2026.08 + # pins), NOT from disabling this -- flipping it false would drop the + # encryption requirement for every BR/EDR HID device (CVE-2023-45866). if grep -qE '^ClassicBondedOnly[[:space:]]*=[[:space:]]*true[[:space:]]*$' "$input_conf"; then pass "bluetooth input.conf: ClassicBondedOnly=true (CVE-2023-45866 mitigation intact)" else fail "bluetooth input.conf: ClassicBondedOnly=true (CVE-2023-45866 mitigation intact)" \ - "not set to true -- if this was flipped to fix DS3 pairing, the supported route is the CablePairing series in board/mister/de10nano/patches/bluez5_utils/, which fixes it WITHOUT weakening every other BR/EDR HID device" + "not set to true -- if this was flipped to fix DS3 pairing, the supported route is bluez's own CablePairing series (already in the pinned bluez), which fixes it WITHOUT weakening every other BR/EDR HID device" fi else fail "bluetooth input.conf present" "etc/bluetooth/input.conf not in rootfs.tar" fi -# The CablePairing patch series actually reached the source tree. Asserted -# against the PATCHED BUILD TREE rather than the rootfs, because none of it is +# The CablePairing series is actually in the bluez source tree we build. +# Asserted against the BUILD TREE rather than the rootfs, because none of it is # visible in a shipped file: bluetoothd's behaviour changes, its name and size -# do not. Without this, the series silently ceasing to apply (a Buildroot bump, -# a bad rebase, a deleted directory) would produce a perfectly green build in -# which a DS3 simply cannot connect over Bluetooth -- the exact regression this -# whole change exists to fix. +# do not. Without this, the series going missing would produce a perfectly +# green build in which a DS3 simply cannot connect over Bluetooth. +# +# WHAT THIS GUARDS CHANGED WITH THE 2026.08 BUMP, but the check did not. The +# series used to arrive as backports under +# board/mister/de10nano/patches/bluez5_utils/, because Buildroot pinned bluez +# 5.79 and the series landed upstream in 5.83; the gate then meant "our patches +# applied". Buildroot 2026.08 pins 5.86, which contains all of it, so that +# directory was deleted and the gate now means "the pinned bluez still carries +# it" -- a downgrade, a vendor fork, or a bluez that ever drops the series +# fails here instead of shipping silently. Deliberately kept: the assertion is +# on BEHAVIOUR reaching the tree, so it never had to care which mechanism put +# it there. # # CHOOSE THE MARKER CAREFULLY -- the obvious one is wrong. An earlier revision -# of this check grepped for BT_IO_SEC_LOW, which is VACUOUS: that string is +# of this check grepped for BT_IO_SEC_LOW, which is VACUOUS: that string was # already present in pristine 5.79 (profiles/input/server.c:274), and is in -# fact the very line patch 0004 replaces. The check passed identically whether -# the series had applied or not -- inverted, if anything, since it matches most -# reliably on an UNPATCHED tree. +# fact the very line the series replaces. The check passed identically whether +# the series was present or not -- inverted, if anything, since it matched most +# reliably on a tree WITHOUT it. # -# server_set_cable_pairing is introduced only by patch 0004 and appears nowhere -# in pristine 5.79 (verified against the unpatched tarball, along with +# server_set_cable_pairing comes only from the series and appeared nowhere in +# pristine 5.79 (verified against the unpatched tarball, along with # get_necessary_sec_level, device_is_cable_pairing and # btd_adapter_has_cable_pairing_devices -- any of the four would do). # -# THE FAILURE THIS GUARDS IS LIVE, not hypothetical: Buildroot never revisits -# .stamp_patched, so adding patches to an ALREADY-BUILT tree is a silent no-op. -# An incremental `make all` over an output/ that predates this branch ships a -# bluetoothd with no CablePairing support and no DS3 -- looking, in every -# shipped file, exactly like a correct build. Use `make bluez5_utils-dirclean` -# after changing anything in board/mister/de10nano/patches/bluez5_utils/. -# # Same glob-into-an-array idiom as the CONFIG_NFSD gate below -- but note a # stale sibling build dir from a version bump is REAL here (Buildroot never # removes the old one), so the newest directory is chosen rather than demanding @@ -1415,17 +1417,17 @@ for _d in "$BUILD_DIR"/build/bluez5_utils-[0-9]*; do [ -d "$_d" ] && bluez_dirs+=("$_d") done if [ "${#bluez_dirs[@]}" -eq 0 ]; then - skip "bluez CablePairing series applied (DS3 over Bluetooth)" \ + skip "bluez CablePairing series present (DS3 over Bluetooth)" \ "no $BUILD_DIR/build/bluez5_utils-[0-9]* directory found" else # Newest by version sort, so a leftover tree from a previous pin does not # decide the verdict. bluez_newest=$(printf '%s\n' "${bluez_dirs[@]}" | sort -V | tail -1) if grep -q 'server_set_cable_pairing' "$bluez_newest/profiles/input/server.c" 2>/dev/null; then - pass "bluez CablePairing series applied in $(basename "$bluez_newest") (DS3 over Bluetooth)" + pass "bluez CablePairing series present in $(basename "$bluez_newest") (DS3 over Bluetooth)" else - fail "bluez CablePairing series applied (DS3 over Bluetooth)" \ - "server_set_cable_pairing absent from $bluez_newest/profiles/input/server.c -- board/mister/de10nano/patches/bluez5_utils/ did not apply (a stale .stamp_patched will do this silently; try 'make bluez5_utils-dirclean'), so a DS3 cannot connect" + fail "bluez CablePairing series present (DS3 over Bluetooth)" \ + "server_set_cable_pairing absent from $bluez_newest/profiles/input/server.c -- the pinned bluez does not carry the CablePairing series (upstream since 5.83), so a DS3 cannot connect. If bluez was downgraded below 5.83 the series must be backported again; see docs/bluetooth-parity.md" fi fi @@ -1511,6 +1513,44 @@ fi require_present "etc/init.d/S50sshd" "S50sshd" +# A user's SSH key on the FAT partition must be honoured, so that key access +# survives an image update. Asserted on the SHIPPED sshd_config, not the +# overlay source, because the overlay only matters if it actually reaches the +# rootfs. +# +# WHY THIS GATE EXISTS: an update replaces linux.img wholesale, so a key under +# /root/.ssh -- which lives inside that file -- does not survive one. (/ is also +# read-only at boot and a fresh image ships no /root/.ssh at all; it becomes +# writable only via /etc/profile's remount on interactive login, so putting a +# key there by hand needs a login first -- circular when the key IS the login +# method.) /media/fat/linux/authorized_keys is the only location a user can +# write from an ordinary PC (card reader, any OS) that the update process does +# not touch. Dropping this line would silently return every user to "your key +# is gone after each update", with nothing else failing. +if tar_has "etc/ssh/sshd_config"; then + sshd_conf="$WORKDIR/sshd_config" + tar xOf "$ROOTFS_TAR" ./etc/ssh/sshd_config > "$sshd_conf" 2>/dev/null + if grep -qE '^AuthorizedKeysFile[[:space:]].*[[:space:]]/media/fat/linux/authorized_keys[[:space:]]*$' "$sshd_conf"; then + pass "sshd_config: AuthorizedKeysFile includes /media/fat/linux/authorized_keys (key survives an image update)" + else + fail "sshd_config: AuthorizedKeysFile includes /media/fat/linux/authorized_keys" \ + "absent -- a user key placed on the FAT partition would be ignored, so SSH key access would be lost on every image update. Actual: $(grep -E '^AuthorizedKeysFile' "$sshd_conf" || echo '')" + fi + + # StrictModes must stay at its default (yes). The FAT path above satisfies it + # only because the initramfs mounts with fmask=0022,dmask=0022 + # (board/mister/de10nano/initramfs-overlay/init); an explicit 'StrictModes no' + # would mean someone worked around a permissions problem instead of fixing it. + if grep -qE '^StrictModes[[:space:]]+no' "$sshd_conf"; then + fail "sshd_config: StrictModes not disabled" \ + "'StrictModes no' is set -- the FAT authorized_keys path is designed to satisfy StrictModes, so disabling it hides a real permissions fault" + else + pass "sshd_config: StrictModes left at default (yes)" + fi +else + fail "sshd_config present" "etc/ssh/sshd_config not in rootfs.tar" +fi + # ============================================================================= section "P3.8 — MIDI / MT-32 parity" # =============================================================================