Skip to content

Commit

Permalink
11935 loader: fix memory corruption bug in vdev_read
Browse files Browse the repository at this point in the history
Reviewed by: Matthias Scheler <matthias.scheler@wdc.com>
Reviewed by: Andy Fiddaman <omnios@citrus-it.co.uk>
Approved by: Dan McDonald <danmcd@joyent.com>
  • Loading branch information
tsoome committed Nov 14, 2019
1 parent f67d64d commit cb09bd3
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 31 deletions.
2 changes: 1 addition & 1 deletion usr/src/boot/Makefile.version
Expand Up @@ -33,4 +33,4 @@ LOADER_VERSION = 1.1
# Use date like formatting here, YYYY.MM.DD.XX, without leading zeroes.
# The version is processed from left to right, the version number can only
# be increased.
BOOT_VERSION = $(LOADER_VERSION)-2019.11.05.1
BOOT_VERSION = $(LOADER_VERSION)-2019.11.06.1
110 changes: 80 additions & 30 deletions usr/src/boot/lib/libstand/zfs/zfs.c
Expand Up @@ -347,57 +347,107 @@ vdev_read(vdev_t *vdev __unused, void *priv, off_t offset, void *buf,
size_t bytes)
{
int fd, ret;
size_t res, size, remainder, rb_size, blksz;
unsigned secsz;
off_t off;
char *bouncebuf, *rb_buf;
size_t res, head, tail, total_size, full_sec_size;
unsigned secsz, do_tail_read;
off_t start_sec;
char *outbuf, *bouncebuf;

fd = (uintptr_t)priv;
outbuf = (char *)buf;
bouncebuf = NULL;

ret = ioctl(fd, DIOCGSECTORSIZE, &secsz);
if (ret != 0)
return (ret);

off = offset / secsz;
remainder = offset % secsz;
if (lseek(fd, off * secsz, SEEK_SET) == -1)
return (errno);

rb_buf = buf;
rb_size = bytes;
size = roundup2(bytes + remainder, secsz);
blksz = size;
if (remainder != 0 || size != bytes) {
bouncebuf = zfs_alloc(secsz);
/*
* Handling reads of arbitrary offset and size - multi-sector case
* and single-sector case.
*
* Multi-sector Case
* (do_tail_read = true if tail > 0)
*
* |<----------------------total_size--------------------->|
* | |
* |<--head-->|<--------------bytes------------>|<--tail-->|
* | | | |
* | | |<~full_sec_size~>| | |
* +------------------+ +------------------+
* | |0101010| . . . |0101011| |
* +------------------+ +------------------+
* start_sec start_sec + n
*
*
* Single-sector Case
* (do_tail_read = false)
*
* |<------total_size = secsz----->|
* | |
* |<-head->|<---bytes--->|<-tail->|
* +-------------------------------+
* | |0101010101010| |
* +-------------------------------+
* start_sec
*/
start_sec = offset / secsz;
head = offset % secsz;
total_size = roundup2(head + bytes, secsz);
tail = total_size - (head + bytes);
do_tail_read = ((tail > 0) && (head + bytes > secsz));
full_sec_size = total_size;
if (head > 0)
full_sec_size -= secsz;
if (do_tail_read)
full_sec_size -= secsz;

/* Return of partial sector data requires a bounce buffer. */
if ((head > 0) || do_tail_read) {
bouncebuf = malloc(secsz);
if (bouncebuf == NULL) {
printf("vdev_read: out of memory\n");
return (ENOMEM);
}
rb_buf = bouncebuf;
blksz = rb_size - remainder;
}

while (bytes > 0) {
res = read(fd, rb_buf, rb_size);
if (res != rb_size) {
if (lseek(fd, start_sec * secsz, SEEK_SET) == -1) {
ret = errno;
goto error;
}

/* Partial data return from first sector */
if (head > 0) {
res = read(fd, bouncebuf, secsz);
if (res != secsz) {
ret = EIO;
goto error;
}
memcpy(outbuf, bouncebuf + head, min(secsz - head, bytes));
outbuf += min(secsz - head, bytes);
}

/* Full data return from read sectors */
if (full_sec_size > 0) {
res = read(fd, outbuf, full_sec_size);
if (res != full_sec_size) {
ret = EIO;
goto error;
}
outbuf += full_sec_size;
}

/* Partial data return from last sector */
if (do_tail_read) {
res = read(fd, bouncebuf, secsz);
if (res != secsz) {
ret = EIO;
goto error;
}
if (bytes < blksz)
blksz = bytes;
if (bouncebuf != NULL)
memcpy(buf, rb_buf + remainder, blksz);
buf = (void *)((uintptr_t)buf + blksz);
bytes -= blksz;
remainder = 0;
blksz = rb_size;
memcpy(outbuf, bouncebuf, secsz - tail);
}

ret = 0;
error:
if (bouncebuf != NULL)
zfs_free(bouncebuf, secsz);
free(bouncebuf);
return (ret);
}

Expand Down

0 comments on commit cb09bd3

Please sign in to comment.