diff options
author | Xin Li <delphij@google.com> | 2020-09-23 00:34:32 -0700 |
---|---|---|
committer | Xin Li <delphij@google.com> | 2020-09-23 00:34:32 -0700 |
commit | 482403793438098b8e613ebb24bb6ae1046aeef3 (patch) | |
tree | 2f42e0a6005555f8268146a70e056467dafea60c | |
parent | 063c1723a6c2c0259fde18efa969cbbd62dcc73b (diff) | |
parent | 62a280b2c2489b2fa99a54e806d987bd0bf0039a (diff) | |
download | fsck_msdos-482403793438098b8e613ebb24bb6ae1046aeef3.tar.gz |
Sync with upstream 667eef4ac (svn revision 366064 + 366065).
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.
Bug: 167345711
Change-Id: Ica05e89fdbf3c9078a0939be340a045e00460eb0
-rw-r--r-- | dir.c | 17 |
1 files changed, 12 insertions, 5 deletions
@@ -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, struct dosDirEntry *dir) } 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,10 +413,16 @@ checksize(struct fat_descriptor *fat, u_char *p, struct dosDirEntry *dir) 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", + pwarn("size of %s is %u, should at most be %" PRIu64 "\n", fullpath(dir), dir->size, physicalSize); if (ask(1, "Truncate")) { dir->size = physicalSize; |