git: 45711ee38a09 - stable/13 - filemon: Reject FILEMON_SET_FD commands when the fd is a kqueue

From: Mark Johnston <markj_at_FreeBSD.org>
Date: Thu, 10 Feb 2022 13:46:52 UTC
The branch stable/13 has been updated by markj:

URL: https://cgit.FreeBSD.org/src/commit/?id=45711ee38a09b8b8083d13b3aeec64fa2e3d075e

commit 45711ee38a09b8b8083d13b3aeec64fa2e3d075e
Author:     Mark Johnston <markj@FreeBSD.org>
AuthorDate: 2022-02-03 14:41:17 +0000
Commit:     Mark Johnston <markj@FreeBSD.org>
CommitDate: 2022-02-10 13:46:24 +0000

    filemon: Reject FILEMON_SET_FD commands when the fd is a kqueue
    
    When FILEMON_SET_FD is used, the filemon handle effectively wraps the
    passed file.  In particular, the handle may be inherited by a child
    process, or transferred over a unix domain socket, so we must verify
    that the backing file permits this.
    
    Reported by:    syzbot+36e6be9e02735fe66ca8@syzkaller.appspotmail.com
    Reviewed by:    emaste
    Sponsored by:   The FreeBSD Foundation
    
    (cherry picked from commit b84ed4e7f626de5475cf26bae6d7ff15ec9f9675)
---
 sys/dev/filemon/filemon.c | 20 +++++++++++++++-----
 1 file changed, 15 insertions(+), 5 deletions(-)

diff --git a/sys/dev/filemon/filemon.c b/sys/dev/filemon/filemon.c
index cd48c68d8b04..4c3a23a53042 100644
--- a/sys/dev/filemon/filemon.c
+++ b/sys/dev/filemon/filemon.c
@@ -359,9 +359,10 @@ static int
 filemon_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag __unused,
     struct thread *td)
 {
-	int error = 0;
 	struct filemon *filemon;
+	struct file *fp;
 	struct proc *p;
+	int error;
 
 	if ((error = devfs_get_cdevpriv((void **) &filemon)) != 0)
 		return (error);
@@ -376,12 +377,21 @@ filemon_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag __unused,
 			break;
 		}
 
-		error = fget_write(td, *(int *)data,
-		    &cap_pwrite_rights,
-		    &filemon->fp);
-		if (error == 0)
+		error = fget_write(td, *(int *)data, &cap_pwrite_rights, &fp);
+		if (error == 0) {
+			/*
+			 * The filemon handle may be passed to another process,
+			 * so the underlying file handle must support this.
+			 */
+			if ((fp->f_ops->fo_flags & DFLAG_PASSABLE) == 0) {
+				fdrop(fp, curthread);
+				error = EINVAL;
+				break;
+			}
+			filemon->fp = fp;
 			/* Write the file header. */
 			filemon_write_header(filemon);
+		}
 		break;
 
 	/* Set the monitored process ID. */