git: 4cafd1021a5b - stable/13 - udf: Improve input validation.

From: Dag-Erling Smørgrav <des_at_FreeBSD.org>
Date: Thu, 24 Jul 2025 13:02:40 UTC
The branch stable/13 has been updated by des:

URL: https://cgit.FreeBSD.org/src/commit/?id=4cafd1021a5b6002812f4446794541eab8c08534

commit 4cafd1021a5b6002812f4446794541eab8c08534
Author:     Dag-Erling Smørgrav <des@FreeBSD.org>
AuthorDate: 2025-07-16 19:33:24 +0000
Commit:     Dag-Erling Smørgrav <des@FreeBSD.org>
CommitDate: 2025-07-24 13:02:12 +0000

    udf: Improve input validation.
    
    The existing code frequently assigns unsigned 64-bit values to variables
    that are signed and / or shorter without checking for overflow.  Try to
    deal with these cases.
    
    While here, fix two structs that used single-element arrays in place of
    flexible array members.
    
    PR:             287896
    MFC after:      1 week
    Reviewed by:    imp
    Differential Revision:  https://reviews.freebsd.org/D51339
    
    (cherry picked from commit 55f80afa17e8926f69660f19631194bcf7fa66f4)
---
 sys/fs/udf/ecma167-udf.h |  4 ++--
 sys/fs/udf/udf_vfsops.c  |  7 ++++++-
 sys/fs/udf/udf_vnops.c   | 48 ++++++++++++++++++++++++++++++++++++------------
 3 files changed, 44 insertions(+), 15 deletions(-)

diff --git a/sys/fs/udf/ecma167-udf.h b/sys/fs/udf/ecma167-udf.h
index 839bbec08254..19e114763cac 100644
--- a/sys/fs/udf/ecma167-udf.h
+++ b/sys/fs/udf/ecma167-udf.h
@@ -243,7 +243,7 @@ struct part_map_spare {
 	uint8_t			n_st;	/* Number of Sparing Tables */
 	uint8_t			reserved1;
 	uint32_t		st_size;
-	uint32_t		st_loc[1];
+	uint32_t		st_loc[];
 } __packed;
 
 union udf_pmap {
@@ -266,7 +266,7 @@ struct udf_sparing_table {
 	uint16_t		rt_l;	/* Relocation Table len */
 	uint8_t			reserved[2];
 	uint32_t		seq_num;
-	struct spare_map_entry	entries[1];
+	struct spare_map_entry	entries[];
 } __packed;
 
 /* Partition Descriptor [3/10.5] */
diff --git a/sys/fs/udf/udf_vfsops.c b/sys/fs/udf/udf_vfsops.c
index 4c9208642dea..cebc136b418a 100644
--- a/sys/fs/udf/udf_vfsops.c
+++ b/sys/fs/udf/udf_vfsops.c
@@ -81,6 +81,7 @@
 #include <sys/fcntl.h>
 #include <sys/iconv.h>
 #include <sys/kernel.h>
+#include <sys/limits.h>
 #include <sys/malloc.h>
 #include <sys/mount.h>
 #include <sys/namei.h>
@@ -739,7 +740,7 @@ udf_fhtovp(struct mount *mp, struct fid *fhp, int flags, struct vnode **vpp)
 	struct ifid *ifhp;
 	struct vnode *nvp;
 	struct udf_node *np;
-	off_t fsize;
+	uint64_t fsize;
 	int error;
 
 	ifhp = (struct ifid *)fhp;
@@ -751,6 +752,10 @@ udf_fhtovp(struct mount *mp, struct fid *fhp, int flags, struct vnode **vpp)
 
 	np = VTON(nvp);
 	fsize = le64toh(np->fentry->inf_len);
+	if (fsize > OFF_MAX) {
+		*vpp = NULLVP;
+		return (EIO);
+	}
 
 	*vpp = nvp;
 	vnode_create_vobject(*vpp, fsize, curthread);
diff --git a/sys/fs/udf/udf_vnops.c b/sys/fs/udf/udf_vnops.c
index d85bb9970913..8051b1e24d22 100644
--- a/sys/fs/udf/udf_vnops.c
+++ b/sys/fs/udf/udf_vnops.c
@@ -39,6 +39,7 @@
 #include <sys/conf.h>
 #include <sys/buf.h>
 #include <sys/iconv.h>
+#include <sys/limits.h>
 #include <sys/mount.h>
 #include <sys/vnode.h>
 #include <sys/dirent.h>
@@ -182,11 +183,14 @@ udf_access(struct vop_access_args *a)
 }
 
 static int
-udf_open(struct vop_open_args *ap) {
+udf_open(struct vop_open_args *ap)
+{
 	struct udf_node *np = VTON(ap->a_vp);
-	off_t fsize;
+	uint64_t fsize;
 
 	fsize = le64toh(np->fentry->inf_len);
+	if (fsize > OFF_MAX)
+		return (EIO);
 	vnode_create_vobject(ap->a_vp, fsize, ap->a_td);
 	return 0;
 }
@@ -317,12 +321,13 @@ udf_getattr(struct vop_getattr_args *a)
 		 * that directories consume at least one logical block,
 		 * make it appear so.
 		 */
-		if (fentry->logblks_rec != 0) {
-			vap->va_size =
-			    le64toh(fentry->logblks_rec) * node->udfmp->bsize;
-		} else {
+		vap->va_size = le64toh(fentry->logblks_rec);
+		if (vap->va_size == 0)
 			vap->va_size = node->udfmp->bsize;
-		}
+		else if (vap->va_size > UINT64_MAX / node->udfmp->bsize)
+			vap->va_size = UINT64_MAX;
+		else
+			vap->va_size *= node->udfmp->bsize;
 	} else {
 		vap->va_size = le64toh(fentry->inf_len);
 	}
@@ -449,6 +454,7 @@ udf_read(struct vop_read_args *ap)
 	struct buf *bp;
 	uint8_t *data;
 	daddr_t lbn, rablock;
+	uint64_t len;
 	off_t diff, fsize;
 	ssize_t n;
 	int error = 0;
@@ -474,7 +480,12 @@ udf_read(struct vop_read_args *ap)
 		return (error);
 	}
 
-	fsize = le64toh(node->fentry->inf_len);
+	len = le64toh(node->fentry->inf_len);
+	if (len > OFF_MAX) {
+		/* too big, just cap to the requested length */
+		len = uio->uio_resid;
+	}
+	fsize = len;
 	udfmp = node->udfmp;
 	do {
 		lbn = lblkno(udfmp, uio->uio_offset);
@@ -786,6 +797,7 @@ udf_readdir(struct vop_readdir_args *a)
 	struct udf_uiodir uiodir;
 	struct udf_dirstream *ds;
 	u_long *cookies = NULL;
+	uint64_t len;
 	int ncookies;
 	int error = 0;
 
@@ -815,8 +827,12 @@ udf_readdir(struct vop_readdir_args *a)
 	 * Iterate through the file id descriptors.  Give the parent dir
 	 * entry special attention.
 	 */
-	ds = udf_opendir(node, uio->uio_offset, le64toh(node->fentry->inf_len),
-	    node->udfmp);
+	len = le64toh(node->fentry->inf_len);
+	if (len > INT_MAX) {
+		/* too big, just cap to INT_MAX */
+		len = INT_MAX;
+	}
+	ds = udf_opendir(node, uio->uio_offset, len, node->udfmp);
 
 	while ((fid = udf_getfid(ds)) != NULL) {
 		/* XXX Should we return an error on a bad fid? */
@@ -908,7 +924,8 @@ udf_readlink(struct vop_readlink_args *ap)
 	struct udf_node *node;
 	void *buf;
 	char *cp;
-	int error, len, root;
+	uint64_t len;
+	int error, root;
 
 	/*
 	 * A symbolic link in UDF is a list of variable-length path
@@ -918,6 +935,8 @@ udf_readlink(struct vop_readlink_args *ap)
 	vp = ap->a_vp;
 	node = VTON(vp);
 	len = le64toh(node->fentry->inf_len);
+	if (len > MAXPATHLEN)
+		return (EIO);
 	buf = malloc(len, M_DEVBUF, M_WAITOK);
 	iov[0].iov_len = len;
 	iov[0].iov_base = buf;
@@ -1120,13 +1139,14 @@ udf_lookup(struct vop_cachedlookup_args *a)
 	struct udf_mnt *udfmp;
 	struct fileid_desc *fid = NULL;
 	struct udf_dirstream *ds;
+	uint64_t fsize;
 	u_long nameiop;
 	u_long flags;
 	char *nameptr;
 	long namelen;
 	ino_t id = 0;
 	int offset, error = 0;
-	int fsize, lkflags, ltype, numdirpasses;
+	int lkflags, ltype, numdirpasses;
 
 	dvp = a->a_dvp;
 	node = VTON(dvp);
@@ -1137,6 +1157,10 @@ udf_lookup(struct vop_cachedlookup_args *a)
 	nameptr = a->a_cnp->cn_nameptr;
 	namelen = a->a_cnp->cn_namelen;
 	fsize = le64toh(node->fentry->inf_len);
+	if (fsize > INT_MAX) {
+		/* too big, just cap to INT_MAX */
+		fsize = INT_MAX;
+	}
 
 	/*
 	 * If this is a LOOKUP and we've already partially searched through