Commit e3deec09 authored by James Bottomley's avatar James Bottomley

[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: default avatarJames Bottomley <James.Bottomley@suse.de>
parent 534ef056
...@@ -1026,55 +1026,39 @@ static int scsi_vpd_inquiry(struct scsi_device *sdev, unsigned char *buffer, ...@@ -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 * responsible for calling kfree() on this pointer when it is no longer
* needed. If we cannot retrieve the VPD page this routine returns %NULL. * 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; 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 */ /* 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) if (result)
goto fail; goto fail;
/* If the user actually wanted this page, we can skip the rest */ /* If the user actually wanted this page, we can skip the rest */
if (page == 0) 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) if (buf[i + 4] == page)
goto found; 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 */ /* The device claims it doesn't support the requested page */
goto fail; goto fail;
found: found:
result = scsi_vpd_inquiry(sdev, buf, page, 255); result = scsi_vpd_inquiry(sdev, buf, page, buf_len);
if (result) if (result)
goto fail; goto fail;
/* return 0;
* 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;
fail: fail:
kfree(buf); return -EINVAL;
return NULL;
} }
EXPORT_SYMBOL_GPL(scsi_get_vpd_page); EXPORT_SYMBOL_GPL(scsi_get_vpd_page);
......
...@@ -1946,13 +1946,13 @@ static void sd_read_block_limits(struct scsi_disk *sdkp) ...@@ -1946,13 +1946,13 @@ static void sd_read_block_limits(struct scsi_disk *sdkp)
{ {
struct request_queue *q = sdkp->disk->queue; struct request_queue *q = sdkp->disk->queue;
unsigned int sector_sz = sdkp->device->sector_size; 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 */ if (!buffer ||
buffer = scsi_get_vpd_page(sdkp->device, 0xb0); /* Block Limits VPD */
scsi_get_vpd_page(sdkp->device, 0xb0, buffer, vpd_len))
if (buffer == NULL) goto out;
return;
blk_queue_io_min(sdkp->disk->queue, blk_queue_io_min(sdkp->disk->queue,
get_unaligned_be16(&buffer[6]) * sector_sz); get_unaligned_be16(&buffer[6]) * sector_sz);
...@@ -1984,6 +1984,7 @@ static void sd_read_block_limits(struct scsi_disk *sdkp) ...@@ -1984,6 +1984,7 @@ static void sd_read_block_limits(struct scsi_disk *sdkp)
get_unaligned_be32(&buffer[32]) & ~(1 << 31); get_unaligned_be32(&buffer[32]) & ~(1 << 31);
} }
out:
kfree(buffer); kfree(buffer);
} }
...@@ -1993,20 +1994,23 @@ static void sd_read_block_limits(struct scsi_disk *sdkp) ...@@ -1993,20 +1994,23 @@ static void sd_read_block_limits(struct scsi_disk *sdkp)
*/ */
static void sd_read_block_characteristics(struct scsi_disk *sdkp) static void sd_read_block_characteristics(struct scsi_disk *sdkp)
{ {
char *buffer; unsigned char *buffer;
u16 rot; u16 rot;
const int vpd_len = 32;
/* Block Device Characteristics VPD */ buffer = kmalloc(vpd_len, GFP_KERNEL);
buffer = scsi_get_vpd_page(sdkp->device, 0xb1);
if (buffer == NULL) if (!buffer ||
return; /* Block Device Characteristics VPD */
scsi_get_vpd_page(sdkp->device, 0xb1, buffer, vpd_len))
goto out;
rot = get_unaligned_be16(&buffer[4]); rot = get_unaligned_be16(&buffer[4]);
if (rot == 1) if (rot == 1)
queue_flag_set_unlocked(QUEUE_FLAG_NONROT, sdkp->disk->queue); queue_flag_set_unlocked(QUEUE_FLAG_NONROT, sdkp->disk->queue);
out:
kfree(buffer); kfree(buffer);
} }
......
...@@ -448,13 +448,17 @@ static void ses_match_to_enclosure(struct enclosure_device *edev, ...@@ -448,13 +448,17 @@ static void ses_match_to_enclosure(struct enclosure_device *edev,
.addr = 0, .addr = 0,
}; };
buf = scsi_get_vpd_page(sdev, 0x83); buf = kmalloc(INIT_ALLOC_SIZE, GFP_KERNEL);
if (!buf) if (!buf || scsi_get_vpd_page(sdev, 0x83, buf, INIT_ALLOC_SIZE))
return; goto free;
ses_enclosure_data_process(edev, to_scsi_device(edev->edev.parent), 0); ses_enclosure_data_process(edev, to_scsi_device(edev->edev.parent), 0);
vpd_len = ((buf[2] << 8) | buf[3]) + 4; 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; desc = buf + 4;
while (desc < buf + vpd_len) { while (desc < buf + vpd_len) {
......
...@@ -348,7 +348,8 @@ extern int scsi_mode_select(struct scsi_device *sdev, int pf, int sp, ...@@ -348,7 +348,8 @@ extern int scsi_mode_select(struct scsi_device *sdev, int pf, int sp,
struct scsi_sense_hdr *); struct scsi_sense_hdr *);
extern int scsi_test_unit_ready(struct scsi_device *sdev, int timeout, extern int scsi_test_unit_ready(struct scsi_device *sdev, int timeout,
int retries, struct scsi_sense_hdr *sshdr); 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, extern int scsi_device_set_state(struct scsi_device *sdev,
enum scsi_device_state state); enum scsi_device_state state);
extern struct scsi_event *sdev_evt_alloc(enum scsi_device_event evt_type, extern struct scsi_event *sdev_evt_alloc(enum scsi_device_event evt_type,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment