From 54431fc856d21799af3a6bff91e4c062aabf8e74 Mon Sep 17 00:00:00 2001 From: archipelago Date: Mon, 31 Aug 2026 09:16:18 -0400 Subject: [PATCH] fix(host): enforce the full kdump crash reservation --- core/archipelago/src/host_fixups.rs | 29 ++++++++++++++----- .../_archived/build-auto-installer-iso.sh | 7 +++++ tests/lifecycle/os-audit.sh | 21 +++++++++----- 3 files changed, 43 insertions(+), 14 deletions(-) diff --git a/core/archipelago/src/host_fixups.rs b/core/archipelago/src/host_fixups.rs index d781a7c7..086be456 100644 --- a/core/archipelago/src/host_fixups.rs +++ b/core/archipelago/src/host_fixups.rs @@ -237,24 +237,39 @@ exit 2 } } -/// Append `crashkernel=` to the installed GRUB cmdline and run update-grub. +/// Set the installed GRUB cmdline to one fixed `crashkernel=` reservation and +/// run update-grub. Debian's kdump-tools package installs a grub.d snippet that +/// otherwise appends its own range-based reservation after ours; on amd64 that +/// silently wins and reserves only 192M instead of the intended 256M. /// The reservation itself only exists after the next reboot — memory cannot /// be set aside at runtime — so the caller must log the reboot caveat. -/// Returns true if the cmdline changed. +/// Returns true if the generated cmdline changed. async fn ensure_crashkernel_cmdline() -> Result { let script = format!( r#" set -u GRUB=/etc/default/grub +KDUMP_GRUB=/etc/default/grub.d/kdump-tools.cfg PARAM='{CRASHKERNEL_PARAM}' [ -f "$GRUB" ] || exit 3 +CHANGED=0 +# kdump-tools sources this after /etc/default/grub and unconditionally appends +# crashkernel=512M-:192M. Neutralize that package default: Archipelago owns the +# explicit fixed reservation in GRUB_CMDLINE_LINUX_DEFAULT below. +if [ -f "$KDUMP_GRUB" ] && grep -qE '^[^#]*crashkernel=' "$KDUMP_GRUB"; then + printf '%s\n' '# Archipelago owns crashkernel sizing in /etc/default/grub.' > "$KDUMP_GRUB" + CHANGED=1 +fi LINE=$(grep -E '^GRUB_CMDLINE_LINUX_DEFAULT=' "$GRUB" | head -1) [ -n "$LINE" ] || exit 3 -case "$LINE" in - *"$PARAM"*) exit 0 ;; -esac -NEWLINE=$(printf '%s' "$LINE" | sed "s/\"$/ $PARAM\"/") -sed -i "s|^GRUB_CMDLINE_LINUX_DEFAULT=.*|$NEWLINE|" "$GRUB" +# Remove any prior value before appending ours, so repeated fixups can never +# create conflicting parameters whose kernel precedence is easy to misread. +NEWLINE=$(printf '%s' "$LINE" | sed -E "s/[[:space:]]+crashkernel=[^ \"']+//g; s/\"$/ $PARAM\"/") +if [ "$NEWLINE" != "$LINE" ]; then + sed -i "s|^GRUB_CMDLINE_LINUX_DEFAULT=.*|$NEWLINE|" "$GRUB" + CHANGED=1 +fi +[ "$CHANGED" -eq 1 ] || exit 0 timeout 120 update-grub >/dev/null 2>&1 || true exit 2 "# diff --git a/image-recipe/_archived/build-auto-installer-iso.sh b/image-recipe/_archived/build-auto-installer-iso.sh index 1b358501..83b03941 100755 --- a/image-recipe/_archived/build-auto-installer-iso.sh +++ b/image-recipe/_archived/build-auto-installer-iso.sh @@ -3746,6 +3746,13 @@ if [ -d "$BOOT_MEDIA/archipelago/plymouth-theme" ]; then # Configure clean boot: splash, suppress kernel noise, hide cursor sed -i 's/GRUB_CMDLINE_LINUX_DEFAULT=".*"/GRUB_CMDLINE_LINUX_DEFAULT="quiet splash loglevel=0 rd.systemd.show_status=false vt.global_cursor_default=0 acpi=force crashkernel=256M"/' \ /mnt/target/etc/default/grub 2>/dev/null || true + # kdump-tools ships a grub.d snippet that appends crashkernel=512M-:192M + # after this line. The later value silently wins on amd64, so neutralize + # the package default and keep Archipelago's explicit fixed reservation. + if [ -f /mnt/target/etc/default/grub.d/kdump-tools.cfg ]; then + printf '%s\n' '# Archipelago owns crashkernel sizing in /etc/default/grub.' \ + > /mnt/target/etc/default/grub.d/kdump-tools.cfg + fi echo " Installed Archipelago Plymouth theme on target" fi diff --git a/tests/lifecycle/os-audit.sh b/tests/lifecycle/os-audit.sh index 4ca451cf..5dab8fb3 100755 --- a/tests/lifecycle/os-audit.sh +++ b/tests/lifecycle/os-audit.sh @@ -242,14 +242,21 @@ section_d() { else record FAIL "kdump-tools configured" "/etc/default/kdump-tools missing USE_KDUMP=1 — host fixup didn't land" fi - # D2. crashkernel reservation — memory is reserved at BOOT, so a node that - # took the OTA fixup but hasn't rebooted yet is WARN, not FAIL. - if grep -q 'crashkernel=' /proc/cmdline 2>/dev/null; then - record PASS "crashkernel reserved" "$(grep -oE 'crashkernel=[^ ]+' /proc/cmdline | head -1)" - elif grep -q 'crashkernel=' /etc/default/grub 2>/dev/null; then - record WARN "crashkernel reserved" "written to GRUB — applies on next reboot" + # D2. crashkernel reservation — grade the memory the kernel actually + # reserved, not merely the first matching cmdline token. Debian's + # kdump-tools.cfg used to append a second crashkernel= range after our 256M; + # the audit falsely passed while /sys reported only 192M reserved. + local crash_size expected_size=$((256 * 1024 * 1024)) + crash_size=$(cat /sys/kernel/kexec_crash_size 2>/dev/null || echo 0) + [[ "$crash_size" =~ ^[0-9]+$ ]] || crash_size=0 + if (( crash_size >= expected_size )); then + record PASS "crashkernel reserved" "$((crash_size / 1024 / 1024))MiB actually reserved" + elif (( crash_size > 0 )); then + record FAIL "crashkernel reserved" "$((crash_size / 1024 / 1024))MiB reserved; expected >=256MiB (conflicting cmdline?)" + elif grep -q 'crashkernel=256M' /etc/default/grub 2>/dev/null; then + record WARN "crashkernel reserved" "256M written to GRUB — applies on next reboot" else - record FAIL "crashkernel reserved" "absent from /proc/cmdline AND /etc/default/grub" + record FAIL "crashkernel reserved" "no reservation and crashkernel=256M absent from GRUB" fi # D3. hang/panic capture policy — runtime-settable, expected immediately if [[ "$(cat /proc/sys/kernel/hung_task_panic 2>/dev/null)" == "1" ]]; then