From e06be586410cdad14d2dce76af4f2d43eebe7c83 Mon Sep 17 00:00:00 2001 From: Ned Bass Date: Wed, 10 Nov 2010 13:36:18 -0800 Subject: [PATCH] Fix for access beyond end of device error This commit fixes a sign extension bug affecting l2arc devices. Extremely large offsets may be passed down to the low level block device driver on reads, generating errors similar to attempt to access beyond end of device sdbi1: rw=14, want=36028797014862705, limit=125026959 The unwanted sign extension occurrs because the function arc_read_nolock() stores the offset as a daddr_t, a 32-bit signed int type in the Linux kernel. This offset is then passed to zio_read_phys() as a uint64_t argument, causing sign extension for values of 0x80000000 or greater. To avoid this, we store the offset in a uint64_t. This change also changes a few daddr_t struct members to uint64_t in the libspl headers to avoid similar bugs cropping up in the future. We also add an ASSERT to __vdev_disk_physio() to check for invalid offsets. Closes #66 Signed-off-by: Brian Behlendorf --- lib/libspl/include/sys/dkio.h | 4 ++-- lib/libspl/include/sys/dklabel.h | 4 ++-- lib/libspl/include/sys/vtoc.h | 12 ++++++------ module/zfs/arc.c | 2 +- module/zfs/vdev_disk.c | 2 ++ 5 files changed, 13 insertions(+), 11 deletions(-) diff --git a/lib/libspl/include/sys/dkio.h b/lib/libspl/include/sys/dkio.h index 32f7865..6b430b8 100644 --- a/lib/libspl/include/sys/dkio.h +++ b/lib/libspl/include/sys/dkio.h @@ -258,13 +258,13 @@ struct defect_header { */ #ifdef _SYSCALL32 struct part_info32 { - daddr32_t p_start; + uint32_t p_start; int p_length; }; #endif /* _SYSCALL32 */ struct part_info { - daddr_t p_start; + uint64_t p_start; int p_length; }; diff --git a/lib/libspl/include/sys/dklabel.h b/lib/libspl/include/sys/dklabel.h index 77d5da1..8772e4a 100644 --- a/lib/libspl/include/sys/dklabel.h +++ b/lib/libspl/include/sys/dklabel.h @@ -81,8 +81,8 @@ extern "C" { * Returned in struct dk_allmap by ioctl DKIOC[SG]APART (dkio(7I)) */ struct dk_map { - daddr_t dkl_cylno; /* starting cylinder */ - daddr_t dkl_nblk; /* number of blocks; if == 0, */ + uint64_t dkl_cylno; /* starting cylinder */ + uint64_t dkl_nblk; /* number of blocks; if == 0, */ /* partition is undefined */ }; diff --git a/lib/libspl/include/sys/vtoc.h b/lib/libspl/include/sys/vtoc.h index 004b490..22a652b 100644 --- a/lib/libspl/include/sys/vtoc.h +++ b/lib/libspl/include/sys/vtoc.h @@ -97,10 +97,10 @@ extern "C" { #define VT_EOVERFLOW (-7) /* VTOC op. data struct limited */ struct partition { - ushort_t p_tag; /* ID tag of partition */ - ushort_t p_flag; /* permission flags */ - daddr_t p_start; /* start sector no of partition */ - long p_size; /* # of blocks in partition */ + ushort_t p_tag; /* ID tag of partition */ + ushort_t p_flag; /* permission flags */ + uint64_t p_start; /* start sector no of partition */ + long p_size; /* # of blocks in partition */ }; struct vtoc { @@ -121,7 +121,7 @@ struct extpartition { ushort_t p_flag; /* permission flags */ ushort_t p_pad[2]; diskaddr_t p_start; /* start sector no of partition */ - diskaddr_t p_size; /* # of blocks in partition */ + diskaddr_t p_size; /* # of blocks in partition */ }; @@ -156,7 +156,7 @@ struct extvtoc { for (i = 0; i < V_NUMPAR; i++) { \ v.v_part[i].p_tag = extv.v_part[i].p_tag; \ v.v_part[i].p_flag = extv.v_part[i].p_flag; \ - v.v_part[i].p_start = (daddr_t)extv.v_part[i].p_start; \ + v.v_part[i].p_start = (uint64_t)extv.v_part[i].p_start; \ v.v_part[i].p_size = (long)extv.v_part[i].p_size; \ v.timestamp[i] = (time_t)extv.timestamp[i]; \ } \ diff --git a/module/zfs/arc.c b/module/zfs/arc.c index f8dfee2..32d99bf 100644 --- a/module/zfs/arc.c +++ b/module/zfs/arc.c @@ -2777,7 +2777,7 @@ top: uint64_t size = BP_GET_LSIZE(bp); arc_callback_t *acb; vdev_t *vd = NULL; - daddr_t addr = -1; + uint64_t addr = -1; boolean_t devw = B_FALSE; if (hdr == NULL) { diff --git a/module/zfs/vdev_disk.c b/module/zfs/vdev_disk.c index 86a089d..3e59bd2 100644 --- a/module/zfs/vdev_disk.c +++ b/module/zfs/vdev_disk.c @@ -360,6 +360,8 @@ __vdev_disk_physio(struct block_device *bdev, zio_t *zio, caddr_t kbuf_ptr, int bio_size, bio_count = 16; int i = 0, error = 0, block_size; + ASSERT3U(kbuf_offset + kbuf_size, <=, bdev->bd_inode->i_size); + retry: dr = vdev_disk_dio_alloc(bio_count); if (dr == NULL) -- 1.8.3.1