aboutsummaryrefslogtreecommitdiff
path: root/sys/kern/kern_descrip.c
diff options
context:
space:
mode:
authorMateusz Guzik <mjg@FreeBSD.org>2022-03-24 20:51:03 +0000
committerMateusz Guzik <mjg@FreeBSD.org>2022-04-02 12:09:07 +0000
commit0c805718cbd3709e3ffc1a0d41612168c8242360 (patch)
tree7e3342222bf6591233d864ea2f145dc5a637efc0 /sys/kern/kern_descrip.c
parent17628f1b7927cefe2c28c6cb786cc51890589cf9 (diff)
downloadsrc-0c805718cbd3709e3ffc1a0d41612168c8242360.tar.gz
src-0c805718cbd3709e3ffc1a0d41612168c8242360.zip
vfs: fix memory leak on lookup with fds with ioctl caps
Reviewed by: markj PR: 262515 Noted by: firk@cantconnect.ru Differential Revision: https://reviews.freebsd.org/D34667
Diffstat (limited to 'sys/kern/kern_descrip.c')
-rw-r--r--sys/kern/kern_descrip.c75
1 files changed, 74 insertions, 1 deletions
diff --git a/sys/kern/kern_descrip.c b/sys/kern/kern_descrip.c
index b13fc719c2b0..dd510cfd23f9 100644
--- a/sys/kern/kern_descrip.c
+++ b/sys/kern/kern_descrip.c
@@ -1805,11 +1805,19 @@ filecaps_fill(struct filecaps *fcaps)
/*
* Free memory allocated within filecaps structure.
*/
+static void
+filecaps_free_ioctl(struct filecaps *fcaps)
+{
+
+ free(fcaps->fc_ioctls, M_FILECAPS);
+ fcaps->fc_ioctls = NULL;
+}
+
void
filecaps_free(struct filecaps *fcaps)
{
- free(fcaps->fc_ioctls, M_FILECAPS);
+ filecaps_free_ioctl(fcaps);
bzero(fcaps, sizeof(*fcaps));
}
@@ -3047,6 +3055,71 @@ fgetvp_lookup_smr(int fd, struct nameidata *ndp, struct vnode **vpp, bool *fsear
}
#endif
+int
+fgetvp_lookup(int fd, struct nameidata *ndp, struct vnode **vpp)
+{
+ struct thread *td;
+ struct file *fp;
+ struct vnode *vp;
+ struct componentname *cnp;
+ cap_rights_t rights;
+ int error;
+
+ td = curthread;
+ rights = *ndp->ni_rightsneeded;
+ cap_rights_set_one(&rights, CAP_LOOKUP);
+ cnp = &ndp->ni_cnd;
+
+ error = fget_cap(td, ndp->ni_dirfd, &rights, &fp, &ndp->ni_filecaps);
+ if (__predict_false(error != 0))
+ return (error);
+ if (__predict_false(fp->f_ops == &badfileops)) {
+ error = EBADF;
+ goto out_free;
+ }
+ vp = fp->f_vnode;
+ if (__predict_false(vp == NULL)) {
+ error = ENOTDIR;
+ goto out_free;
+ }
+ vref(vp);
+ /*
+ * XXX does not check for VDIR, handled by namei_setup
+ */
+ if ((fp->f_flag & FSEARCH) != 0)
+ cnp->cn_flags |= NOEXECCHECK;
+ fdrop(fp, td);
+
+#ifdef CAPABILITIES
+ /*
+ * If file descriptor doesn't have all rights,
+ * all lookups relative to it must also be
+ * strictly relative.
+ */
+ CAP_ALL(&rights);
+ if (!cap_rights_contains(&ndp->ni_filecaps.fc_rights, &rights) ||
+ ndp->ni_filecaps.fc_fcntls != CAP_FCNTL_ALL ||
+ ndp->ni_filecaps.fc_nioctls != -1) {
+ ndp->ni_lcf |= NI_LCF_STRICTRELATIVE;
+ ndp->ni_resflags |= NIRES_STRICTREL;
+ }
+#endif
+
+ /*
+ * TODO: avoid copying ioctl caps if it can be helped to begin with
+ */
+ if ((cnp->cn_flags & WANTIOCTLCAPS) == 0)
+ filecaps_free_ioctl(&ndp->ni_filecaps);
+
+ *vpp = vp;
+ return (0);
+
+out_free:
+ filecaps_free(&ndp->ni_filecaps);
+ fdrop(fp, td);
+ return (error);
+}
+
/*
* Fetch the descriptor locklessly.
*