git: 2b9d052d3eeb - main - freebsd32: fix getfsstat sign extension bugs

From: Brooks Davis <brooks_at_FreeBSD.org>
Date: Wed, 17 Nov 2021 20:22:45 UTC
The branch main has been updated by brooks:

URL: https://cgit.FreeBSD.org/src/commit/?id=2b9d052d3eeb9ffd1094dc5165eff555b9fd6bc3

commit 2b9d052d3eeb9ffd1094dc5165eff555b9fd6bc3
Author:     Brooks Davis <brooks@FreeBSD.org>
AuthorDate: 2021-11-17 20:12:26 +0000
Commit:     Brooks Davis <brooks@FreeBSD.org>
CommitDate: 2021-11-17 20:12:26 +0000

    freebsd32: fix getfsstat sign extension bugs
    
    Add freebsd32 versions of getfsstat and freebsd11_getfsstat so that
    bufsize is properly sign-extended if a negative value is passed.
    Reject negative values before passing to kern_getfsstat as a size_t.
    
    Reviewed by:    kevans
---
 sys/compat/freebsd32/freebsd32_misc.c          | 25 +++++++++++++++++++++++++
 sys/compat/freebsd32/freebsd32_proto.h         | 14 ++++++++++++++
 sys/compat/freebsd32/freebsd32_syscall.h       |  4 ++--
 sys/compat/freebsd32/freebsd32_syscalls.c      |  4 ++--
 sys/compat/freebsd32/freebsd32_sysent.c        |  4 ++--
 sys/compat/freebsd32/freebsd32_systrace_args.c |  8 ++++----
 sys/compat/freebsd32/syscalls.master           |  4 ++--
 sys/kern/vfs_syscalls.c                        | 19 ++++++++++++++-----
 sys/sys/syscallsubr.h                          |  2 ++
 9 files changed, 67 insertions(+), 17 deletions(-)

diff --git a/sys/compat/freebsd32/freebsd32_misc.c b/sys/compat/freebsd32/freebsd32_misc.c
index a165b47b2928..5bff0cea845e 100644
--- a/sys/compat/freebsd32/freebsd32_misc.c
+++ b/sys/compat/freebsd32/freebsd32_misc.c
@@ -292,6 +292,21 @@ copy_statfs(struct statfs *in, struct ostatfs32 *out)
 }
 #endif
 
