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
This commit is contained in:
Toomas Soome 2024-07-17 09:47:56 +03:00
parent 2e9761eb80
commit fae4b97c1a

View File

@ -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;