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 <behlendorf1@llnl.gov>
This commit is contained in:
Ned Bass 2010-11-10 13:36:18 -08:00 committed by Brian Behlendorf
parent 1f30b9d432
commit e06be58641
5 changed files with 13 additions and 11 deletions

View File

@ -258,13 +258,13 @@ struct defect_header {
*/ */
#ifdef _SYSCALL32 #ifdef _SYSCALL32
struct part_info32 { struct part_info32 {
daddr32_t p_start; uint32_t p_start;
int p_length; int p_length;
}; };
#endif /* _SYSCALL32 */ #endif /* _SYSCALL32 */
struct part_info { struct part_info {
daddr_t p_start; uint64_t p_start;
int p_length; int p_length;
}; };

View File

@ -81,8 +81,8 @@ extern "C" {
* Returned in struct dk_allmap by ioctl DKIOC[SG]APART (dkio(7I)) * Returned in struct dk_allmap by ioctl DKIOC[SG]APART (dkio(7I))
*/ */
struct dk_map { struct dk_map {
daddr_t dkl_cylno; /* starting cylinder */ uint64_t dkl_cylno; /* starting cylinder */
daddr_t dkl_nblk; /* number of blocks; if == 0, */ uint64_t dkl_nblk; /* number of blocks; if == 0, */
/* partition is undefined */ /* partition is undefined */
}; };

View File

@ -99,7 +99,7 @@ extern "C" {
struct partition { struct partition {
ushort_t p_tag; /* ID tag of partition */ ushort_t p_tag; /* ID tag of partition */
ushort_t p_flag; /* permission flags */ ushort_t p_flag; /* permission flags */
daddr_t p_start; /* start sector no of partition */ uint64_t p_start; /* start sector no of partition */
long p_size; /* # of blocks in partition */ long p_size; /* # of blocks in partition */
}; };
@ -156,7 +156,7 @@ struct extvtoc {
for (i = 0; i < V_NUMPAR; i++) { \ for (i = 0; i < V_NUMPAR; i++) { \
v.v_part[i].p_tag = extv.v_part[i].p_tag; \ 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_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.v_part[i].p_size = (long)extv.v_part[i].p_size; \
v.timestamp[i] = (time_t)extv.timestamp[i]; \ v.timestamp[i] = (time_t)extv.timestamp[i]; \
} \ } \

View File

@ -2777,7 +2777,7 @@ top:
uint64_t size = BP_GET_LSIZE(bp); uint64_t size = BP_GET_LSIZE(bp);
arc_callback_t *acb; arc_callback_t *acb;
vdev_t *vd = NULL; vdev_t *vd = NULL;
daddr_t addr = -1; uint64_t addr = -1;
boolean_t devw = B_FALSE; boolean_t devw = B_FALSE;
if (hdr == NULL) { if (hdr == NULL) {

View File

@ -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 bio_size, bio_count = 16;
int i = 0, error = 0, block_size; int i = 0, error = 0, block_size;
ASSERT3U(kbuf_offset + kbuf_size, <=, bdev->bd_inode->i_size);
retry: retry:
dr = vdev_disk_dio_alloc(bio_count); dr = vdev_disk_dio_alloc(bio_count);
if (dr == NULL) if (dr == NULL)