]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
systemctl: also attempt kexec image extraction on EINVAL
authorRocker Zhang <zhang.rocker.liyuan@gmail.com>
Thu, 21 May 2026 15:47:48 +0000 (23:47 +0800)
committerLuca Boccassi <luca.boccassi@gmail.com>
Thu, 21 May 2026 18:00:58 +0000 (19:00 +0100)
load_kexec_kernel() retries kexec_file_load() with an extracted kernel
(decompressed Image / ZBOOT PE / UKI) when the kernel rejects the image,
but it only does so when kexec_file_load() failed with ENOEXEC. On arm64
that retry never happens: arm64's image_probe()
(arch/arm64/kernel/kexec_image.c) returns -EINVAL on an ARM64_IMAGE_MAGIC
mismatch, whereas x86's bzImage64_probe() and the generic
kexec_image_probe_default() return -ENOEXEC. So `systemctl kexec` of a
UKI on arm64 skips the extraction path and falls back to the
/usr/sbin/kexec binary, which is no longer a dependency since e107c7ead0
("systemctl: replace kexec-tools dependency with direct kexec_file_load()
syscall") -- leaving kexec broken.

Accept EINVAL in addition to ENOEXEC. This is safe: the extraction in
kexec_maybe_decompress_kernel() re-gates on the actual file magic (MZ /
compression headers) and is a no-op returning 0 for anything else, so an
EINVAL that is not a format mismatch just falls through to the existing
fallback as before.

Fixing this in systemd (rather than only in the kernel) is appropriate:
systemd must keep working with already-shipped arm64 kernels whose
kexec_file_load() returns EINVAL for an unrecognized image magic.

Relates to: https://github.com/systemd/systemd/issues/28538

Co-developed-by: Claude Opus 4.7 <noreply@anthropic.com>
src/systemctl/systemctl-start-special.c

index fd38c678ea69a564d0d041e9a404c0ad0fc36382..a8702ad438edfbbffcb1aef2fcb3a5f125987d14 100644 (file)
@@ -113,9 +113,14 @@ static int load_kexec_kernel(void) {
 
         int saved_errno = errno;
 
-        if (saved_errno == ENOEXEC) {
+        if (IN_SET(saved_errno, ENOEXEC, EINVAL)) {
                 /* The kernel didn't recognize the image format. Try decompressing or extracting the
-                 * kernel (e.g. compressed Image, ZBOOT PE, or UKI) and loading again. */
+                 * kernel (e.g. compressed Image, ZBOOT PE, or UKI) and loading again.
+                 *
+                 * Most architectures' kexec_file_load() returns ENOEXEC when no image loader matches
+                 * (the default behavior of kexec_image_probe_default()), but arm64's image_probe()
+                 * returns EINVAL for an unrecognized magic. Accept both, otherwise `systemctl kexec`
+                 * of a UKI never reaches the extraction path on arm64. */
                 log_debug_errno(saved_errno, "Kernel rejected image, trying decompression/extraction: %m");
 
                 _cleanup_close_ int extracted_kernel_fd = -EBADF, extracted_initrd_fd = -EBADF;