From: Tomas Henzl <thenzl@redhat.com> Date: Tue, 17 Aug 2010 15:03:29 -0400 Subject: [scsi] fix potential kmalloc failure in scsi_get_vpd_page Message-id: <20100817150329.6769.42186.sendpatchset@localhost.localdomain> Patchwork-id: 27661 O-Subject: [RHEL5.6 PATCH/5] scsi: eliminate potential kmalloc failure in scsi_get_vpd_page() Bugzilla: 599420 RH-Acked-by: Stanislaw Gruszka <sgruszka@redhat.com> The best way to fix this is to eliminate the internal kmalloc() and make the caller allocate the required amount of storage. http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=e3deec090558d5cb5ffdc574e5560f3ed9723394 Signed-off-by: Jarod Wilson <jarod@redhat.com> diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index 3a7a9fe..f830672 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -858,55 +858,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/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index 1fb92bf..b117ac5 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h @@ -339,7 +339,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); -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,