Module Name: src Committed By: christos Date: Thu Sep 30 12:35:55 UTC 2021
Modified Files: src/lib/libc/resolv: res_init.c res_private.h Log Message: kqueue(2) file descriptors are not inherited across fork(2). A process that that calls getaddrinfo(3) will end up cacheing the kqueue(2) file descriptor in its res_state structure. If that process fork(2)s and calls getaddrinfo(3) again might end up closing that cached file descriptor which can end up pointing to a different file object than the kqueue(2) original one. To fix this, associate the kqueue(2) file descriptor with the process id that created it, and don't close(2) it if it is being closed from a different process. An alternative fix would be to attach the resolver to a fork(2) hook to cleanup the res_state, but handling it internally in the resolver is less intrusive. This was discovered by Dima Veselov when using the FreeRADIUS package. To generate a diff of this commit: cvs rdiff -u -r1.31 -r1.32 src/lib/libc/resolv/res_init.c cvs rdiff -u -r1.3 -r1.4 src/lib/libc/resolv/res_private.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/lib/libc/resolv/res_init.c diff -u src/lib/libc/resolv/res_init.c:1.31 src/lib/libc/resolv/res_init.c:1.32 --- src/lib/libc/resolv/res_init.c:1.31 Wed Apr 19 18:21:07 2017 +++ src/lib/libc/resolv/res_init.c Thu Sep 30 08:35:55 2021 @@ -1,4 +1,4 @@ -/* $NetBSD: res_init.c,v 1.31 2017/04/19 22:21:07 christos Exp $ */ +/* $NetBSD: res_init.c,v 1.32 2021/09/30 12:35:55 christos Exp $ */ /* * Copyright (c) 1985, 1989, 1993 @@ -72,7 +72,7 @@ static const char sccsid[] = "@(#)res_init.c 8.1 (Berkeley) 6/7/93"; static const char rcsid[] = "Id: res_init.c,v 1.26 2008/12/11 09:59:00 marka Exp"; #else -__RCSID("$NetBSD: res_init.c,v 1.31 2017/04/19 22:21:07 christos Exp $"); +__RCSID("$NetBSD: res_init.c,v 1.32 2021/09/30 12:35:55 christos Exp $"); #endif #endif /* LIBC_SCCS and not lint */ @@ -190,6 +190,21 @@ res_ninit(res_state statp) { return (__res_vinit(statp, 0)); } +static int +__res_kqinit(res_state statp) +{ + struct kevent kc; + struct __res_state_ext *ext = statp->_u._ext.ext; + + ext->kq = kqueue1(O_CLOEXEC); + ext->kqpid = getpid(); + EV_SET(&kc, ext->resfd, EVFILT_VNODE, + EV_ADD|EV_ENABLE|EV_CLEAR, NOTE_DELETE|NOTE_WRITE| NOTE_EXTEND| + NOTE_ATTRIB|NOTE_LINK|NOTE_RENAME|NOTE_REVOKE, 0, 0); + (void)kevent(ext->kq, &kc, 1, NULL, 0, &ts); + return ext->kq; +} + /*% This function has to be reachable by res_data.c but not publically. */ int __res_vinit(res_state statp, int preinit) { @@ -346,7 +361,6 @@ __res_vinit(res_state statp, int preinit nserv = 0; if ((fp = fopen(__res_conf_name, "re")) != NULL) { struct stat st; - struct kevent kc; /* read the config file */ while (fgets(buf, (int)sizeof(buf), fp) != NULL) { @@ -500,11 +514,7 @@ __res_vinit(res_state statp, int preinit if (fstat(statp->_u._ext.ext->resfd, &st) != -1) __res_conf_time = statp->_u._ext.ext->res_conf_time = st.st_mtimespec; - statp->_u._ext.ext->kq = kqueue1(O_CLOEXEC); - EV_SET(&kc, statp->_u._ext.ext->resfd, EVFILT_VNODE, - EV_ADD|EV_ENABLE|EV_CLEAR, NOTE_DELETE|NOTE_WRITE| NOTE_EXTEND| - NOTE_ATTRIB|NOTE_LINK|NOTE_RENAME|NOTE_REVOKE, 0, 0); - (void)kevent(statp->_u._ext.ext->kq, &kc, 1, NULL, 0, &ts); + __res_kqinit(statp); } else { statp->_u._ext.ext->kq = -1; statp->_u._ext.ext->resfd = -1; @@ -573,6 +583,9 @@ res_check(res_state statp, struct timesp struct kevent ke; if (statp->_u._ext.ext->kq == -1) goto out; + if (statp->_u._ext.ext->kqpid != getpid() && + __res_kqinit(statp) == -1) + goto out; switch (kevent(statp->_u._ext.ext->kq, NULL, 0, &ke, 1, &ts)) { case 0: @@ -812,13 +825,14 @@ res_nclose(res_state statp) void res_ndestroy(res_state statp) { + struct __res_state_ext *ext = statp->_u._ext.ext; res_nclose(statp); - if (statp->_u._ext.ext != NULL) { - if (statp->_u._ext.ext->kq != -1) - (void)close(statp->_u._ext.ext->kq); - if (statp->_u._ext.ext->resfd != -1) - (void)close(statp->_u._ext.ext->resfd); - free(statp->_u._ext.ext); + if (ext != NULL) { + if (ext->kq != -1 && ext->kqpid == getpid()) + (void)close(ext->kq); + if (ext->resfd != -1) + (void)close(ext->resfd); + free(ext); statp->_u._ext.ext = NULL; } if (statp->_rnd != NULL) { Index: src/lib/libc/resolv/res_private.h diff -u src/lib/libc/resolv/res_private.h:1.3 src/lib/libc/resolv/res_private.h:1.4 --- src/lib/libc/resolv/res_private.h:1.3 Sat Oct 24 13:24:01 2009 +++ src/lib/libc/resolv/res_private.h Thu Sep 30 08:35:55 2021 @@ -1,4 +1,4 @@ -/* $NetBSD: res_private.h,v 1.3 2009/10/24 17:24:01 christos Exp $ */ +/* $NetBSD: res_private.h,v 1.4 2021/09/30 12:35:55 christos Exp $ */ #ifndef res_private_h #define res_private_h @@ -16,6 +16,7 @@ struct __res_state_ext { char nsuffix2[64]; struct timespec res_conf_time; int kq, resfd; + pid_t kqpid; }; extern int res_ourserver_p(const res_state, const struct sockaddr *);