Author: tsoome
Date: Sat May  9 06:25:20 2020
New Revision: 360836
URL: https://svnweb.freebsd.org/changeset/base/360836

Log:
  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

Modified:
  head/stand/libsa/zfs/zfs.c

Modified: head/stand/libsa/zfs/zfs.c
==============================================================================
--- head/stand/libsa/zfs/zfs.c  Sat May  9 05:04:02 2020        (r360835)
+++ head/stand/libsa/zfs/zfs.c  Sat May  9 06:25:20 2020        (r360836)
@@ -418,7 +418,7 @@ vdev_read(vdev_t *vdev, void *priv, off_t offset, void
                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
                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 */
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to