Hi tech@,

sthen@ pointed out to me that dhcpd doesn't properly terminate the pf table
handler. 

I reproduced the issue both on 6.1 and -current.

Minimal config I used on my server:
/etc/dhcpd.conf
 subnet 45.63.9.186 netmask 255.255.255.224 {
   range 45.63.9.186 45.63.9.186;
 }

enabled dhcpd and used the -A flag to trigger the pf handler being spawned:
 rcctl enable dhcpd
 rcctl set dhcpd flags -A test

and performed the test

# rcctl start dhcpd; rcctl stop dhcpd; pgrep -lf dhcpd
dhcpd(ok)
dhcpd(ok)
96584 dhcpd: pf table handler

which demonstrates that indeed the pf table handler is left running.

Both the main program and the child spin in a for(;;) loop, there is no
code anticipating the need to signal a child to quit, there is also
no attempt to detect the parent dying by the child.

The child also uses a notreached exit(3) after the loop.

When the parent is not around the unattended child proceeds to puke all over
the logfiles:

Jul 11 21:04:06 tintagel dhcpd[62506]: pf pipe error: Broken pipe
Jul 11 21:04:06 tintagel dhcpd[62506]: pf pipe closed
Jul 11 21:04:06 tintagel dhcpd[62506]: pf pipe error: Broken pipe
Jul 11 21:04:06 tintagel dhcpd[62506]: pf pipe closed
Jul 11 21:04:06 tintagel dhcpd[62506]: pf pipe error: Broken pipe
Jul 11 21:04:06 tintagel dhcpd[62506]: pf pipe closed
Jul 11 21:04:06 tintagel dhcpd[62506]: pf pipe error: Broken pipe
Jul 11 21:04:06 tintagel dhcpd[62506]: pf pipe closed
Jul 11 21:04:06 tintagel dhcpd[62506]: pf pipe error: Broken pipe
Jul 11 21:04:06 tintagel dhcpd[62506]: pf pipe closed
Jul 11 21:04:06 tintagel dhcpd[62506]: pf pipe error: Broken pipe

brynet@ noticed the exit(3) instead of _exit(2) and he also pointed
out that the pf table handler is a bit optimistic on warnings instead of bailing
out with an error and terminating.

Examples:
55    if ((fd = open(_PATH_DEV_PF, O_RDWR|O_NOFOLLOW, 0660)) == -1)
56        log_warn("can't open pf device");

this child is useless if it can't pf

57    if (chroot(_PATH_VAREMPTY) == -1)
58        log_warn("chroot %s", _PATH_VAREMPTY);
59    if (chdir("/") == -1)
60        log_warn("chdir(\"/\")");
61    if (setgroups(1, &pw->pw_gid) ||
62        setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
63        setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
64        log_warn("can't drop privileges");

not able to chroot and drop privileges seems like good enough reason to die.

76        if (nfds > 0 && (pfd[0].revents & POLLHUP))
77            log_warnx("pf pipe closed");

we won't get any work to do as the pipe is closed, should we die?

Now, I did try a quick diff changing log_warn on those to fatal()
https://junk.tintagel.pl/dhcpd-pf-handler.diff

This of course results in the child dying when the parent is gone
but we believe it's not commitable as the following needs to be
decided:

 - if the handler fails (ie. not able to open pf) should it signal
   the main process to die or just stop working (like it did now)?
 - fatal() can't be used as it uses exit(3) instead of _exit(2)
 - anything else that we might have missed

looking for pointers/suggestions on how to move forward with this.

regards,
Adam

Reply via email to