aboutsummaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorMark Johnston <markj@FreeBSD.org>2022-03-17 16:54:37 +0000
committerMark Johnston <markj@FreeBSD.org>2022-03-17 19:39:00 +0000
commitc70224229205c756bf1c2007a6b96b37126eb047 (patch)
tree082ba42ce39adac981d558f92a6f9be4edb445b8 /sys
parent7846554819d3be52816ca6ad60bf84f6409a0bf1 (diff)
downloadsrc-c70224229205c756bf1c2007a6b96b37126eb047.tar.gz
src-c70224229205c756bf1c2007a6b96b37126eb047.zip
file: Avoid a read-after-free of fd tables in sysctl handlers
Some loops access the fd table of a different process, and drop the filedesc lock while iterating, so they check the table's refcount. However, we access the table before the first iteration, in order to get the number of table entries, and this access can be a use-after-free. Fix the problem by checking the refcount before we start iterating. Reported by: pho Reviewed by: mjg MFC after: 2 weeks Sponsored by: The FreeBSD Foundation Differential Revision: https://reviews.freebsd.org/D34575
Diffstat (limited to 'sys')
-rw-r--r--sys/kern/kern_descrip.c27
1 files changed, 18 insertions, 9 deletions
diff --git a/sys/kern/kern_descrip.c b/sys/kern/kern_descrip.c
index 07984b9a4640..da1eb24cf9c8 100644
--- a/sys/kern/kern_descrip.c
+++ b/sys/kern/kern_descrip.c
@@ -4194,9 +4194,9 @@ sysctl_kern_file(SYSCTL_HANDLER_ARGS)
if (fdp == NULL)
continue;
FILEDESC_SLOCK(fdp);
+ if (refcount_load(&fdp->fd_refcnt) == 0)
+ goto nextproc;
FILEDESC_FOREACH_FP(fdp, n, fp) {
- if (refcount_load(&fdp->fd_refcnt) == 0)
- break;
xf.xf_fd = n;
xf.xf_file = (uintptr_t)fp;
xf.xf_data = (uintptr_t)fp->f_data;
@@ -4207,9 +4207,16 @@ sysctl_kern_file(SYSCTL_HANDLER_ARGS)
xf.xf_offset = foffset_get(fp);
xf.xf_flag = fp->f_flag;
error = SYSCTL_OUT(req, &xf, sizeof(xf));
- if (error)
+
+ /*
+ * There is no need to re-check the fdtable refcount
+ * here since the filedesc lock is not dropped in the
+ * loop body.
+ */
+ if (error != 0)
break;
}
+nextproc:
FILEDESC_SUNLOCK(fdp);
fddrop(fdp);
if (error)
@@ -4469,9 +4476,9 @@ kern_proc_filedesc_out(struct proc *p, struct sbuf *sb, ssize_t maxlen,
if (pwd != NULL)
pwd_drop(pwd);
FILEDESC_SLOCK(fdp);
+ if (refcount_load(&fdp->fd_refcnt) == 0)
+ goto skip;
FILEDESC_FOREACH_FP(fdp, i, fp) {
- if (refcount_load(&fdp->fd_refcnt) == 0)
- break;
#ifdef CAPABILITIES
rights = *cap_rights(fdp, i);
#else /* !CAPABILITIES */
@@ -4484,9 +4491,10 @@ kern_proc_filedesc_out(struct proc *p, struct sbuf *sb, ssize_t maxlen,
* loop continues.
*/
error = export_file_to_sb(fp, i, &rights, efbuf);
- if (error != 0)
+ if (error != 0 || refcount_load(&fdp->fd_refcnt) == 0)
break;
}
+skip:
FILEDESC_SUNLOCK(fdp);
fail:
if (fdp != NULL)
@@ -4633,18 +4641,19 @@ sysctl_kern_proc_ofiledesc(SYSCTL_HANDLER_ARGS)
if (pwd != NULL)
pwd_drop(pwd);
FILEDESC_SLOCK(fdp);
+ if (refcount_load(&fdp->fd_refcnt) == 0)
+ goto skip;
FILEDESC_FOREACH_FP(fdp, i, fp) {
- if (refcount_load(&fdp->fd_refcnt) == 0)
- break;
export_file_to_kinfo(fp, i, NULL, kif, fdp,
KERN_FILEDESC_PACK_KINFO);
FILEDESC_SUNLOCK(fdp);
kinfo_to_okinfo(kif, okif);
error = SYSCTL_OUT(req, okif, sizeof(*okif));
FILEDESC_SLOCK(fdp);
- if (error)
+ if (error != 0 || refcount_load(&fdp->fd_refcnt) == 0)
break;
}
+skip:
FILEDESC_SUNLOCK(fdp);
fddrop(fdp);
pddrop(pdp);