Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 23 Sep 2020 06:52:23 +0000 (UTC)
From:      Xin LI <delphij@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r366064 - head/sbin/fsck_msdosfs
Message-ID:  <202009230652.08N6qNsa070062@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: delphij
Date: Wed Sep 23 06:52:22 2020
New Revision: 366064
URL: https://svnweb.freebsd.org/changeset/base/366064

Log:
  sbin/fsck_msdosfs: Fix an integer overflow on 32-bit platforms.
  
  The purpose of checksize() is to verify that the referenced cluster
  chain size matches the recorded file size (up to 2^32 - 1) in the
  directory entry. We follow the cluster chain, then multiple the
  cluster count by bytes per cluster to get the physical size, then
  check it against the recorded size.
  
  When a file is close to 4 GiB (between 4GiB - cluster size and 4GiB,
  both non-inclusive), the product of cluster count and bytes per
  cluster would be exactly 4 GiB. On 32-bit systems, because size_t
  is 32-bit, this would wrap back to 0, which will cause the file be
  truncated to 0.
  
  Fix this by using 64-bit physicalSize instead.
  
  This fix is inspired by an Android change request at
  https://android-review.googlesource.com/c/platform/external/fsck_msdos/+/1428461
  
  PR:		249533
  Reviewed by:	kevlo
  MFC after:	3 days
  Differential Revision:	https://reviews.freebsd.org/D26524

Modified:
  head/sbin/fsck_msdosfs/dir.c

Modified: head/sbin/fsck_msdosfs/dir.c
==============================================================================
--- head/sbin/fsck_msdosfs/dir.c	Wed Sep 23 04:09:02 2020	(r366063)
+++ head/sbin/fsck_msdosfs/dir.c	Wed Sep 23 06:52:22 2020	(r366064)
@@ -388,7 +388,8 @@ static int
 checksize(struct fat_descriptor *fat, u_char *p, struct dosDirEntry *dir)
 {
 	int ret = FSOK;
-	size_t physicalSize;
+	size_t chainsize;
+	u_int64_t physicalSize;
 	struct bootblock *boot;
 
 	boot = fat_get_boot(fat);
@@ -401,9 +402,9 @@ checksize(struct fat_descriptor *fat, u_char *p, struc
 	} else {
 		if (!fat_is_valid_cl(fat, dir->head))
 			return FSERROR;
-		ret = checkchain(fat, dir->head, &physicalSize);
+		ret = checkchain(fat, dir->head, &chainsize);
 		/*
-		 * Upon return, physicalSize would hold the chain length
+		 * Upon return, chainsize would hold the chain length
 		 * that checkchain() was able to validate, but if the user
 		 * refused the proposed repair, it would be unsafe to
 		 * proceed with directory entry fix, so bail out in that
@@ -412,7 +413,13 @@ checksize(struct fat_descriptor *fat, u_char *p, struc
 		if (ret == FSERROR) {
 			return (FSERROR);
 		}
-		physicalSize *= boot->ClusterSize;
+		/*
+		 * The maximum file size on FAT32 is 4GiB - 1, which
+		 * will occupy a cluster chain of exactly 4GiB in
+		 * size.  On 32-bit platforms, since size_t is 32-bit,
+		 * it would wrap back to 0.
+		 */
+		physicalSize = (u_int64_t)chainsize * boot->ClusterSize;
 	}
 	if (physicalSize < dir->size) {
 		pwarn("size of %s is %u, should at most be %zu\n",



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202009230652.08N6qNsa070062>