Bug#1150884: udev: initramfs hook copies symlinks with 'cp -r' and modifies chmod /dev/null !
develchizewr at mailbox.org
develchizewr at mailbox.org
Wed Oct 14 11:20:50 BST 2026
Package: udev
Source: systemd
Version: 259.5-0ubuntu3.4
Severity: normal
Dear Maintainer,
since "initramfs-tools: copy network drop-ins too" (#1074564, systemd
256.2-1), debian/extra/initramfs-tools/hooks/udev copies .link files with:
find -L /etc/systemd/network -name '*.link*' -execdir cp -r -pt
"$DESTDIR/usr/lib/systemd/network/" '{}' +
(same for /usr/lib/systemd/network and, in debian/master,
/usr/local/lib/systemd/network).
Because of -r, symlinks (e.g. masks pointing to /dev/null) are copied as
symlinks into $DESTDIR. If a regular file of the same name is copied
later into the same destination, cp writes through the existing symlink
in $DESTDIR and fchmod()s its target. With a mask pointing to /dev/null,
/dev/null becomes mode 0644 until the next reboot (devtmpfs resets it).
Login shells then fail with "Permission denied: /dev/null" for non-root
users.
I hit this on Ubuntu 26.04 after update-initramfs -u. The installed hook
is identical to debian/master except for the /usr/local block (compared
by eye).
Reproduction without any special system state (touches only a temporary
directory; "victim" stands in for /dev/null):
t=$(mktemp -d); mkdir -p $t/usr $t/etc $t/dst; : > $t/victim; chmod
666 $t/victim
ln -s $t/victim $t/usr/99-default.link
echo admin > $t/etc/99-default.link; chmod 644 $t/etc/99-default.link
find -L $t/usr -name '*.link*' -execdir cp -r -pt $t/dst/ '{}' +
find -L $t/etc -name '*.link*' -execdir cp -r -pt $t/dst/ '{}' +
stat -c '%A %s' $t/victim
Result: "-rw-r--r-- 6" (mode and content of the victim file changed).
Expected: unchanged.
In my case it was triggered by running the hook twice into the same
$DESTDIR (a leftover symlink /etc/initramfs-tools/hooks/udev ->
/usr/share/initramfs-tools/hooks/udev), together with symlinks to
/dev/null in a subdirectory of /etc/systemd/network that the recursive
find descends into. The plain "documented mask" case (regular file in
/usr/lib, symlink in /etc, one run) is not affected.
Proposed fix: add --remove-destination to the three cp calls in the
hook, i.e.
find -L ... -name '*.link*' -execdir cp -r --remove-destination -pt
"$DESTDIR/usr/lib/systemd/network/" '{}' +
An existing entry in $DESTDIR (symlink or regular file) is then replaced
instead of followed, so later files of the same name override earlier
ones as intended and nothing outside $DESTDIR is touched. I verified
this with the reproduction above and with the two-run variant (hook run
twice into the same $DESTDIR): the victim file stays unchanged in both
cases. I did not test it inside a full package build.
Best regards
Chizzon
More information about the Pkg-systemd-maintainers
mailing list