Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e3deec09 authored by James Bottomley's avatar James Bottomley
Browse files

[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
Loading
Loading
Loading
Loading
+12 −28
Original line number Diff line number Diff line
@@ -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);
	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);
	result = scsi_vpd_inquiry(sdev, buf, page, buf_len);
	if (result)
		goto fail;

	return buf;
	return 0;

 fail:
	kfree(buf);
	return NULL;
	return -EINVAL;
}
EXPORT_SYMBOL_GPL(scsi_get_vpd_page);

+15 −11
Original line number Diff line number Diff line
@@ -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);

	if (!buffer ||
	    /* Block Limits VPD */
	buffer = scsi_get_vpd_page(sdkp->device, 0xb0);

	if (buffer == NULL)
		return;
	    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);
}

+7 −3
Original line number Diff line number Diff line
@@ -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) {
+2 −1
Original line number Diff line number Diff line
@@ -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,