start-stop-daemon.c: fix failure case on --notify fd:4

When the created notified pipe (often on 4) is the same number as
the notification fd target, dup2() does not clear the FD_CLOEXEC
flag, so the fd gets closed right when exec'ing the daemon,
reporting readiness failure. Fix this by explicitly testing for
the case and clearing the flag when necessary.

Note that this happens because of a call to close_range() right
before the test. close_range() is the real problem, it should
never be used and this bug is a perfect illustration of why; but
getting rid of close_range() is a much more invasive change that
I don't want to commit to right now, especially since navi's plan
is to eventually deprecate start-stop-daemon.
This commit is contained in:
Laurent Bercot 2025-11-18 09:15:18 +01:00 committed by navi
parent aa456f3827
commit f09a15d461

View File

@ -1130,13 +1130,25 @@ int main(int argc, char **argv)
|| rc_yesno(getenv("EINFO_QUIET")))
dup2(stderr_fd, STDERR_FILENO);
cloexec_fds_from(3);
cloexec_fds_from(3); /* FIXME: this is problematic, see right below */
if (notify.type == NOTIFY_FD) {
if (close(notify.pipe[0]) == -1)
eerrorx("%s: failed to close notify pipe[0]: %s", applet, strerror(errno));
if (dup2(notify.pipe[1], notify.fd) == -1)
eerrorx("%s: failed to initialize notify fd: %s", applet, strerror(errno));
/* if notify.pipe[1] == notify.fd then the FD_CLOEXEC flag is not cleared by dup2,
leading to failure. The workaround here is to clear it manually, but the
real fix is that we should never close/cloexec fds in bulk like this */
if (notify.pipe[1] == notify.fd) {
int flags = fcntl(notify.fd, F_GETFD, 0);
if (flags == -1)
eerrorx("%s: failed to get flags for notify fd: %s", applet, strerror(errno));
if (fcntl(notify.fd, F_SETFD, flags & ~FD_CLOEXEC) == -1)
eerrorx("%s: failed to set flags for notify fd: %s", applet, strerror(errno));
}
}
if (scheduler != NULL) {