git: 994ed958ae05 - main - linux(4): Add a dedicated fstat() implementation

From: Dmitry Chagin <dchagin_at_FreeBSD.org>
Date: Fri, 28 Apr 2023 08:57:21 UTC
The branch main has been updated by dchagin:

URL: https://cgit.FreeBSD.org/src/commit/?id=994ed958ae0545faa86dd43b6e64bf7b64b9220b

commit 994ed958ae0545faa86dd43b6e64bf7b64b9220b
Author:     Dmitry Chagin <dchagin@FreeBSD.org>
AuthorDate: 2023-04-28 08:55:04 +0000
Commit:     Dmitry Chagin <dchagin@FreeBSD.org>
CommitDate: 2023-04-28 08:55:04 +0000

    linux(4): Add a dedicated fstat() implementation
    
    In between kern_fstat() and translate_fd_major_minor(), another process
    having the same filedesc could modify or close fd.
    
    Reviewed by:            kib
    Differential Revision:  https://reviews.freebsd.org/D39763
---
 sys/compat/linux/linux_stats.c | 75 +++++++++++++++++-------------------------
 1 file changed, 31 insertions(+), 44 deletions(-)

diff --git a/sys/compat/linux/linux_stats.c b/sys/compat/linux/linux_stats.c
index dc1e50a4fa00..260709b23202 100644
--- a/sys/compat/linux/linux_stats.c
+++ b/sys/compat/linux/linux_stats.c
@@ -47,6 +47,8 @@ __FBSDID("$FreeBSD$");
 #include <sys/ktrace.h>
 #endif
 
+#include <security/audit/audit.h>
+
 #ifdef COMPAT_LINUX32
 #include <machine/../linux32/linux.h>
 #include <machine/../linux32/linux32_proto.h>
@@ -59,6 +61,32 @@ __FBSDID("$FreeBSD$");
 #include <compat/linux/linux_util.h>
 
 
+static int
+linux_kern_fstat(struct thread *td, int fd, struct stat *sbp)
+{
+	struct vnode *vp;
+	struct file *fp;
+	int error;
+
+	AUDIT_ARG_FD(fd);
+
+	error = fget(td, fd, &cap_fstat_rights, &fp);
+	if (__predict_false(error != 0))
+		return (error);
+
+	AUDIT_ARG_FILE(td->td_proc, fp);
+
+	error = fo_stat(fp, sbp, td->td_ucred);
+	if (error == 0 && (vp = fp->f_vnode) != NULL)
+		translate_vnhook_major_minor(vp, sbp);
+	fdrop(fp, td);
+#ifdef KTRACE
+	if (KTRPOINT(td, KTR_STRUCT))
+		ktrstat_error(sbp, error);
+#endif
+	return (error);
+}
+
 static int
 linux_kern_statat(struct thread *td, int flag, int fd, const char *path,
     enum uio_seg pathseg, struct stat *sbp)
@@ -77,7 +105,7 @@ linux_kern_statat(struct thread *td, int flag, int fd, const char *path,
 	if ((error = namei(&nd)) != 0) {
 		if (error == ENOTDIR &&
 		    (nd.ni_resflags & NIRES_EMPTYPATH) != 0)
-			error = kern_fstat(td, fd, sbp);
+			error = linux_kern_fstat(td, fd, sbp);
 		return (error);
 	}
 	error = VOP_STAT(nd.ni_vp, sbp, td->td_ucred, NOCRED);
@@ -111,45 +139,6 @@ linux_kern_lstat(struct thread *td, const char *path, enum uio_seg pathseg,
 }
 #endif
 
-static void
-translate_fd_major_minor(struct thread *td, int fd, struct stat *buf)
-{
-	struct file *fp;
-	struct vnode *vp;
-	struct mount *mp;
-	int major, minor;
-
-	/*
-	 * No capability rights required here.
-	 */
-	if ((!S_ISCHR(buf->st_mode) && !S_ISBLK(buf->st_mode)) ||
-	    fget(td, fd, &cap_no_rights, &fp) != 0)
-		return;
-	vp = fp->f_vnode;
-	if (vp != NULL && vn_isdisk(vp)) {
-		buf->st_mode &= ~S_IFMT;
-		buf->st_mode |= S_IFBLK;
-	}
-	if (vp != NULL && rootdevmp != NULL) {
-		mp = vp->v_mount;
-		__compiler_membar();
-		if (mp != NULL && mp->mnt_vfc == rootdevmp->mnt_vfc)
-			buf->st_dev = rootdevmp->mnt_stat.f_fsid.val[0];
-	}
-	if (linux_vn_get_major_minor(vp, &major, &minor) == 0) {
-		buf->st_rdev = (major << 8 | minor);
-	} else if (fp->f_type == DTYPE_PTS) {
-		struct tty *tp = fp->f_data;
-
-		/* Convert the numbers for the slave device. */
-		if (linux_driver_get_major_minor(devtoname(tp->t_dev),
-					 &major, &minor) == 0) {
-			buf->st_rdev = (major << 8 | minor);
-		}
-	}
-	fdrop(fp, td);
-}
-
 /*
  * l_dev_t has the same encoding as dev_t in the latter's low 16 bits, so
  * truncation of a dev_t to 16 bits gives the same result as unpacking
@@ -242,8 +231,7 @@ linux_newfstat(struct thread *td, struct linux_newfstat_args *args)
 	struct stat buf;
 	int error;
 
-	error = kern_fstat(td, args->fd, &buf);
-	translate_fd_major_minor(td, args->fd, &buf);
+	error = linux_kern_fstat(td, args->fd, &buf);
 	if (!error)
 		error = newstat_copyout(&buf, args->buf);
 
@@ -634,8 +622,7 @@ linux_fstat64(struct thread *td, struct linux_fstat64_args *args)
 	struct stat buf;
 	int error;
 
-	error = kern_fstat(td, args->fd, &buf);
-	translate_fd_major_minor(td, args->fd, &buf);
+	error = linux_kern_fstat(td, args->fd, &buf);
 	if (!error)
 		error = stat64_copyout(&buf, args->statbuf);