diff options
author | James Bottomley <James.Bottomley@suse.de> | 2009-11-03 12:33:07 -0600 |
---|---|---|
committer | James Bottomley <James.Bottomley@suse.de> | 2010-01-18 10:48:05 -0600 |
commit | e3deec090558d5cb5ffdc574e5560f3ed9723394 (patch) | |
tree | c76a5e26a3e08598ada0a2de34adcdf714aa7168 | |
parent | 534ef056db8a8fb6b9d50188d88ed5d1fbc66673 (diff) |
[SCSI] eliminate potential kmalloc failure in scsi_get_vpd_page()
The best way to fix this is to eliminate the intenal kmalloc() and
make the caller allocate the required amount of storage.
Signed-off-by: James Bottomley <James.Bottomley@suse.de>
-rw-r--r-- | drivers/scsi/scsi.c | 40 | ||||
-rw-r--r-- | drivers/scsi/sd.c | 26 | ||||
-rw-r--r-- | drivers/scsi/ses.c | 10 | ||||
-rw-r--r-- | include/scsi/scsi_device.h | 3 |
4 files changed, 36 insertions, 43 deletions
diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index a60da555557..513661f45e5 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -1026,55 +1026,39 @@ static int scsi_vpd_inquiry(struct scsi_device *sdev, unsigned char *buffer, * responsible for calling kfree() on this pointer when it is no longer * needed. If we cannot retrieve the VPD page this routine returns %NULL. */ -unsigned char *scsi_get_vpd_page(struct scsi_device *sdev, u8 page) +int scsi_get_vpd_page(struct scsi_device *sdev, u8 page, unsigned char *buf, + int buf_len) { int i, result; - unsigned int len; - const unsigned int init_vpd_len = 255; - unsigned char *buf = kmalloc(init_vpd_len, GFP_KERNEL); - - if (!buf) - return NULL; /* Ask for all the pages supported by this device */ - result = scsi_vpd_inquiry(sdev, buf, 0, init_vpd_len); + result = scsi_vpd_inquiry(sdev, buf, 0, buf_len); if (result) goto fail; /* If the user actually wanted this page, we can skip the rest */ if (page == 0) - return buf; + return -EINVAL; - for (i = 0; i < buf[3]; i++) + for (i = 0; i < min((int)buf[3], buf_len - 4); i++) if (buf[i + 4] == page) goto found; + + if (i < buf[3] && i > buf_len) + /* ran off the end of the buffer, give us benefit of doubt */ + goto found; /* The device claims it doesn't support the requested page */ goto fail; found: - result = scsi_vpd_inquiry(sdev, buf, page, 255); + result = scsi_vpd_inquiry(sdev, buf, page, buf_len); if (result) goto fail; - /* - * Some pages are longer than 255 bytes. The actual length of - * the page is returned in the header. - */ - len = ((buf[2] << 8) | buf[3]) + 4; - if (len <= init_vpd_len) - return buf; - - kfree(buf); - buf = kmalloc(len, GFP_KERNEL); - result = scsi_vpd_inquiry(sdev, buf, page, len); - if (result) - goto fail; - - return buf; + return 0; fail: - kfree(buf); - return NULL; + return -EINVAL; } EXPORT_SYMBOL_GPL(scsi_get_vpd_page); diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 255da53e5a0..c5e9a99d406 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -1946,13 +1946,13 @@ static void sd_read_block_limits(struct scsi_disk *sdkp) { struct request_queue *q = sdkp->disk->queue; unsigned int sector_sz = sdkp->device->sector_size; - char *buffer; + const int vpd_len = 32; + unsigned char *buffer = kmalloc(vpd_len, GFP_KERNEL); - /* Block Limits VPD */ - buffer = scsi_get_vpd_page(sdkp->device, 0xb0); - - if (buffer == NULL) - return; + if (!buffer || + /* Block Limits VPD */ + scsi_get_vpd_page(sdkp->device, 0xb0, buffer, vpd_len)) + goto out; blk_queue_io_min(sdkp->disk->queue, get_unaligned_be16(&buffer[6]) * sector_sz); @@ -1984,6 +1984,7 @@ static void sd_read_block_limits(struct scsi_disk *sdkp) get_unaligned_be32(&buffer[32]) & ~(1 << 31); } + out: kfree(buffer); } @@ -1993,20 +1994,23 @@ static void sd_read_block_limits(struct scsi_disk *sdkp) */ static void sd_read_block_characteristics(struct scsi_disk *sdkp) { - char *buffer; + unsigned char *buffer; u16 rot; + const int vpd_len = 32; - /* Block Device Characteristics VPD */ - buffer = scsi_get_vpd_page(sdkp->device, 0xb1); + buffer = kmalloc(vpd_len, GFP_KERNEL); - if (buffer == NULL) - return; + if (!buffer || + /* Block Device Characteristics VPD */ + scsi_get_vpd_page(sdkp->device, 0xb1, buffer, vpd_len)) + goto out; rot = get_unaligned_be16(&buffer[4]); if (rot == 1) queue_flag_set_unlocked(QUEUE_FLAG_NONROT, sdkp->disk->queue); + out: kfree(buffer); } diff --git a/drivers/scsi/ses.c b/drivers/scsi/ses.c index 55b034b7270..1d7a8780e00 100644 --- a/drivers/scsi/ses.c +++ b/drivers/scsi/ses.c @@ -448,13 +448,17 @@ static void ses_match_to_enclosure(struct enclosure_device *edev, .addr = 0, }; - buf = scsi_get_vpd_page(sdev, 0x83); - if (!buf) - return; + buf = kmalloc(INIT_ALLOC_SIZE, GFP_KERNEL); + if (!buf || scsi_get_vpd_page(sdev, 0x83, buf, INIT_ALLOC_SIZE)) + goto free; ses_enclosure_data_process(edev, to_scsi_device(edev->edev.parent), 0); vpd_len = ((buf[2] << 8) | buf[3]) + 4; + kfree(buf); + buf = kmalloc(vpd_len, GFP_KERNEL); + if (!buf ||scsi_get_vpd_page(sdev, 0x83, buf, vpd_len)) + goto free; desc = buf + 4; while (desc < buf + vpd_len) { diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index 7c4449900c2..d80b6dbed1c 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h @@ -348,7 +348,8 @@ extern int scsi_mode_select(struct scsi_device *sdev, int pf, int sp, struct scsi_sense_hdr *); extern int scsi_test_unit_ready(struct scsi_device *sdev, int timeout, int retries, struct scsi_sense_hdr *sshdr); -extern unsigned char *scsi_get_vpd_page(struct scsi_device *, u8 page); +extern int scsi_get_vpd_page(struct scsi_device *, u8 page, unsigned char *buf, + int buf_len); extern int scsi_device_set_state(struct scsi_device *sdev, enum scsi_device_state state); extern struct scsi_event *sdev_evt_alloc(enum scsi_device_event evt_type, |