CVE-2017-5547: Improper Restriction of Operations within the Bounds of a Memory Buffer (CWE-119)
value: drivers/hid/hid-corsair.c in the Linux kernel 4.9.x before 4.9.6 interacts incorrectly with the CONFIG_VMAP_STACK option, which allows local users to cause a denial of service (system crash or memory corruption) or possibly have unspecified other impact by leveraging use of more than one virtual page for a DMA scatterlist.
CWE description: The product performs operations on a memory buffer, but it reads from or writes to a memory location outside the buffer's intended boundary. This may result in read or write operations on unexpected memory locations that could be linked to other variables, data structures, or internal program data.
CVSS v3 base score: 7.8
Published: 2017-02-06T06:59Z
Repository: https://github.com/torvalds/linux
Language: C
Files changed: drivers/hid/hid-corsair.c
Commit date: 2017-01-13 11:59:31 +0100
Commit message: HID: corsair: fix DMA buffers on stack

Not all platforms support DMA to the stack, and specifically since v4.9
this is no longer supported on x86 with VMAP_STACK either.

Note that the macro-mode buffer was larger than necessary.

Fixes: 6f78193ee9ea ("HID: corsair: Add Corsair Vengeance K90 driver")
Cc: stable <stable@vger.kernel.org>
Signed-off-by: Johan Hovold <johan@kernel.org>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>

Diff: diff --git a/drivers/hid/hid-corsair.c b/drivers/hid/hid-corsair.c
index 717704e9ae07bd..5971907a23b13d 100644
--- a/drivers/hid/hid-corsair.c
+++ b/drivers/hid/hid-corsair.c
@@ -146,30 +146,40 @@ static enum led_brightness k90_backlight_get(struct led_classdev *led_cdev)
 	struct k90_led *led = container_of(led_cdev, struct k90_led, cdev);
 	struct device *dev = led->cdev.dev->parent;
 	struct usb_interface *usbif = to_usb_interface(dev->parent);
 	struct usb_device *usbdev = interface_to_usbdev(usbif);
 	int brightness;
-	char data[8];
+	char *data;
+
+	data = kmalloc(8, GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
 
 	ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0),
 			      K90_REQUEST_STATUS,
 			      USB_DIR_IN | USB_TYPE_VENDOR |
 			      USB_RECIP_DEVICE, 0, 0, data, 8,
 			      USB_CTRL_SET_TIMEOUT);
 	if (ret < 0) {
 		dev_warn(dev, "Failed to get K90 initial state (error %d).\n",
 			 ret);
-		return -EIO;
+		ret = -EIO;
+		goto out;
 	}
 	brightness = data[4];
 	if (brightness < 0 || brightness > 3) {
 		dev_warn(dev,
 			 "Read invalid backlight brightness: %02hhx.\n",
 			 data[4]);
-		return -EIO;
+		ret = -EIO;
+		goto out;
 	}
-	return brightness;
+	ret = brightness;
+out:
+	kfree(data);
+
+	return ret;
 }
 
 static enum led_brightness k90_record_led_get(struct led_classdev *led_cdev)
 {
 	struct k90_led *led = container_of(led_cdev, struct k90_led, cdev);
@@ -251,21 +261,26 @@ static ssize_t k90_show_macro_mode(struct device *dev,
 {
 	int ret;
 	struct usb_interface *usbif = to_usb_interface(dev->parent);
 	struct usb_device *usbdev = interface_to_usbdev(usbif);
 	const char *macro_mode;
-	char data[8];
+	char *data;
+
+	data = kmalloc(2, GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
 
 	ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0),
 			      K90_REQUEST_GET_MODE,
 			      USB_DIR_IN | USB_TYPE_VENDOR |
 			      USB_RECIP_DEVICE, 0, 0, data, 2,
 			      USB_CTRL_SET_TIMEOUT);
 	if (ret < 0) {
 		dev_warn(dev, "Failed to get K90 initial mode (error %d).\n",
 			 ret);
-		return -EIO;
+		ret = -EIO;
+		goto out;
 	}
 
 	switch (data[0]) {
 	case K90_MACRO_MODE_HW:
 		macro_mode = "HW";
@@ -275,14 +290,19 @@ static ssize_t k90_show_macro_mode(struct device *dev,
 		macro_mode = "SW";
 		break;
 	default:
 		dev_warn(dev, "K90 in unknown mode: %02hhx.\n",
 			 data[0]);
-		return -EIO;
+		ret = -EIO;
+		goto out;
 	}
 
-	return snprintf(buf, PAGE_SIZE, "%s\n", macro_mode);
+	ret = snprintf(buf, PAGE_SIZE, "%s\n", macro_mode);
+out:
+	kfree(data);
+
+	return ret;
 }
 
 static ssize_t k90_store_macro_mode(struct device *dev,
 				    struct device_attribute *attr,
 				    const char *buf, size_t count)
@@ -318,30 +338,40 @@ static ssize_t k90_show_current_profile(struct device *dev,
 {
 	int ret;
 	struct usb_interface *usbif = to_usb_interface(dev->parent);
 	struct usb_device *usbdev = interface_to_usbdev(usbif);
 	int current_profile;
-	char data[8];
+	char *data;
+
+	data = kmalloc(8, GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
 
 	ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0),
 			      K90_REQUEST_STATUS,
 			      USB_DIR_IN | USB_TYPE_VENDOR |
 			      USB_RECIP_DEVICE, 0, 0, data, 8,
 			      USB_CTRL_SET_TIMEOUT);
 	if (ret < 0) {
 		dev_warn(dev, "Failed to get K90 initial state (error %d).\n",
 			 ret);
-		return -EIO;
+		ret = -EIO;
+		goto out;
 	}
 	current_profile = data[7];
 	if (current_profile < 1 || current_profile > 3) {
 		dev_warn(dev, "Read invalid current profile: %02hhx.\n",
 			 data[7]);
-		return -EIO;
+		ret = -EIO;
+		goto out;
 	}
 
-	return snprintf(buf, PAGE_SIZE, "%d\n", current_profile);
+	ret = snprintf(buf, PAGE_SIZE, "%d\n", current_profile);
+out:
+	kfree(data);
+
+	return ret;
 }
 
 static ssize_t k90_store_current_profile(struct device *dev,
 					 struct device_attribute *attr,
 					 const char *buf, size_t count)

Vulnerable code: 	char data[8];
		return -EIO;
		return -EIO;
	return brightness;
	char data[8];
		return -EIO;
		return -EIO;
	return snprintf(buf, PAGE_SIZE, "%s\n", macro_mode);
	char data[8];
		return -EIO;
		return -EIO;
	return snprintf(buf, PAGE_SIZE, "%d\n", current_profile);
Fixed code: 	char *data;

	data = kmalloc(8, GFP_KERNEL);
	if (!data)
		return -ENOMEM;
		ret = -EIO;
		goto out;
		ret = -EIO;
		goto out;
	ret = brightness;
out:
	kfree(data);

	return ret;
	char *data;

	data = kmalloc(2, GFP_KERNEL);
	if (!data)
		return -ENOMEM;
		ret = -EIO;
		goto out;
		ret = -EIO;
		goto out;
	ret = snprintf(buf, PAGE_SIZE, "%s\n", macro_mode);
out:
	kfree(data);

	return ret;
	char *data;

	data = kmalloc(8, GFP_KERNEL);
	if (!data)
		return -ENOMEM;
		ret = -EIO;
		goto out;
		ret = -EIO;
		goto out;
	ret = snprintf(buf, PAGE_SIZE, "%d\n", current_profile);
out:
	kfree(data);

	return ret;