Skip to content

init scripts: start-stop-daemon -p can spawn duplicate daemons and kill unrelated processes #2325

Description

@widgetii

While fixing S95majestic in #2324 I traced a family of init-script bugs to a single flag, and eleven other scripts still carry the same pattern.

The defect

Busybox start-stop-daemon's do_procinit() reads only the pid the -p pidfile holds and never scans /proc. -x is then just a filter applied to that one pid. So once the pidfile is stale, start-stop-daemon cannot see the live daemon at all.

Measured on a Hi3516EV300 (busybox 1.36.1), with the daemon live as pid 1177 and the pidfile poisoned with a dead pid:

# start-stop-daemon -b -m -S -q -p /var/run/majestic.pid -x majestic -- -s
rc=0                      # started a SECOND instance, pid 1233
# cat /var/run/majestic.pid
1233                      # -m overwrote the file with the duplicate's pid

Two consequences:

  1. Duplicate daemons. -S cannot see the running instance, so it starts another one. If the duplicate then dies, -m has already replaced the pidfile with its dead pid, so the state is self-perpetuating.
  2. Killing unrelated processes. Every one of these scripts stops with start-stop-daemon -K -q -p "$PIDFILE" and no -x guard, so it signals whatever process now owns that pid. With the pidfile pointing at an unrelated live process, I watched it kill that process while the real daemon kept running.

Affected scripts

Script Package
S01syslogd general/overlay/etc/init.d/
S49ntpd general/overlay/etc/init.d/
S50dropbear general/overlay/etc/init.d/
S60crond general/overlay/etc/init.d/
S50mdnsd mdnsd-openipc
S60siproxd siproxd-openipc
S60precision-time openipc-precision-time
S89edge n3n-openipc
S90matter matter
S96onvifserver onvif-simple-server
S97baresip baresip-openipc

These bite far less often than majestic did, because those daemons rarely die and get restarted — majestic is the one that gets OOM-killed, hits a busy sensor HAL, and is restarted by watchdogs and sysupgrade. The defect is latent, not theoretical.

S60crond deserves priority: a crond watchdog is the intended recovery path for a dead majestic, so a crond that cannot be reliably restarted defeats the recovery story.

The fix

Same shape as #2324 — drop -p, match on the absolute executable, and keep argv[0] stable:

DAEMON="foo"
DAEMON_PATH="/usr/bin/$DAEMON"

running() { start-stop-daemon -t -K -q -x "$DAEMON_PATH"; }

start()   { start-stop-daemon -b -S -q -x "$DAEMON_PATH" -a "$DAEMON" -- $DAEMON_ARGS; }
stop()    { start-stop-daemon -K -q -x "$DAEMON_PATH"
            i=0; while running && [ $i -lt 10 ]; do i=$((i + 1)); sleep 1; done
            running && return 1
            rm -f "/var/run/$DAEMON.pid"; }

-a "$DAEMON" matters: without it argv[0] becomes the absolute path, ps output changes, and bare-name -x matchers break. With it, ps, pidof and killall behave exactly as they do today.

Worth doing as one sweep once #2324 has settled, since it touches every board's boot path. Each script's binary path needs checking individually — they are not all under /usr/bin.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions