Skip to content

Stage 06 and the acceptance refuse an executable file build/config/image-programs.txt does not name - #403

Merged
DevomB merged 357 commits into
image-excludes-listfrom
image-programs-named
Oct 13, 2026
Merged

DevomB merged 357 commits into
image-excludes-listfrom
image-programs-named

Conversation

@DevomB

@DevomB DevomB commented Oct 12, 2026 •

Copy link
Copy Markdown
Owner

What was wrong. A file a recipe stops installing stays in the cached sysroot, and stage 06 copies the sysroot into the image. traceroute shipped that way after its recipe dropped it (2026-09-28). Nothing compared what the image carries with what the recipes mean it to carry.

What changed.

  • tools/check-image-programs.sh ROOT fails on two things:
    • an executable file (a regular file with an execute bit) the root carries that build/config/image-programs.txt does not name;
    • an entry in the list that names nothing.
  • The list names every executable file of the image, one path or glob per line. Globs stand only for version numbers (GCC's and perl's directories, a shared library's version past its soname, versioned program names), glibc's gconv modules and the two doc trees' examples.
  • The check runs in two places:
    • stage 06 runs it on the staged root, after the hardening audit, and the list and the tool are in s_rootfs's fingerprint;
    • the acceptance runs it again on the image (image-programs).
  • --write FILE writes the names a root holds, as a list to start from.
  • docs/status.md's stale-files gap says what is now caught and what is not.

Scope and order:

How the run proves it.

  • The tests run fixture roots through the check:
    • a named tree passes;
    • a program left behind, an entry naming nothing, a pattern for another soname and an absolute entry each fail;
    • --write lists what a root holds.
  • The combined Distro run of those changes passes stage 06 and the acceptance's image-programs item with this list.

DevomB added 30 commits October 11, 2026 22:34
…e net zone, and its access point has an address of its own

Distro 38203941757 failed wifi-open on two counts. The lease was read off
wlan0, a name the radio loses in the rounds before it, where the net zone
calls it nic0, so no lease could be found. And the open access point took
the WPA3 one's BSSID: the station held both networks' entries under one
BSSID for the 30 s the kernel keeps an entry, the open access point logged
one SAE attempt from it, and the station never associated. Another network
is another access point, so it gets an address of its own.

A failure now says what the net zone's supplicant sees (its status, its
networks, its scan results and the BSSIDs it ignores, asked through
kryptikd supplicant, since the socket is its user's) and what the access
point is.
…s name in the net zone and has an access point of its own
perl's Configure looks up the machine it runs on: myhostname, mydomain,
perladmin and cf_email from its name, myuname from uname -a, osvers
from the running kernel, cf_time from the clock. All of it lands in
Config.pm and Config_heavy.pl, so the image named the CI runner and its
Azure kernel, and two builds of one tree differed there. Configure
ignores SOURCE_DATE_EPOCH, and sets cf_time after it applies -D
options, so the recipe writes a config.over, which Configure loads
once it has looked everything up. The recipe fails if the installed
perl does not report the fixed host name.
Each net zone start set a radio back to its hardware address, so every
scan before the first join went out from the radio's own address, and a
scan after leaving a network went out from that network's address, which
any radio in range hears: a scan could follow the machine where the
networks it joins cannot.

kryptikd gives a radio's station a new random, locally administered
address each time it carries the radio into the net zone, in place of the
hardware's (a wired NIC still gets its own back). As its supplicant starts,
the net zone turns on wpa_supplicant's MAC_RAND_SCAN, so a scan while no
network is joined goes out from a new random address each time without
the interface's address changing; it asks through kryptikd supplicant, as
the control socket is that user's, and says whether the driver gave random
scan addresses. The kernel refuses them to a station that has joined, so
a scan then goes out from that network's own address.

wifi-scan-address hears every probe request on hwsim's monitor interface
from the first network's save to the open network's join, and fails on a
broadcast one (a scan's) from the radio's own address or a saved
network's. On the old code the first scans came from the radio's own.
…for no passphrase

kryptik wifi add --open could save a network with no passphrase, but the
menu could not: w offered only a network with a passphrase. Now w, then o
asks for the network's name alone, says first that anyone in range reads
what crosses an open network, and saves it through kryptik-launch
--wifi-add-open, as the command line does.

chrome-wifi-add-open (desktop suite) types w, o and a name into the menu's
own window and finds the network in the net zone's file with key_mgmt=NONE
and no psk line. On the old menu, o is answered with "?" and nothing is
saved.
-D_GLIBCXX_ASSERTIONS makes libstdc++ check its preconditions: an index
or iterator past a container's end, a front() of an empty vector, a
null unique_ptr dereferenced. The program aborts instead of reading or
writing past the object, as with Fedora's and Arch's default flags and
the OpenSSF compiler hardening guide. The image's C++ is groff, ninja,
GCC's own programs, libgmpxx and ncurses++; groff and ninja read files
a user may have downloaded.

The flag goes in CXXFLAGS only, through KRYPTIK_CXXFLAGS_HARDENING,
which stage 04's per-package flags use too. hardening-flags.sh builds
a C++ program with the flag set and expects an index past a vector's
end to abort.
…s, and for scans once joined

A driver that refuses to change the interface's address keeps the radio's
own and scans from it unless it offers random scan addresses: net-zone.md
and the guide said the radio's own never goes on the air without that
condition. The threat model's sentence now covers scans before joining
only, as net-zone.md says a scan while joined goes out from that network's
address. netzone-init.sh's comment and its line for a driver without random
scan addresses name the station's address, the last network's after one.
…aits for the last window to close

The help line was 89 characters, which wraps in an 80-column window; it is
77 now. chrome-wifi-add-open counted the menu windows right after closing
the last one, which may still be going: the new window would then only
bring the count back to where it was. It waits up to 5 s for the count to
drop first.
ath11k reads a WCN6855 at SoC revision 2.1 from WCN6855/hw2.1, and
the QCA2066 and QCA6698AQ, which share its PCI id, from their own
hw2.1 directories (ath11k_pci_probe). The list named WCN6855/hw2.0
alone, so every such card, the common revision in laptops, found no
firmware and had no Wi-Fi. linux-firmware's hw2.1 entries for the
WCN6855 are links into hw2.0, so they cost nothing.

ath11k and ath12k read a variant directory (nfa765, ncm865) only when
a devicetree names one (firmware-name, or three Qualcomm Arm boards
in ath11k's use-case table); an x86 machine has none, so [!n]* leaves
those 5 MB out.
_GLIBCXX_ASSERTIONS checks operator[] bounds, front and back of an empty container and null smart pointers; checked iterators are debug mode (_GLIBCXX_DEBUG). GCC's native build copies CXXFLAGS into CXXFLAGS_FOR_TARGET, so libstdc++ itself is built with the flag as well.
r8169 and r8152 name a patch file per chip revision (33 in 6.18, all
in linux-firmware 20260916) and apply it when the interface comes up
(rtl_fw.c). The list named none, so every Realtek NIC and USB adapter
ran without its revision's PHY fixes; the driver logs the missing file
and carries on. The nine other rtl_nic files are older versions and
chips 6.18 does not drive. ADR-012 names Ethernet beside graphics and
Wi-Fi.
tg3 names tg3.bin (the 5701 A0, which fails to open without it), tg3_tso.bin and tg3_tso5.bin (TSO on the 5703 to 5705, disabled without them) and tg357766.bin (MODULE_FIRMWARE, tg3.c:220-231). The list named none.
… net zone hears them

A network could be saved only by a name typed exactly, with nothing on the
machine to say which networks were in range or how they are secured.

kryptikd asks the nic zone with a SIGUSR1 to its pid 1, sent through a pidfd
taken while the registry's start time matches; the signal carries nothing.
netzone-init.sh scans each radio with iw, from a random address while no
network is joined and only listening once one is, so nothing goes out from
the address that network knows, and reads a busy radio's last results. It
writes a line per network (signal, security, SSID as iw escapes it) and
names a radio rfkill holds off, in its own /run/netzone-scan. kryptikd reads
that inside the zone's root with no link followed, as it reads the status
line (now one function on the registry's pid record), and takes it as an
untrusted zone's word: a line of any other form is dropped, an SSID is
unescaped and shown quoted with nothing unprintable, each network once at
its strongest, at most 40, saved ones marked.

kryptikd wifi scan does this as root. kryptikd serve runs it as a job for
the desktop (wifi-scan, one at a time, so a scan's seconds do not hold the
daemon), kryptik-launch --wifi-scan and kryptik wifi scan ask it, and the
menu's Wi-Fi window shows it under s.

Checks: tools/tests/netzone-scan.sh (iw's listing to the zone's answer, and
the scan's fallbacks); wifi.rs tests (an answer holding everything a zone
might write); the cli suite through the launch service; the zones suite
before the net zone holds a radio and joined to the simulated access point
(wifi-scan-no-radio, wifi-scan); the desktop suite through the launcher
(chrome-wifi-scan).
…he other Wi-Fi verbs

So it no longer rewrites the line that lists the update verbs, which an
update PR rewraps.
…de to load

On Zen 1 to 5, while the running microcode is at or below the EntrySign
cutoff (a firmware older than AMD's 2025 microcode-signature fix), 6.18's
loader takes a patch only if amd_shas.c holds its SHA-256 (amd.c,
verify_sha256_digest and need_sha_check). Of the pinned linux-firmware's
containers, every fam17h patch is listed (7 of 7), fam19h lacks five EPYC
ones (18 of 23), and fam1ah has none (0 of 9). Such a machine keeps its
firmware's microcode, and dmesg says "No sha256 digest for patch ID".

The guide's first section says to update the firmware first on AMD and
what the log line means; the README's microcode line points there.
ath10k drives the QCA6174 hw2.1 from its own directory (QCA6174_HW_2_1_FW_DIR); it asks for API 6, which hw2.1 has not, and loads firmware-5. The list named hw3.0 only.
BRCMFMAC_SDIO defaults to y once MMC is built in, which boot.fragment does for eMMC. firmware.list ships the PCIe parts' firmware and none for SDIO, so the SDIO bus code (sdio.c, the BCDC protocol it selects, and 25 firmware names) could only fail a device's probe.
…ot after the wait

The signal lands once a foreground step ends, the time query among them
(8 s at most), so the loop then slept its full 10 s before it scanned: up
to 18 s before a listening scan of every channel, against kryptikd's 25 s.
The pass now goes straight round when a scan was asked for, and kryptikd
waits 40 s. The loop's comment says a scan's signal brings the next tick's
work forward by up to 10 s. The docs and a comment say a driver without
random scan addresses only listens, before joining as after.
firmware.list picks what ships, so nothing noticed when a driver asks
for a file the list leaves out: WCN6855 revision 2.1, the Realtek NIC
patches and tg3's files all went unshipped that way. The new item reads
each module's .modinfo firmware= names (MODULE_FIRMWARE, globs
included), keeps those linux-firmware's WHENCE has, and fails on one
the image lacks unless build/config/firmware-accepted.txt says why,
and on an accepted entry that matches none.

It found one more: e100 refuses an 82551 without d102e_ucode.bin, which
carries the B-step receive fixes, so that file now ships. The D101M
and D101S microcode only adds CPUSaver and is accepted.
… says wifi=off meanwhile

Switching a radio off (rfkill: its switch, an airplane-mode key) takes its
link down: cfg80211_rfkill_set_block runs dev_close on each of its netdevs.
wpa_supplicant brings the link up again only on rfkill's events, which it
reads from /dev/rfkill, and the zone's /dev does not hold it ("rfkill:
Cannot open RFKILL control device" in every start's log). So once the
radio was switched on again its link stayed down, and Wi-Fi stayed off
until the net zone restarted. The status line said wifi=connecting
throughout.

netzone-init.sh's loop now brings a supplicant's radio up when its link is
down and rfkill no longer holds it, which the supplicant takes as its
interface enabled again and scans and joins; and wifi= reads off when
every radio is switched off, which the menu's Network line words as "Wi-Fi
switched off". radio_off is one function for the status and the scan's
answer.

wifi-radio-off (zones suite) soft-blocks the station radio's rfkill
(phy0's, from zone 0): the status says wifi=off and kryptikd wifi scan
names the radio switched off; unblocked, the radio joins the access point
again. On the old code the status stays connecting and the radio never
rejoins. serve's tests word wifi=off.
…ts number

With s the menu listed what was in range, but saving one still meant
typing its name exactly, which a scan had just shown. The scan's answer is
now kept and its networks numbered (heard_list), and a or o take a number
from it in place of a name (heard_name): the name between the daemon's
quotes. A name the daemon quoted with a backslash holds a byte quoted so no
terminal acts on it, so the menu does not pass it on by number and asks
for the name. f still takes a name, as the saved networks are listed by it.

tools/tests/chrome-wifi-pick.sh (under sh, bash and dash): the networks
are numbered and a radio's line is not; a number gives back a plain name,
a UTF-8 one and one with spaces at its ends; a name quoted with an escape,
one with an escaped quote, a number past the list and 0 are refused.
… rfkill

intel-hid, wireless-hotkey and asus-wireless, modules loaded by their ACPI ids; rfkill-input (RFKILL_INPUT, already on) switches the radios, and the net zone follows rfkill.
…scribe's short name

Each part writes what it tested, and the merge refuses parts that tested
something else. The line held `git describe`'s name, whose abbreviation git
sizes by the clone's object count: the parts check out shallow and get 7
characters, while the verdict job's full clone, grown by the repository's
branches, gets 8. Every part of run 38234029691 passed and the verdict still
refused them as "not one run".

The identity line is now the full revision, marked when the tree was dirty,
which was the one other thing the describe carried; the usb and iso hashes
are compared as before. The acceptance-inputs suite adds a part whose
describe is shorter than the merge's (one run), a dirty merge over a clean
part and a clean merge over a dirty part (both refused).
…the guide says what it shows in one place

The integrity suite's recoveries print --status before the repair as well
as after it, as a user reads it first. Step 2 checks that it names the
foreign-signed boot file as neither slot's kernel, with --commit-slot a as
the repair, and that the commit gives a store with no Kryptik entry one for
slot a, in BootOrder. Step 4 checks that it says the zeroed ESP does not
mount and that --restore-slot rewrites it. The guide's recover section says
in one paragraph what --status shows and which repair each finding takes.
…, kernel or filesystem, and the guide claims only the boot-file repair the suite runs
…programs

libmnl is built before iproute2, whose libmnl tools are left out (ip and tc keep it for the kernel's error messages), and Python is built without its dbm modules, which only a cached tree holding gdbm gave it.
A rebuild on a cached tree had built Python's _gdbm (gdbm comes after Python) and iproute2's libmnl tools (libmnl came after iproute2), and the list was made from such a tree. A build from scratch, as a release is, has neither.
@DevomB DevomB closed this Oct 13, 2026
@DevomB
DevomB merged commit 8e0d30f into image-excludes-list Oct 13, 2026
20 checks passed
@DevomB
DevomB deleted the image-programs-named branch October 13, 2026 15:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant