git: fae4b97c1a6b - main - loader: reading from large dosfs fails

From: Toomas Soome <tsoome_at_FreeBSD.org>
Date: Thu, 10 Oct 2024 08:40:42 UTC
The branch main has been updated by tsoome:

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

commit fae4b97c1a6b762230824ac0ba169dff13fa006c
Author:     Toomas Soome <tsoome@FreeBSD.org>
AuthorDate: 2024-07-17 06:47:56 +0000
Commit:     Toomas Soome <tsoome@FreeBSD.org>
CommitDate: 2024-10-10 08:40:25 +0000

    loader: reading from large dosfs fails
    
    With 8GB disk image and FAT32, our read offset calculation wraps over
    32-bit integer and we end up reading garbage. The problem appears when
    disk image is filled with data and the block to bytes translations do
    not fit into 32-bit integers.
    
    illumos issue: https://www.illumos.org/issues/16666
    
    Sponsored by:   MNX Cloud, Inc.
    MFC after:      1 week
---
 stand/libsa/dosfs.c | 26 +++++++++++++++++---------
 1 file changed, 17 insertions(+), 9 deletions(-)

diff --git a/stand/libsa/dosfs.c b/stand/libsa/dosfs.c
index 2ee95bf2c601..123350263d09 100644
--- a/stand/libsa/dosfs.c
+++ b/stand/libsa/dosfs.c
@@ -1,6 +1,7 @@
 /*
  * Copyright (c) 1996, 1998 Robert Nordier
  * All rights reserved.
+ * Copyright 2024 MNX Cloud, Inc.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -139,8 +140,8 @@ static DOS_DE dot[2] = {
 #define okclus(fs, c)  ((c) >= LOCLUS && (c) <= (fs)->xclus)
 
 /* Get start cluster from directory entry */
-#define stclus(sz, de)  ((sz) != 32 ? cv2((de)->clus) :          \
-                         ((u_int)cv2((de)->dex.h_clus) << 16) |  \
+#define stclus(sz, de)  ((sz) != 32 ? (u_int)cv2((de)->clus) :	\
+                         ((u_int)cv2((de)->dex.h_clus) << 16) |	\
 			 cv2((de)->clus))
 
 static int parsebs(DOS_FS *, DOS_BS *);
@@ -152,7 +153,7 @@ static off_t fsize(DOS_FS *, DOS_DE *);
 static int fatcnt(DOS_FS *, u_int);
 static int fatget(DOS_FS *, u_int *);
 static int fatend(u_int, u_int);
-static int ioread(DOS_FS *, u_int, void *, size_t);
+static int ioread(DOS_FS *, uint64_t, void *, size_t);
 static int ioget(struct open_file *, daddr_t, void *, size_t);
 
 static int
@@ -364,7 +365,9 @@ static int
 dos_read(struct open_file *fd, void *buf, size_t nbyte, size_t *resid)
 {
 	off_t size;
-	u_int nb, off, clus, c, cnt, n;
+	uint64_t off;
+	size_t nb;
+	u_int clus, c, cnt, n;
 	DOS_FILE *f = (DOS_FILE *)fd->f_fsdata;
 	int err = 0;
 
@@ -375,7 +378,7 @@ dos_read(struct open_file *fd, void *buf, size_t nbyte, size_t *resid)
 	 * 4-5 sec.
 	 */
 	twiddle(4);
-	nb = (u_int)nbyte;
+	nb = nbyte;
 	if ((size = fsize(f->fs, &f->de)) == -1)
 		return (EINVAL);
 	if (nb > (n = size - f->offset))
@@ -402,8 +405,12 @@ dos_read(struct open_file *fd, void *buf, size_t nbyte, size_t *resid)
 		}
 		if (!clus || (n = f->fs->bsize - off) > cnt)
 			n = cnt;
-		if ((err = ioread(f->fs, (c ? blkoff(f->fs, c) :
-		    secbyt(f->fs->lsndir)) + off, buf, n)))
+		if (c != 0)
+			off += blkoff(f->fs, (uint64_t)c);
+		else
+			off += secbyt(f->fs->lsndir);
+		err = ioread(f->fs, off, buf, n);
+		if (err != 0)
 			goto out;
 		f->offset += n;
 		f->c = c;
@@ -908,11 +915,12 @@ fatend(u_int sz, u_int c)
  * Offset-based I/O primitive
  */
 static int
-ioread(DOS_FS *fs, u_int offset, void *buf, size_t nbyte)
+ioread(DOS_FS *fs, uint64_t offset, void *buf, size_t nbyte)
 {
 	char *s;
-	u_int off, n;
+	size_t n;
 	int err;
+	uint64_t off;
 	u_char local_buf[SECSIZ];
 
 	s = buf;