summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorgordon <gordon@FreeBSD.org>2019-07-24 12:57:49 +0000
committergordon <gordon@FreeBSD.org>2019-07-24 12:57:49 +0000
commitc1fcd74c987c51e233da57e48dfda0bbd87da794 (patch)
tree32a3ff2e85bcef9b631abda5d289471a2a23b2fa
parent7ff5bf4da5b7ccef5593da9181d436e9043437a9 (diff)
downloadFreeBSD-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
-rw-r--r--sys/kern/uipc_usrreq.c41
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);
}
OpenPOWER on IntegriCloud