From aee4066efe6090038ee315deb8dbd19abf5b758a Mon Sep 17 00:00:00 2001 From: Pawel Winogrodzki Date: Fri, 28 Aug 2026 17:05:37 -0700 Subject: [PATCH 1/4] fix(systemd): lock disks while running filesystem checks Acquire the exclusive whole-disk lock in systemd-fsck before launching its child fsck process, and retain it until that process exits. This is a systemd-owned alternative to native e2fsprogs locking. The two implementations must not be combined because nested locks would deadlock. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: bb0e6716-4886-4e95-9efe-71dcde8687d6 --- .../systemd-boot-signed.spec | 5 +- .../systemd-fsck-lock-whole-disk.patch | 77 +++++++++++++++++++ SPECS/systemd/systemd.spec | 13 +++- 3 files changed, 93 insertions(+), 2 deletions(-) create mode 100644 SPECS/systemd/systemd-fsck-lock-whole-disk.patch diff --git a/SPECS-SIGNED/systemd-boot-signed/systemd-boot-signed.spec b/SPECS-SIGNED/systemd-boot-signed/systemd-boot-signed.spec index 73238100e8d..381e6c76687 100644 --- a/SPECS-SIGNED/systemd-boot-signed/systemd-boot-signed.spec +++ b/SPECS-SIGNED/systemd-boot-signed/systemd-boot-signed.spec @@ -20,7 +20,7 @@ Version: 255 # determine the build information from local checkout Version: %(tools/meson-vcs-tag.sh . error | sed -r 's/-([0-9])/.^\1/; s/-g/_g/') %endif -Release: 34%{?dist} +Release: 35%{?dist} License: LGPL-2.1-or-later AND MIT AND GPL-2.0-or-later Vendor: Microsoft Corporation Distribution: Azure Linux @@ -98,6 +98,9 @@ popd /boot/efi/EFI/BOOT/%{grubefiname} %changelog +* Fri Aug 28 2026 Pawel Winogrodzki - 255-35 +- Bump release to match systemd spec. + * Mon Aug 17 2026 Aditya Singh - 255-34 - Bump release to match systemd spec. diff --git a/SPECS/systemd/systemd-fsck-lock-whole-disk.patch b/SPECS/systemd/systemd-fsck-lock-whole-disk.patch new file mode 100644 index 00000000000..3945463148e --- /dev/null +++ b/SPECS/systemd/systemd-fsck-lock-whole-disk.patch @@ -0,0 +1,77 @@ +diff --git a/src/fsck/fsck.c b/src/fsck/fsck.c +index 000ed69..8eb209a 100644 +--- a/src/fsck/fsck.c ++++ b/src/fsck/fsck.c +@@ -15,6 +15,7 @@ + #include "sd-device.h" + + #include "alloc-util.h" ++#include "blockdev-util.h" + #include "bus-common-errors.h" + #include "bus-error.h" + #include "bus-locator.h" +@@ -23,6 +24,7 @@ + #include "fd-util.h" + #include "fs-util.h" + #include "fsck-util.h" ++#include "lock-util.h" + #include "main-func.h" + #include "parse-util.h" + #include "path-util.h" +@@ -233,8 +235,45 @@ static int fsck_progress_socket(void) { + return TAKE_FD(fd); + } + ++static int lock_whole_disk(const char *device) { ++ _cleanup_free_ char *whole_disk = NULL; ++ _cleanup_close_ int fd = -EBADF; ++ struct stat st; ++ dev_t devno; ++ int r; ++ ++ assert(device); ++ ++ r = path_get_whole_disk(device, /* backing = */ false, &devno); ++ if (r < 0) ++ return log_error_errno(r, "Failed to find whole block device for '%s': %m", device); ++ ++ r = devname_from_devnum(S_IFBLK, devno, &whole_disk); ++ if (r < 0) ++ return log_error_errno(r, "Failed to resolve whole block device for '%s': %m", device); ++ ++ fd = open(whole_disk, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY); ++ if (fd < 0) ++ return log_error_errno(errno, "Failed to open whole block device '%s': %m", whole_disk); ++ ++ if (fstat(fd, &st) < 0) ++ return log_error_errno(errno, "Failed to stat whole block device '%s': %m", whole_disk); ++ if (!S_ISBLK(st.st_mode) || st.st_rdev != devno) ++ return log_error_errno(SYNTHETIC_ERRNO(ENXIO), ++ "Path '%s' no longer refers to block device %u:%u.", ++ whole_disk, major(devno), minor(devno)); ++ ++ r = lock_generic(fd, LOCK_BSD, LOCK_EX); ++ if (r < 0) ++ return log_error_errno(r, "Failed to lock whole block device '%s': %m", whole_disk); ++ ++ log_debug("Locked whole block device %s while checking %s.", whole_disk, device); ++ return TAKE_FD(fd); ++} ++ + static int run(int argc, char *argv[]) { + _cleanup_close_pair_ int progress_pipe[2] = EBADF_PAIR; ++ _cleanup_close_ int lock_fd = -EBADF; + _cleanup_(sd_device_unrefp) sd_device *dev = NULL; + _cleanup_free_ char *dpath = NULL; + _cleanup_fclose_ FILE *console = NULL; +@@ -333,6 +372,10 @@ static int run(int argc, char *argv[]) { + } + } + ++ lock_fd = lock_whole_disk(device); ++ if (lock_fd < 0) ++ return lock_fd; ++ + console = fopen("/dev/console", "we"); + if (console && + arg_show_progress && diff --git a/SPECS/systemd/systemd.spec b/SPECS/systemd/systemd.spec index bc46e768f95..059ccc0f2ef 100644 --- a/SPECS/systemd/systemd.spec +++ b/SPECS/systemd/systemd.spec @@ -50,7 +50,7 @@ Version: 255 # determine the build information from local checkout Version: %(tools/meson-vcs-tag.sh . error | sed -r 's/-([0-9])/.^\1/; s/-g/_g/') %endif -Release: 34%{?dist} +Release: 35%{?dist} # FIXME - hardcode to 'stable' for now as that's what we have in our blobstore %global stable 1 @@ -162,6 +162,13 @@ Patch0914: Prevent-corruption-from-stale-alias-state-on-daemon-reload.patch Patch0915: CVE-2026-15059.patch Patch0916: CVE-2026-16742.patch +# Alternative to the proposed native e2fsprogs whole-disk lock: +# https://lore.kernel.org/linux-ext4/20260824161512.1332649-1-naraghavan@linux.microsoft.com/ +# Do not ship both implementations. systemd-fsck retains its exclusive +# lock while waiting for fsck; native e2fsck locking would then block on +# a second independently-opened lock and deadlock boot. +Patch0917: systemd-fsck-lock-whole-disk.patch + %ifarch %{ix86} x86_64 aarch64 %global want_bootloader 1 %endif @@ -1259,6 +1266,10 @@ rm -f %{name}.lang # %autochangelog. So we need to continue manually maintaining the # changelog here. %changelog +* Fri Aug 28 2026 Pawel Winogrodzki - 255-35 +- Lock the whole disk in systemd-fsck while its child fsck process checks + the filesystem. + * Thu Aug 13 2026 Azure Linux Security Servicing Account - 255-34 - Patch for CVE-2026-16742, CVE-2026-15059 From cd33a9aa8d1fcb002db9bae1b29e791d60021303 Mon Sep 17 00:00:00 2001 From: Pawel Winogrodzki Date: Wed, 2 Sep 2026 11:25:59 -0700 Subject: [PATCH 2/4] docs(systemd): move lock warning into patch Keep the package spec focused on patch registration and document the mutual-exclusion requirement at the lock acquisition site. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: bb0e6716-4886-4e95-9efe-71dcde8687d6 --- SPECS/systemd/systemd-fsck-lock-whole-disk.patch | 5 ++++- SPECS/systemd/systemd.spec | 6 ------ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/SPECS/systemd/systemd-fsck-lock-whole-disk.patch b/SPECS/systemd/systemd-fsck-lock-whole-disk.patch index 3945463148e..088e21a82aa 100644 --- a/SPECS/systemd/systemd-fsck-lock-whole-disk.patch +++ b/SPECS/systemd/systemd-fsck-lock-whole-disk.patch @@ -64,10 +64,13 @@ index 000ed69..8eb209a 100644 _cleanup_(sd_device_unrefp) sd_device *dev = NULL; _cleanup_free_ char *dpath = NULL; _cleanup_fclose_ FILE *console = NULL; -@@ -333,6 +372,10 @@ static int run(int argc, char *argv[]) { +@@ -333,6 +372,13 @@ static int run(int argc, char *argv[]) { } } ++ /* Do not combine this implementation with native e2fsprogs (e2fsck) whole-disk locking. ++ * systemd-fsck retains this descriptor while waiting for fsck, so an independently opened exclusive ++ * lock in the child would deadlock. */ + lock_fd = lock_whole_disk(device); + if (lock_fd < 0) + return lock_fd; diff --git a/SPECS/systemd/systemd.spec b/SPECS/systemd/systemd.spec index 059ccc0f2ef..0e8504ca881 100644 --- a/SPECS/systemd/systemd.spec +++ b/SPECS/systemd/systemd.spec @@ -161,12 +161,6 @@ Patch0913: network-also-check-ID_NET_MANAGED_BY-property-on-rec.patch Patch0914: Prevent-corruption-from-stale-alias-state-on-daemon-reload.patch Patch0915: CVE-2026-15059.patch Patch0916: CVE-2026-16742.patch - -# Alternative to the proposed native e2fsprogs whole-disk lock: -# https://lore.kernel.org/linux-ext4/20260824161512.1332649-1-naraghavan@linux.microsoft.com/ -# Do not ship both implementations. systemd-fsck retains its exclusive -# lock while waiting for fsck; native e2fsck locking would then block on -# a second independently-opened lock and deadlock boot. Patch0917: systemd-fsck-lock-whole-disk.patch %ifarch %{ix86} x86_64 aarch64 From c286104bd8557354e1488ca592ea54e00bb361e7 Mon Sep 17 00:00:00 2001 From: Pawel Winogrodzki Date: Wed, 2 Sep 2026 16:37:34 -0700 Subject: [PATCH 3/4] docs(systemd): move lock warning to patch header Keep downstream compatibility guidance in the patch description. Do not add it to the patched source. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: bb0e6716-4886-4e95-9efe-71dcde8687d6 --- SPECS/systemd/systemd-fsck-lock-whole-disk.patch | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/SPECS/systemd/systemd-fsck-lock-whole-disk.patch b/SPECS/systemd/systemd-fsck-lock-whole-disk.patch index 088e21a82aa..411d8ac3810 100644 --- a/SPECS/systemd/systemd-fsck-lock-whole-disk.patch +++ b/SPECS/systemd/systemd-fsck-lock-whole-disk.patch @@ -1,3 +1,11 @@ +Lock the whole parent disk while fsck runs so udev cannot probe sibling +partitions during filesystem repair. + +Do not combine this implementation with native e2fsprogs (e2fsck) +whole-disk locking. systemd-fsck retains its lock descriptor while waiting +for fsck, so an independently opened exclusive lock in the child would +deadlock. + diff --git a/src/fsck/fsck.c b/src/fsck/fsck.c index 000ed69..8eb209a 100644 --- a/src/fsck/fsck.c @@ -64,13 +72,10 @@ index 000ed69..8eb209a 100644 _cleanup_(sd_device_unrefp) sd_device *dev = NULL; _cleanup_free_ char *dpath = NULL; _cleanup_fclose_ FILE *console = NULL; -@@ -333,6 +372,13 @@ static int run(int argc, char *argv[]) { +@@ -333,6 +372,10 @@ static int run(int argc, char *argv[]) { } } -+ /* Do not combine this implementation with native e2fsprogs (e2fsck) whole-disk locking. -+ * systemd-fsck retains this descriptor while waiting for fsck, so an independently opened exclusive -+ * lock in the child would deadlock. */ + lock_fd = lock_whole_disk(device); + if (lock_fd < 0) + return lock_fd; From 836fa3cab7d3d44f36026c3734d9e6b71ae4ac5d Mon Sep 17 00:00:00 2001 From: Pawel Winogrodzki Date: Thu, 3 Sep 2026 16:32:49 -0700 Subject: [PATCH 4/4] fix(systemd): bound whole-disk lock wait Wait up to 60 seconds for the exclusive whole-disk lock. If the wait times out, emit a warning and run the filesystem checker without the lock to preserve previous boot behavior. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: bb0e6716-4886-4e95-9efe-71dcde8687d6 --- .../systemd-fsck-lock-whole-disk.patch | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/SPECS/systemd/systemd-fsck-lock-whole-disk.patch b/SPECS/systemd/systemd-fsck-lock-whole-disk.patch index 411d8ac3810..c1eb567519c 100644 --- a/SPECS/systemd/systemd-fsck-lock-whole-disk.patch +++ b/SPECS/systemd/systemd-fsck-lock-whole-disk.patch @@ -1,6 +1,9 @@ Lock the whole parent disk while fsck runs so udev cannot probe sibling partitions during filesystem repair. +Wait up to 60 seconds for the lock. If the timeout expires, warn and run +the filesystem check without the lock to preserve the previous behavior. + Do not combine this implementation with native e2fsprogs (e2fsck) whole-disk locking. systemd-fsck retains its lock descriptor while waiting for fsck, so an independently opened exclusive lock in the child would @@ -26,7 +29,15 @@ index 000ed69..8eb209a 100644 #include "main-func.h" #include "parse-util.h" #include "path-util.h" -@@ -233,8 +235,45 @@ static int fsck_progress_socket(void) { +@@ -32,6 +34,7 @@ + #include "socket-util.h" + #include "special.h" + #include "stdio-util.h" ++#include "time-util.h" + + static bool arg_skip = false; + static bool arg_force = false; +@@ -233,8 +236,47 @@ static int fsck_progress_socket(void) { return TAKE_FD(fd); } @@ -58,7 +69,9 @@ index 000ed69..8eb209a 100644 + "Path '%s' no longer refers to block device %u:%u.", + whole_disk, major(devno), minor(devno)); + -+ r = lock_generic(fd, LOCK_BSD, LOCK_EX); ++ r = lock_generic_with_timeout(fd, LOCK_BSD, LOCK_EX, 60 * USEC_PER_SEC); ++ if (r == -ETIMEDOUT) ++ return r; + if (r < 0) + return log_error_errno(r, "Failed to lock whole block device '%s': %m", whole_disk); + @@ -72,12 +85,17 @@ index 000ed69..8eb209a 100644 _cleanup_(sd_device_unrefp) sd_device *dev = NULL; _cleanup_free_ char *dpath = NULL; _cleanup_fclose_ FILE *console = NULL; -@@ -333,6 +372,10 @@ static int run(int argc, char *argv[]) { +@@ -333,6 +375,15 @@ static int run(int argc, char *argv[]) { } } + lock_fd = lock_whole_disk(device); -+ if (lock_fd < 0) ++ if (lock_fd == -ETIMEDOUT) { ++ log_warning( ++ "Timed out waiting 60 seconds for whole block device lock for '%s'; proceeding without lock.", ++ device); ++ lock_fd = -EBADF; ++ } else if (lock_fd < 0) + return lock_fd; + console = fopen("/dev/console", "we");