aboutsummaryrefslogtreecommitdiff
path: root/stand
diff options
context:
space:
mode:
authorToomas Soome <tsoome@FreeBSD.org>2020-05-09 06:25:20 +0000
committerToomas Soome <tsoome@FreeBSD.org>2020-05-09 06:25:20 +0000
commit4a2d7cee02aedce2f5ba34fd596f7bd74f95a1a3 (patch)
tree79999ae09942c14f068ed034dc8074fb97ec5bba /stand
parent70868d48e8f364c0d0620d2314c9a431699fa538 (diff)
downloadsrc-4a2d7cee02aedce2f5ba34fd596f7bd74f95a1a3.tar.gz
src-4a2d7cee02aedce2f5ba34fd596f7bd74f95a1a3.zip
loader: vdev_read() can corrupt memory
When reading less than sector size but from sector boundary, the vdev_read() will read full sector into the provided buffer and therefore corrupting memory past buffer end. MFC after: 2 days
Notes
Notes: svn path=/head/; revision=360836
Diffstat (limited to 'stand')
-rw-r--r--stand/libsa/zfs/zfs.c28
1 files changed, 21 insertions, 7 deletions
diff --git a/stand/libsa/zfs/zfs.c b/stand/libsa/zfs/zfs.c
index 5f15f7b91632..d94072d80628 100644
--- a/stand/libsa/zfs/zfs.c
+++ b/stand/libsa/zfs/zfs.c
@@ -418,7 +418,7 @@ vdev_read(vdev_t *vdev, void *priv, off_t offset, void *buf, size_t bytes)
full_sec_size -= secsz;
/* Return of partial sector data requires a bounce buffer. */
- if ((head > 0) || do_tail_read) {
+ if ((head > 0) || do_tail_read || bytes < secsz) {
bouncebuf = malloc(secsz);
if (bouncebuf == NULL) {
printf("vdev_read: out of memory\n");
@@ -442,14 +442,28 @@ vdev_read(vdev_t *vdev, void *priv, off_t offset, void *buf, size_t bytes)
outbuf += min(secsz - head, bytes);
}
- /* Full data return from read sectors */
+ /*
+ * Full data return from read sectors.
+ * Note, there is still corner case where we read
+ * from sector boundary, but less than sector size, e.g. reading 512B
+ * from 4k sector.
+ */
if (full_sec_size > 0) {
- res = read(fd, outbuf, full_sec_size);
- if (res != full_sec_size) {
- ret = EIO;
- goto error;
+ if (bytes < full_sec_size) {
+ res = read(fd, bouncebuf, secsz);
+ if (res != secsz) {
+ ret = EIO;
+ goto error;
+ }
+ memcpy(outbuf, bouncebuf, bytes);
+ } else {
+ res = read(fd, outbuf, full_sec_size);
+ if (res != full_sec_size) {
+ ret = EIO;
+ goto error;
+ }
+ outbuf += full_sec_size;
}
- outbuf += full_sec_size;
}
/* Partial data return from last sector */