diff options
author | gordon <gordon@FreeBSD.org> | 2019-07-24 12:57:49 +0000 |
---|---|---|
committer | gordon <gordon@FreeBSD.org> | 2019-07-24 12:57:49 +0000 |
commit | c1fcd74c987c51e233da57e48dfda0bbd87da794 (patch) | |
tree | 32a3ff2e85bcef9b631abda5d289471a2a23b2fa /sys/kern | |
parent | 7ff5bf4da5b7ccef5593da9181d436e9043437a9 (diff) | |
download | FreeBSD-src-c1fcd74c987c51e233da57e48dfda0bbd87da794.zip FreeBSD-src-c1fcd74c987c51e233da57e48dfda0bbd87da794.tar.gz |
Fix file descriptor reference count leak.
Approved by: so
Security: FreeBSD-SA-19:17.fd
Security: CVE-2019-5607
Diffstat (limited to 'sys/kern')
-rw-r--r-- | sys/kern/uipc_usrreq.c | 41 |
1 files changed, 33 insertions, 8 deletions
diff --git a/sys/kern/uipc_usrreq.c b/sys/kern/uipc_usrreq.c index 0ff3540..d1a3958 100644 --- a/sys/kern/uipc_usrreq.c +++ b/sys/kern/uipc_usrreq.c @@ -1896,29 +1896,52 @@ unp_init(void) UNP_DEFERRED_LOCK_INIT(); } +static void +unp_internalize_cleanup_rights(struct mbuf *control) +{ + struct cmsghdr *cp; + struct mbuf *m; + void *data; + socklen_t datalen; + + for (m = control; m != NULL; m = m->m_next) { + cp = mtod(m, struct cmsghdr *); + if (cp->cmsg_level != SOL_SOCKET || + cp->cmsg_type != SCM_RIGHTS) + continue; + data = CMSG_DATA(cp); + datalen = (caddr_t)cp + cp->cmsg_len - (caddr_t)data; + unp_freerights(data, datalen / sizeof(struct filedesc *)); + } +} + static int unp_internalize(struct mbuf **controlp, struct thread *td) { - struct mbuf *control = *controlp; - struct proc *p = td->td_proc; - struct filedesc *fdesc = p->p_fd; + struct mbuf *control, **initial_controlp; + struct proc *p; + struct filedesc *fdesc; struct bintime *bt; - struct cmsghdr *cm = mtod(control, struct cmsghdr *); + struct cmsghdr *cm; struct cmsgcred *cmcred; struct filedescent *fde, **fdep, *fdev; struct file *fp; struct timeval *tv; - int i, *fdp; void *data; - socklen_t clen = control->m_len, datalen; - int error, oldfds; + socklen_t clen, datalen; + int i, error, *fdp, oldfds; u_int newlen; UNP_LINK_UNLOCK_ASSERT(); + p = td->td_proc; + fdesc = p->p_fd; error = 0; + control = *controlp; + clen = control->m_len; *controlp = NULL; - while (cm != NULL) { + initial_controlp = controlp; + for (cm = mtod(control, struct cmsghdr *); cm != NULL;) { if (sizeof(*cm) > clen || cm->cmsg_level != SOL_SOCKET || cm->cmsg_len > clen || cm->cmsg_len < sizeof(*cm)) { error = EINVAL; @@ -2045,6 +2068,8 @@ unp_internalize(struct mbuf **controlp, struct thread *td) } out: + if (error != 0 && initial_controlp != NULL) + unp_internalize_cleanup_rights(*initial_controlp); m_freem(control); return (error); } |