+int
+freebsd32_getfsstat(struct thread *td, struct freebsd32_getfsstat_args *uap)
+{
+	size_t count;
+	int error;
+
+	if (uap->bufsize < 0 || uap->bufsize > SIZE_MAX)
+		return (EINVAL);
+	error = kern_getfsstat(td, &uap->buf, uap->bufsize, &count,
+	    UIO_USERSPACE, uap->mode);
+	if (error == 0)
+		td->td_retval[0] = count;
+	return (error);
+}
+
 #ifdef COMPAT_FREEBSD4
 int
 freebsd4_freebsd32_getfsstat(struct thread *td,
@@ -323,6 +338,16 @@ freebsd4_freebsd32_getfsstat(struct thread *td,
 }
 #endif
 
+#ifdef COMPAT_FREEBSD11
+int
+freebsd11_freebsd32_getfsstat(struct thread *td,
+    struct freebsd11_freebsd32_getfsstat_args *uap)
+{
+	return(kern_freebsd11_getfsstat(td, uap->buf, uap->bufsize,
+	    uap->mode));
+}
+#endif
+
 int
 freebsd32_sigaltstack(struct thread *td,
 		      struct freebsd32_sigaltstack_args *uap)
diff --git a/sys/compat/freebsd32/freebsd32_proto.h b/sys/compat/freebsd32/freebsd32_proto.h
index ae3abe22dda4..50733c35d51b 100644
--- a/sys/compat/freebsd32/freebsd32_proto.h
+++ b/sys/compat/freebsd32/freebsd32_proto.h
@@ -709,6 +709,11 @@ struct freebsd32_fhstat_args {
 	char u_fhp_l_[PADL_(const struct fhandle *)]; const struct fhandle * u_fhp; char u_fhp_r_[PADR_(const struct fhandle *)];
 	char sb_l_[PADL_(struct stat32 *)]; struct stat32 * sb; char sb_r_[PADR_(struct stat32 *)];
 };
+struct freebsd32_getfsstat_args {
+	char buf_l_[PADL_(struct statfs *)]; struct statfs * buf; char buf_r_[PADR_(struct statfs *)];
+	char bufsize_l_[PADL_(int32_t)]; int32_t bufsize; char bufsize_r_[PADR_(int32_t)];
+	char mode_l_[PADL_(int)]; int mode; char mode_r_[PADR_(int)];
+};
 #ifdef PAD64_REQUIRED
 struct freebsd32_mknodat_args {
 	char fd_l_[PADL_(int)]; int fd; char fd_r_[PADR_(int)];
@@ -912,6 +917,7 @@ int	freebsd32_utimensat(struct thread *, struct freebsd32_utimensat_args *);
 int	freebsd32_fstat(struct thread *, struct freebsd32_fstat_args *);
 int	freebsd32_fstatat(struct thread *, struct freebsd32_fstatat_args *);
 int	freebsd32_fhstat(struct thread *, struct freebsd32_fhstat_args *);
+int	freebsd32_getfsstat(struct thread *, struct freebsd32_getfsstat_args *);
 #ifdef PAD64_REQUIRED
 int	freebsd32_mknodat(struct thread *, struct freebsd32_mknodat_args *);
 #else
@@ -1317,6 +1323,11 @@ struct freebsd11_freebsd32_kevent_args {
 	char nevents_l_[PADL_(int)]; int nevents; char nevents_r_[PADR_(int)];
 	char timeout_l_[PADL_(const struct timespec32 *)]; const struct timespec32 * timeout; char timeout_r_[PADR_(const struct timespec32 *)];
 };
+struct freebsd11_freebsd32_getfsstat_args {
+	char buf_l_[PADL_(struct freebsd11_statfs *)]; struct freebsd11_statfs * buf; char buf_r_[PADR_(struct freebsd11_statfs *)];
+	char bufsize_l_[PADL_(int32_t)]; int32_t bufsize; char bufsize_r_[PADR_(int32_t)];
+	char mode_l_[PADL_(int)]; int mode; char mode_r_[PADR_(int)];
+};
 #ifdef PAD64_REQUIRED
 #else
 #endif
@@ -1344,6 +1355,7 @@ int	freebsd11_freebsd32_lstat(struct thread *, struct freebsd11_freebsd32_lstat_
 int	freebsd11_freebsd32_getdirentries(struct thread *, struct freebsd11_freebsd32_getdirentries_args *);
 int	freebsd11_freebsd32_fhstat(struct thread *, struct freebsd11_freebsd32_fhstat_args *);
 int	freebsd11_freebsd32_kevent(struct thread *, struct freebsd11_freebsd32_kevent_args *);
+int	freebsd11_freebsd32_getfsstat(struct thread *, struct freebsd11_freebsd32_getfsstat_args *);
 int	freebsd11_freebsd32_fstatat(struct thread *, struct freebsd11_freebsd32_fstatat_args *);
 
 #endif /* COMPAT_FREEBSD11 */
@@ -1474,6 +1486,7 @@ int	freebsd11_freebsd32_fstatat(struct thread *, struct freebsd11_freebsd32_fsta
 #define	FREEBSD32_SYS_AUE_freebsd11_freebsd32_kevent	AUE_KEVENT
 #define	FREEBSD32_SYS_AUE_freebsd32_nmount	AUE_NMOUNT
 #define	FREEBSD32_SYS_AUE_freebsd32_sendfile	AUE_SENDFILE
+#define	FREEBSD32_SYS_AUE_freebsd11_freebsd32_getfsstat	AUE_GETFSSTAT
 #define	FREEBSD32_SYS_AUE_freebsd32_ksem_init	AUE_SEMINIT
 #define	FREEBSD32_SYS_AUE_freebsd32_ksem_open	AUE_SEMOPEN
 #define	FREEBSD32_SYS_AUE_freebsd32_sigaction	AUE_SIGACTION
@@ -1538,6 +1551,7 @@ int	freebsd11_freebsd32_fstatat(struct thread *, struct freebsd11_freebsd32_fsta
 #define	FREEBSD32_SYS_AUE_freebsd32_fstat	AUE_FSTAT
 #define	FREEBSD32_SYS_AUE_freebsd32_fstatat	AUE_FSTATAT
 #define	FREEBSD32_SYS_AUE_freebsd32_fhstat	AUE_FHSTAT
+#define	FREEBSD32_SYS_AUE_freebsd32_getfsstat	AUE_GETFSSTAT
 #define	FREEBSD32_SYS_AUE_freebsd32_mknodat	AUE_MKNODAT
 #define	FREEBSD32_SYS_AUE_freebsd32_mknodat	AUE_MKNODAT
 #define	FREEBSD32_SYS_AUE_freebsd32_kevent	AUE_KEVENT
diff --git a/sys/compat/freebsd32/freebsd32_syscall.h b/sys/compat/freebsd32/freebsd32_syscall.h
index 05dfb299e332..4f4e8ac9caf6 100644
--- a/sys/compat/freebsd32/freebsd32_syscall.h
+++ b/sys/compat/freebsd32/freebsd32_syscall.h
@@ -329,7 +329,7 @@
 #define	FREEBSD32_SYS_lchflags	391
 #define	FREEBSD32_SYS_uuidgen	392
 #define	FREEBSD32_SYS_freebsd32_sendfile	393
-#define	FREEBSD32_SYS_freebsd11_getfsstat	395
+#define	FREEBSD32_SYS_freebsd11_freebsd32_getfsstat	395
 #define	FREEBSD32_SYS_freebsd11_statfs	396
 #define	FREEBSD32_SYS_freebsd11_fstatfs	397
 #define	FREEBSD32_SYS_freebsd11_fhstatfs	398
@@ -488,7 +488,7 @@
 #define	FREEBSD32_SYS_getdirentries	554
 #define	FREEBSD32_SYS_statfs	555
 #define	FREEBSD32_SYS_fstatfs	556
-#define	FREEBSD32_SYS_getfsstat	557
+#define	FREEBSD32_SYS_freebsd32_getfsstat	557
 #define	FREEBSD32_SYS_fhstatfs	558
 #define	FREEBSD32_SYS_freebsd32_mknodat	559
 #define	FREEBSD32_SYS_freebsd32_mknodat	559
diff --git a/sys/compat/freebsd32/freebsd32_syscalls.c b/sys/compat/freebsd32/freebsd32_syscalls.c
index 3206e069e85d..7af646c17e3e 100644
--- a/sys/compat/freebsd32/freebsd32_syscalls.c
+++ b/sys/compat/freebsd32/freebsd32_syscalls.c
@@ -409,7 +409,7 @@ const char *freebsd32_syscallnames[] = {
 	"uuidgen",			/* 392 = uuidgen */
 	"freebsd32_sendfile",			/* 393 = freebsd32_sendfile */
 	"#394",			/* 394 = mac_syscall */
-	"compat11.getfsstat",		/* 395 = freebsd11 getfsstat */
+	"compat11.freebsd32_getfsstat",		/* 395 = freebsd11 freebsd32_getfsstat */
 	"compat11.statfs",		/* 396 = freebsd11 statfs */
 	"compat11.fstatfs",		/* 397 = freebsd11 fstatfs */
 	"compat11.fhstatfs",		/* 398 = freebsd11 fhstatfs */
@@ -594,7 +594,7 @@ const char *freebsd32_syscallnames[] = {
 	"getdirentries",			/* 554 = getdirentries */
 	"statfs",			/* 555 = statfs */
 	"fstatfs",			/* 556 = fstatfs */
-	"getfsstat",			/* 557 = getfsstat */
+	"freebsd32_getfsstat",			/* 557 = freebsd32_getfsstat */
 	"fhstatfs",			/* 558 = fhstatfs */
 #ifdef PAD64_REQUIRED
 	"freebsd32_mknodat",			/* 559 = freebsd32_mknodat */
diff --git a/sys/compat/freebsd32/freebsd32_sysent.c b/sys/compat/freebsd32/freebsd32_sysent.c
index 12fecbee75ab..1960cddc0542 100644
--- a/sys/compat/freebsd32/freebsd32_sysent.c
+++ b/sys/compat/freebsd32/freebsd32_sysent.c
@@ -462,7 +462,7 @@ struct sysent freebsd32_sysent[] = {
 	{ .sy_narg = AS(uuidgen_args), .sy_call = (sy_call_t *)sys_uuidgen, .sy_auevent = AUE_NULL, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC },	/* 392 = uuidgen */
 	{ .sy_narg = AS(freebsd32_sendfile_args), .sy_call = (sy_call_t *)freebsd32_sendfile, .sy_auevent = AUE_SENDFILE, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC },	/* 393 = freebsd32_sendfile */
 	{ .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT },			/* 394 = mac_syscall */
-	{ compat11(AS(freebsd11_getfsstat_args),getfsstat), .sy_auevent = AUE_GETFSSTAT, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC },	/* 395 = freebsd11 getfsstat */
+	{ compat11(AS(freebsd11_freebsd32_getfsstat_args),freebsd32_getfsstat), .sy_auevent = AUE_GETFSSTAT, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC },	/* 395 = freebsd11 freebsd32_getfsstat */
 	{ compat11(AS(freebsd11_statfs_args),statfs), .sy_auevent = AUE_STATFS, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC },	/* 396 = freebsd11 statfs */
 	{ compat11(AS(freebsd11_fstatfs_args),fstatfs), .sy_auevent = AUE_FSTATFS, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC },	/* 397 = freebsd11 fstatfs */
 	{ compat11(AS(freebsd11_fhstatfs_args),fhstatfs), .sy_auevent = AUE_FHSTATFS, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC },	/* 398 = freebsd11 fhstatfs */
@@ -647,7 +647,7 @@ struct sysent freebsd32_sysent[] = {
 	{ .sy_narg = AS(getdirentries_args), .sy_call = (sy_call_t *)sys_getdirentries, .sy_auevent = AUE_GETDIRENTRIES, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC },	/* 554 = getdirentries */
 	{ .sy_narg = AS(statfs_args), .sy_call = (sy_call_t *)sys_statfs, .sy_auevent = AUE_STATFS, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC },	/* 555 = statfs */
 	{ .sy_narg = AS(fstatfs_args), .sy_call = (sy_call_t *)sys_fstatfs, .sy_auevent = AUE_FSTATFS, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC },	/* 556 = fstatfs */
-	{ .sy_narg = AS(getfsstat_args), .sy_call = (sy_call_t *)sys_getfsstat, .sy_auevent = AUE_GETFSSTAT, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC },	/* 557 = getfsstat */
+	{ .sy_narg = AS(freebsd32_getfsstat_args), .sy_call = (sy_call_t *)freebsd32_getfsstat, .sy_auevent = AUE_GETFSSTAT, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC },	/* 557 = freebsd32_getfsstat */
 	{ .sy_narg = AS(fhstatfs_args), .sy_call = (sy_call_t *)sys_fhstatfs, .sy_auevent = AUE_FHSTATFS, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC },	/* 558 = fhstatfs */
 #ifdef PAD64_REQUIRED
 	{ .sy_narg = AS(freebsd32_mknodat_args), .sy_call = (sy_call_t *)freebsd32_mknodat, .sy_auevent = AUE_MKNODAT, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC },	/* 559 = freebsd32_mknodat */
diff --git a/sys/compat/freebsd32/freebsd32_systrace_args.c b/sys/compat/freebsd32/freebsd32_systrace_args.c
index 6ba992bed5c9..6727bd8e5c76 100644
--- a/sys/compat/freebsd32/freebsd32_systrace_args.c
+++ b/sys/compat/freebsd32/freebsd32_systrace_args.c
@@ -3207,9 +3207,9 @@ systrace_args(int sysnum, void *params, uint64_t *uarg, int *n_args)
 		*n_args = 2;
 		break;
 	}
-	/* getfsstat */
+	/* freebsd32_getfsstat */
 	case 557: {
-		struct getfsstat_args *p = params;
+		struct freebsd32_getfsstat_args *p = params;
 		uarg[0] = (intptr_t)p->buf; /* struct statfs * */
 		iarg[1] = p->bufsize; /* int32_t */
 		iarg[2] = p->mode; /* int */
@@ -8858,7 +8858,7 @@ systrace_entry_setargdesc(int sysnum, int ndx, char *desc, size_t descsz)
 			break;
 		};
 		break;
-	/* getfsstat */
+	/* freebsd32_getfsstat */
 	case 557:
 		switch (ndx) {
 		case 0:
@@ -11149,7 +11149,7 @@ systrace_return_setargdesc(int sysnum, int ndx, char *desc, size_t descsz)
 		if (ndx == 0 || ndx == 1)
 			p = "int";
 		break;
-	/* getfsstat */
+	/* freebsd32_getfsstat */
 	case 557:
 		if (ndx == 0 || ndx == 1)
 			p = "int";
diff --git a/sys/compat/freebsd32/syscalls.master b/sys/compat/freebsd32/syscalls.master
index 2bcf84d3baf7..3734219fba30 100644
--- a/sys/compat/freebsd32/syscalls.master
+++ b/sys/compat/freebsd32/syscalls.master
@@ -746,7 +746,7 @@
 				    size_t nbytes, struct sf_hdtr32 *hdtr, \
 				    off_t *sbytes, int flags); }
 394	AUE_NULL	UNIMPL	mac_syscall
-395	AUE_GETFSSTAT	COMPAT11|NOPROTO	{ int getfsstat( \
+395	AUE_GETFSSTAT	COMPAT11	{ int freebsd32_getfsstat( \
 				    struct freebsd11_statfs *buf, \
 				    int32_t bufsize, int mode); }
 396	AUE_STATFS	COMPAT11|NOPROTO	{ int statfs(const char *path, \
@@ -1140,7 +1140,7 @@
 555	AUE_STATFS	NOPROTO	{ int statfs(const char *path, \
 				    struct statfs *buf); }
 556	AUE_FSTATFS	NOPROTO	{ int fstatfs(int fd, struct statfs *buf); }
-557	AUE_GETFSSTAT	NOPROTO	{ int getfsstat(struct statfs *buf, \
+557	AUE_GETFSSTAT	STD	{ int freebsd32_getfsstat(struct statfs *buf, \
 				    int32_t bufsize, int mode); }
 558	AUE_FHSTATFS	NOPROTO	{ int fhstatfs(const struct fhandle *u_fhp, \
 				    struct statfs *buf); }
diff --git a/sys/kern/vfs_syscalls.c b/sys/kern/vfs_syscalls.c
index 4357669786f8..1cffe903aef3 100644
--- a/sys/kern/vfs_syscalls.c
+++ b/sys/kern/vfs_syscalls.c
@@ -789,25 +789,34 @@ freebsd11_fstatfs(struct thread *td, struct freebsd11_fstatfs_args *uap)
  */
 int
 freebsd11_getfsstat(struct thread *td, struct freebsd11_getfsstat_args *uap)
+{
+	return (kern_freebsd11_getfsstat(td, uap->buf, uap->bufsize, uap->mode));
+}
+
+int
+kern_freebsd11_getfsstat(struct thread *td, struct freebsd11_statfs * ubuf,
+    long bufsize, int mode)
 {
 	struct freebsd11_statfs osb;
 	struct statfs *buf, *sp;
 	size_t count, size;
 	int error;
 
-	count = uap->bufsize / sizeof(struct ostatfs);
+	if (bufsize < 0)
+		return (EINVAL);
+
+	count = bufsize / sizeof(struct ostatfs);
 	size = count * sizeof(struct statfs);
-	error = kern_getfsstat(td, &buf, size, &count, UIO_SYSSPACE,
-	    uap->mode);
+	error = kern_getfsstat(td, &buf, size, &count, UIO_SYSSPACE, mode);
 	if (error == 0)
 		td->td_retval[0] = count;
 	if (size > 0) {
 		sp = buf;
 		while (count > 0 && error == 0) {
 			freebsd11_cvtstatfs(sp, &osb);
-			error = copyout(&osb, uap->buf, sizeof(osb));
+			error = copyout(&osb, ubuf, sizeof(osb));
 			sp++;
-			uap->buf++;
+			ubuf++;
 			count--;
 		}
 		free(buf, M_STATFS);
diff --git a/sys/sys/syscallsubr.h b/sys/sys/syscallsubr.h
index 5032df4be874..24d2a199a272 100644
--- a/sys/sys/syscallsubr.h
+++ b/sys/sys/syscallsubr.h
@@ -145,6 +145,8 @@ int	kern_fhopen(struct thread *td, const struct fhandle *u_fhp, int flags);
 int	kern_fhstat(struct thread *td, fhandle_t fh, struct stat *buf);
 int	kern_fhstatfs(struct thread *td, fhandle_t fh, struct statfs *buf);
 int	kern_fpathconf(struct thread *td, int fd, int name, long *valuep);
+int	kern_freebsd11_getfsstat(struct thread *td,
+	    struct freebsd11_statfs *ubuf, long bufsize, int mode);
 int	kern_fstat(struct thread *td, int fd, struct stat *sbp);
 int	kern_fstatfs(struct thread *td, int fd, struct statfs *buf);
 int	kern_fsync(struct thread *td, int fd, bool fullsync);