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);

Reply via email to