From bbd0dbb9c131c073c2851d76623bf60fe044f52d Mon Sep 17 00:00:00 2001 From: tamnd <1218621+tamnd@users.noreply.github.com> Date: Fri, 9 Oct 2026 19:26:04 +0700 Subject: [PATCH] Boot X32 with an i386 busybox and check the busybox against the row --- .github/workflows/census.yml | 11 ++++++++++- crates/rk/src/boot.rs | 37 ++++++++++++++++++++++++++++++++++++ crates/rk/src/main.rs | 13 ++++++++++++- 3 files changed, 59 insertions(+), 2 deletions(-) diff --git a/.github/workflows/census.yml b/.github/workflows/census.yml index 0e0e7b1..eaf3496 100644 --- a/.github/workflows/census.yml +++ b/.github/workflows/census.yml @@ -130,7 +130,16 @@ jobs: run: | sudo apt-get install -qy --no-install-recommends busybox-static \ "$([ "$ROW" = A64 ] && echo qemu-system-arm || echo qemu-system-x86)" - target/release/rk boot --build "$OUT" --row "$ROW" --timeout 1500 + # An i386 kernel cannot run the runner's own 64 bit busybox, so X32 gets the i386 one. + busybox=() + if [ "$ROW" = X32 ]; then + sudo dpkg --add-architecture i386 + sudo apt-get update -q + (cd "$RUNNER_TEMP" && apt-get download busybox-static:i386 \ + && dpkg-deb -x busybox-static_*_i386.deb busybox-i386) + busybox=(--busybox "$RUNNER_TEMP/busybox-i386/bin/busybox") + fi + target/release/rk boot --build "$OUT" --row "$ROW" --timeout 1500 "${busybox[@]}" env: ROW: ${{ matrix.row }} - name: keep the record diff --git a/crates/rk/src/boot.rs b/crates/rk/src/boot.rs index 10667ee..04906fd 100644 --- a/crates/rk/src/boot.rs +++ b/crates/rk/src/boot.rs @@ -198,6 +198,24 @@ pub fn is_static_elf(bytes: &[u8]) -> bool { }) } +/// Whether an ELF program runs on a kernel of that kbuild `ARCH`, read from its `e_machine` and its +/// class. An i386 kernel cannot start a 64 bit program, so a busybox taken from an amd64 runner +/// panics an X32 boot with "No working init found" before any check runs. +#[must_use] +pub fn runs_on(bytes: &[u8], arch: &str) -> bool { + if bytes.len() < 20 || &bytes[..4] != b"\x7fELF" { + return false; + } + let machine = u16::from_le_bytes([bytes[18], bytes[19]]); + match arch { + "x86_64" => machine == 62 || machine == 3, + "i386" => machine == 3 && bytes[4] == 1, + "arm64" => machine == 183, + "riscv" => machine == 243, + _ => true, + } +} + /// Everything `rk boot` was asked to do. #[derive(Debug, Clone)] pub struct Plan { @@ -514,6 +532,25 @@ mod tests { assert!(!is_static_elf(b"#!/bin/sh")); } + #[test] + fn a_busybox_is_checked_against_the_row_it_boots() { + let mut elf = vec![0u8; 0x40]; + elf[..4].copy_from_slice(b"\x7fELF"); + elf[4] = 2; + elf[18] = 62; + assert!(runs_on(&elf, "x86_64")); + assert!(!runs_on(&elf, "i386")); + assert!(!runs_on(&elf, "arm64")); + elf[4] = 1; + elf[18] = 3; + assert!(runs_on(&elf, "i386")); + assert!(runs_on(&elf, "x86_64")); + elf[4] = 2; + elf[18] = 183; + assert!(runs_on(&elf, "arm64")); + assert!(!runs_on(b"#!/bin/sh", "x86_64")); + } + #[test] fn the_console_is_read_for_markers_checks_and_panics() { let mut o = Outcome::default(); diff --git a/crates/rk/src/main.rs b/crates/rk/src/main.rs index 766f0b3..77f7911 100644 --- a/crates/rk/src/main.rs +++ b/crates/rk/src/main.rs @@ -780,8 +780,19 @@ fn boot_command(repo: &Repo, args: &Args) -> Result { let initramfs = if let Some(path) = args.get("initramfs") { std::path::PathBuf::from(path) } else { + let busybox = read_busybox(args.get("busybox"))?; + if !boot::runs_on(&busybox, &row.arch) { + return Err(format!( + "{} is not a program the {} row's {} kernel can run; pass --busybox with one \ + built for it", + args.get("busybox").unwrap_or("/bin/busybox"), + row.name, + row.arch + )); + } let path = build.join("initramfs.cpio"); - write_initramfs(&path, args.get("busybox"))?; + std::fs::write(&path, boot::initramfs(&busybox)) + .map_err(|e| format!("writing {}: {e}", path.display()))?; path }; let timeout = match args.get("timeout") {