Package: init-system-helpers
Version: 1.69+nmu1
Severity: normal
Tags: patch
User: [email protected]
Usertags: origin-ubuntu stonking ubuntu-patch
Dear Maintainer,
remove_links() goes into infinite loop if we have several units that
refer to each other via Also= directive.
on a regular setup, we do not hit this issue since the units files have been
removed
before the purge step. however, if we have the situation where A and B provides
the
same unit files (A Replaces B).
and we do:
$ apt remove --purge A B+
(remove/purge A and install B)
The units are still present in /lib when A is purged and trigger the infinite
traversal
loop of deb-systemd-helper remove_links() function.
* Fix deb-systemd-helper remove_links() infinite loop
Thanks for considering the patch.
-- System Information:
Debian Release: trixie/sid
APT prefers noble-updates
APT policy: (500, 'noble-updates'), (500, 'noble-security'), (500, 'noble'),
(100, 'noble-backports')
Architecture: amd64 (x86_64)
Kernel: Linux 7.0.0-28-generic (SMP w/16 CPU threads; PREEMPT)
Kernel taint flags: TAINT_PROPRIETARY_MODULE, TAINT_OOT_MODULE
Locale: LANG=en_US.UTF-8, LC_CTYPE=en_US.UTF-8 (charmap=UTF-8), LANGUAGE not set
Shell: /bin/sh linked to /usr/bin/dash
Init: systemd (via /run/systemd/system)
LSM: AppArmor: enabled
Versions of packages init-system-helpers is related to:
pn insserv <none>
diff -Nru init-system-helpers-1.69+nmu1/script/deb-systemd-helper
init-system-helpers-1.69+nmu1ubuntu1/script/deb-systemd-helper
--- init-system-helpers-1.69+nmu1/script/deb-systemd-helper 2022-07-13
21:55:56.000000000 +0200
+++ init-system-helpers-1.69+nmu1ubuntu1/script/deb-systemd-helper
2026-08-22 10:54:23.000000000 +0200
@@ -439,7 +439,7 @@
}
sub remove_links {
- my ($service_path) = @_;
+ my ($service_path, @visited) = @_;
my $dsh_state = dsh_state_path($service_path);
my @entries = state_file_entries($dsh_state);
@@ -480,12 +480,23 @@
# dh_installsystemd generating an appropriate disable
# command by parsing the service file at debhelper-time.
open(my $fh, '<', "$dpkg_root$service_path") or return;
+ my $unit_name = basename($service_path);
+ push @visited, $unit_name;
while (my $line = <$fh>) {
chomp($line);
my $service_link;
if ($line =~ /^\s*Also=(.+)$/i) {
- remove_links(find_unit($1));
+ # Also= can contain multiple unit names, separated by whitespace.
See
+ # systemd.unit(5) for details.
+ for my $value (split(/\s+/, $1)) {
+ $value =~ s/^(["'])(.*)\g1$/$2/;
+ # We can end up in an infinite recursion, so remember what
units we
+ # already processed to break it
+ next if grep $_ eq $value, @visited;
+ push @visited, $value;
+ remove_links(find_unit($value), @visited);
+ }
}
}
close($fh);