Bug#1063147: 'telinit u' infinitely re-exec's itself inside containers

Daniel P. Berrangé berrange at redhat.com
Mon Feb 5 11:45:57 GMT 2024


Package: systemd-sysv
Version: 255.3-2

Running 'telinit u' within a podman container results in an infinite
loop as telinit repeatedly re-exec's itself.

This behaviour comes from systemctl.c which has this logic for handling
'telinit':

                if (sd_booted() > 0) {
                        arg_action = _ACTION_INVALID;
                        return telinit_parse_argv(argc, argv);
                } else {
                        /* Hmm, so some other init system is running, we need to forward this request to it.
                         */
                        arg_action = ACTION_TELINIT;
                        return 1;
                }

Inside a container 'sd_booted()' will (typically) indicate systemd is
NOT running, thus the 'else' clause will be followed. ACTION_TELINIT
instructs the caller to execve() the telinit binary belonging to any
*non-systemd* init impl, if any.

This binary is determined by the 'telinit-path' meson build option,
which defaults to  /lib/sysvinit/telinit.

Debian used to override this to /usr/lib/sysvinit/telinit, but a few
months ago in Sid, this was changed to point to /usr/sbin/telinit,
which is a symlink back to /usr/bin/systemctl:

  https://salsa.debian.org/systemd-team/systemd/-/commit/da95bc801088a6ab454851cf01addf97dd2c1ab3

IOW, Debian dpkg build has told systemd that the non-systemd telinit
binary is the systemd telinit binary. Hilarity now ensues as it ends
up exec'ing itself for all eternity :-)


You might ask why should anyone hit this scenario ? Well consider that
the 'libc6' package will run 'telinit u' in its postinst script, if it
sees it is in a non-systemd environment.

Not immediately a problem, since libc6 will be pre-installed in any
container or VM disk image and thus the 'postinst' script won't run
[not sure if 'postinst' is run on upgrades too ?].

Debian containers are an execellent environment for testing cross
compiles though, and thus people will install a foreign arch libc6
package in the container which does trigger the postinst script. The
following hangs (well loops forever in execve()):

  $ podman run -it debian:sid-slim
  # dpkg --add-architecture i386
  # apt-get update
  # apt-get install systemd-sysv
  # apt-get install libc6:i386

Simpler example

  $ podman run -it debian:sid-slim
  # dpkg --add-architecture i386
  # apt-get update
  # apt-get install systemd-sysv strace
  # strace -e trace=execve telinit u
  strace: Process 232065 attached
  execve("/usr/sbin/telinit", ["telinit", "u"], 0x7ffd9b26ba00 /* 24 vars */) = 0
  execve("/usr/sbin/telinit", ["telinit", "u"], 0x7ffdab55f1d0 /* 24 vars */) = 0
  execve("/usr/sbin/telinit", ["telinit", "u"], 0x7ffe79152400 /* 24 vars */) = 0
  ....1000000's more times....

Even then though, most people won't (knowingly) install the systemd-sysv
dpkg, so won't hit this problem. A few packages will pull in systemd-sysv
behind the scenes, so you can unwittingly hit the problem. For libvirt
CI, we install the open-iscsi and policykit-1 packages which both pull
in  systemd-sysv, so this hangs:

  $ podman run -it debian:sid-slim
  # dpkg --add-architecture i386
  # apt-get update
  # apt-get install systemd-sysv
  # apt-get install open-iscsi:i386

Our foreign arch CI jobs that use Sid are thus suffering broken container
builds right now.

The simple solution appears to be to just remove the '-Dtelinit-path'
option from debian/rules, and leave it on systemd's built-in defaults.
The binary at this default path won't exist, and thus on a non-systemd
execution environment 'telinit u' will simply exit with an error:

  # telinit u
  Couldn't find an alternative telinit implementation to spawn.

which is a sensible behaviour and what has happened in containers with
Debian until recent Sid.  Other distros (eg Fedora) leave the telinit
binary on systemd's default (non-existant) path too.

Possibly the upstream systemctl.c code should be made to protect itself
against such a mis-configuration by setting an env variable it can look
at to detect re-exec of itself.

Possibly libc6 package postinst script should skip running its
'telinit u' action if it detects it is inside a container, though that
could possibly break something if people do have a real in-systemd init
running ? Seems fairly low probability.

With regards,
Daniel



More information about the Pkg-systemd-maintainers mailing list