diff options
author | Toomas Soome <tsoome@FreeBSD.org> | 2020-05-09 06:25:20 +0000 |
---|---|---|
committer | Toomas Soome <tsoome@FreeBSD.org> | 2020-05-09 06:25:20 +0000 |
commit | 4a2d7cee02aedce2f5ba34fd596f7bd74f95a1a3 (patch) | |
tree | 79999ae09942c14f068ed034dc8074fb97ec5bba /stand/libsa | |
parent | 70868d48e8f364c0d0620d2314c9a431699fa538 (diff) | |
download | src-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/libsa')
-rw-r--r-- | stand/libsa/zfs/zfs.c | 28 |
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 */ |