aboutsummaryrefslogtreecommitdiff
path: root/sys/kern/uipc_usrreq.c
diff options
context:
space:
mode:
authorKonstantin Belousov <kib@FreeBSD.org>2019-07-21 15:07:12 +0000
committerKonstantin Belousov <kib@FreeBSD.org>2019-07-21 15:07:12 +0000
commitf1cf2b9dcb9a4e8cf8295e97eeaa4add96e80412 (patch)
treed5fcf8602ad5925792cac5ce475ee1d48f94583d /sys/kern/uipc_usrreq.c
parent349c8683227737360dfef03187c02a582203e75c (diff)
downloadsrc-f1cf2b9dcb9a4e8cf8295e97eeaa4add96e80412.tar.gz
src-f1cf2b9dcb9a4e8cf8295e97eeaa4add96e80412.zip
Check and avoid overflow when incrementing fp->f_count in
fget_unlocked() and fhold(). On sufficiently large machine, f_count can be legitimately very large, e.g. malicious code can dup same fd up to the per-process filedescriptors limit, and then fork as much as it can. On some smaller machine, I see kern.maxfilesperproc: 939132 kern.maxprocperuid: 34203 which already overflows u_int. More, the malicious code can create transient references by sending fds over unix sockets. I realized that this check is missed after reading https://secfault-security.com/blog/FreeBSD-SA-1902.fd.html Reviewed by: markj (previous version), mjg Tested by: pho (previous version) Sponsored by: The FreeBSD Foundation MFC after: 1 week Differential revision: https://reviews.freebsd.org/D20947
Notes
Notes: svn path=/head/; revision=350199
Diffstat (limited to 'sys/kern/uipc_usrreq.c')
-rw-r--r--sys/kern/uipc_usrreq.c20
1 files changed, 16 insertions, 4 deletions
diff --git a/sys/kern/uipc_usrreq.c b/sys/kern/uipc_usrreq.c
index 0ffa9d3a22ba..1a875d7e5ed4 100644
--- a/sys/kern/uipc_usrreq.c
+++ b/sys/kern/uipc_usrreq.c
@@ -2154,7 +2154,7 @@ unp_internalize(struct mbuf **controlp, struct thread *td)
struct timespec *ts;
void *data;
socklen_t clen, datalen;
- int i, error, *fdp, oldfds;
+ int i, j, error, *fdp, oldfds;
u_int newlen;
UNP_LINK_UNLOCK_ASSERT();
@@ -2237,6 +2237,19 @@ unp_internalize(struct mbuf **controlp, struct thread *td)
goto out;
}
fdp = data;
+ for (i = 0; i < oldfds; i++, fdp++) {
+ if (!fhold(fdesc->fd_ofiles[*fdp].fde_file)) {
+ fdp = data;
+ for (j = 0; j < i; j++, fdp++) {
+ fdrop(fdesc->fd_ofiles[*fdp].
+ fde_file, td);
+ }
+ FILEDESC_SUNLOCK(fdesc);
+ error = EBADF;
+ goto out;
+ }
+ }
+ fdp = data;
fdep = (struct filedescent **)
CMSG_DATA(mtod(*controlp, struct cmsghdr *));
fdev = malloc(sizeof(*fdev) * oldfds, M_FILECAPS,
@@ -2440,7 +2453,6 @@ unp_internalize_fp(struct file *fp)
unp->unp_file = fp;
unp->unp_msgcount++;
}
- fhold(fp);
unp_rights++;
UNP_LINK_WUNLOCK();
}
@@ -2601,10 +2613,10 @@ unp_gc(__unused void *arg, int pending)
if ((unp->unp_gcflag & UNPGC_DEAD) != 0) {
f = unp->unp_file;
if (unp->unp_msgcount == 0 || f == NULL ||
- f->f_count != unp->unp_msgcount)
+ f->f_count != unp->unp_msgcount ||
+ !fhold(f))
continue;
unref[total++] = f;
- fhold(f);
KASSERT(total <= unp_unreachable,
("unp_gc: incorrect unreachable count."));
}