core: stage /run/host/os-release with a symlink to avoid possible race condition

If someone reads /run/host/os-release at the exact same time it is being updated, and it
is large enough, they might read a half-written file. This is very unlikely as
os-release is typically small and very rarely changes, but it is not
impossible.

Bind mount a staging directory instead of the file, and symlink the file
into into, so that we can do atomic file updates and close this gap.
Atomic replacement creates a new inode, so existing bind mounts would
continue to see the old file, and only new services would see the new file.
The indirection via the directory allows to work around this, as the
directory is fixed and never changes so the bind mount is always valid,
and its content is shared with all existing services.

Fixes https://github.com/systemd/systemd/issues/28794

Follow-up for 3f37a82545
This commit is contained in:
Luca Boccassi
2023-08-16 16:17:41 +01:00
committed by Luca Boccassi
parent 409986ed3e
commit 663e27564f
5 changed files with 49 additions and 31 deletions
+2 -2
View File
@@ -50,7 +50,7 @@ elif [ -f /run/testsuite82.touch2 ]; then
# Test that we really are in the new overlayfs root fs
read -r x </lower
test "$x" = "miep"
cmp /etc/os-release /run/systemd/propagate/os-release
cmp /etc/os-release /run/systemd/propagate/.os-release-stage/os-release
grep -q MARKER=1 /etc/os-release
# Switch back to the original root, away from the overlayfs
@@ -96,7 +96,7 @@ elif [ -f /run/testsuite82.touch ]; then
mkdir -p /tmp/nextroot-lower/usr/lib
grep ID /etc/os-release >/tmp/nextroot-lower/usr/lib/os-release
echo MARKER=1 >>/tmp/nextroot-lower/usr/lib/os-release
cmp /etc/os-release /run/systemd/propagate/os-release
cmp /etc/os-release /run/systemd/propagate/.os-release-stage/os-release
(! grep -q MARKER=1 /etc/os-release)
mount -t overlay nextroot /run/nextroot -o lowerdir=/tmp/nextroot-lower:/,ro