Vulnerability: 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.
Vulnerability ID: CVE-2017-5547
CWE: CWE-119
Severity score: 10
Project: https://github.com/torvalds/linux
Published: 2017-02-06
File: drivers/hid/hid-corsair.c
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>
Commit author: Johan Hovold

Vulnerable version (before fix):
 static enum led_brightness k90_backlight_get(struct led_classdev *led_cdev) 	struct usb_interface *usbif = to_usb_interface(dev->parent); 	struct usb_device *usbdev = interface_to_usbdev(usbif); 	int brightness; 	char data[8];  	ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), 			      K90_REQUEST_STATUS, static enum led_brightness k90_backlight_get(struct led_classdev *led_cdev) 	if (ret < 0) { 		dev_warn(dev, "Failed to get K90 initial state (error %d).\n", 			 ret); 		return -EIO; 	} 	brightness = data[4]; 	if (brightness < 0 || brightness > 3) { 		dev_warn(dev, 			 "Read invalid backlight brightness: %02hhx.\n", 			 data[4]); 		return -EIO; 	} 	return brightness; }  static enum led_brightness k90_record_led_get(struct led_classdev *led_cdev) static ssize_t k90_show_macro_mode(struct device *dev, 	struct usb_interface *usbif = to_usb_interface(dev->parent); 	struct usb_device *usbdev = interface_to_usbdev(usbif); 	const char *macro_mode; 	char data[8];  	ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), 			      K90_REQUEST_GET_MODE, static ssize_t k90_show_macro_mode(struct device *dev, 	if (ret < 0) { 		dev_warn(dev, "Failed to get K90 initial mode (error %d).\n", 			 ret); 		return -EIO; 	}  	switch (data[0]) { static ssize_t k90_show_macro_mode(struct device *dev, 	default: 		dev_warn(dev, "K90 in unknown mode: %02hhx.\n", 			 data[0]); 		return -EIO; 	}  	return snprintf(buf, PAGE_SIZE, "%s\n", macro_mode); }  static ssize_t k90_store_macro_mode(struct device *dev, static ssize_t k90_show_current_profile(struct device *dev, 	struct usb_interface *usbif = to_usb_interface(dev->parent); 	struct usb_device *usbdev = interface_to_usbdev(usbif); 	int current_profile; 	char data[8];  	ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), 			      K90_REQUEST_STATUS, static ssize_t k90_show_current_profile(struct device *dev, 	if (ret < 0) { 		dev_warn(dev, "Failed to get K90 initial state (error %d).\n", 			 ret); 		return -EIO; 	} 	current_profile = data[7]; 	if (current_profile < 1 || current_profile > 3) { 		dev_warn(dev, "Read invalid current profile: %02hhx.\n", 			 data[7]); 		return -EIO; 	}  	return snprintf(buf, PAGE_SIZE, "%d\n", current_profile); }  static ssize_t k90_store_current_profile(struct device *dev,

Fixed version (after fix):
 static enum led_brightness k90_backlight_get(struct led_classdev *led_cdev) 	struct usb_interface *usbif = to_usb_interface(dev->parent); 	struct usb_device *usbdev = interface_to_usbdev(usbif); 	int brightness; 	char *data;  	data = kmalloc(8, GFP_KERNEL); 	if (!data) 		return -ENOMEM;  	ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), 			      K90_REQUEST_STATUS, static enum led_brightness k90_backlight_get(struct led_classdev *led_cdev) 	if (ret < 0) { 		dev_warn(dev, "Failed to get K90 initial state (error %d).\n", 			 ret); 		ret = -EIO; 		goto out; 	} 	brightness = data[4]; 	if (brightness < 0 || brightness > 3) { 		dev_warn(dev, 			 "Read invalid backlight brightness: %02hhx.\n", 			 data[4]); 		ret = -EIO; 		goto out; 	} 	ret = brightness; out: 	kfree(data);  	return ret; }  static enum led_brightness k90_record_led_get(struct led_classdev *led_cdev) static ssize_t k90_show_macro_mode(struct device *dev, 	struct usb_interface *usbif = to_usb_interface(dev->parent); 	struct usb_device *usbdev = interface_to_usbdev(usbif); 	const char *macro_mode; 	char *data;  	data = kmalloc(2, GFP_KERNEL); 	if (!data) 		return -ENOMEM;  	ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), 			      K90_REQUEST_GET_MODE, static ssize_t k90_show_macro_mode(struct device *dev, 	if (ret < 0) { 		dev_warn(dev, "Failed to get K90 initial mode (error %d).\n", 			 ret); 		ret = -EIO; 		goto out; 	}  	switch (data[0]) { static ssize_t k90_show_macro_mode(struct device *dev, 	default: 		dev_warn(dev, "K90 in unknown mode: %02hhx.\n", 			 data[0]); 		ret = -EIO; 		goto out; 	}  	ret = snprintf(buf, PAGE_SIZE, "%s\n", macro_mode); out: 	kfree(data);  	return ret; }  static ssize_t k90_store_macro_mode(struct device *dev, static ssize_t k90_show_current_profile(struct device *dev, 	struct usb_interface *usbif = to_usb_interface(dev->parent); 	struct usb_device *usbdev = interface_to_usbdev(usbif); 	int current_profile; 	char *data;  	data = kmalloc(8, GFP_KERNEL); 	if (!data) 		return -ENOMEM;  	ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), 			      K90_REQUEST_STATUS, static ssize_t k90_show_current_profile(struct device *dev, 	if (ret < 0) { 		dev_warn(dev, "Failed to get K90 initial state (error %d).\n", 			 ret); 		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]); 		ret = -EIO; 		goto out; 	}  	ret = snprintf(buf, PAGE_SIZE, "%d\n", current_profile); out: 	kfree(data);  	return ret; }  static ssize_t k90_store_current_profile(struct device *dev,

Patch:
patch: @@ -148,7 +148,11 @@ static enum led_brightness k90_backlight_get(struct led_classdev *led_cdev)
 	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,
@@ -158,16 +162,22 @@ static enum led_brightness k90_backlight_get(struct led_classdev *led_cdev)
 	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)
@@ -253,7 +263,11 @@ static ssize_t k90_show_macro_mode(struct device *dev,
 	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,
@@ -263,7 +277,8 @@ static ssize_t k90_show_macro_mode(struct device *dev,
 	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]) {
@@ -277,10 +292,15 @@ static ssize_t k90_show_macro_mode(struct device *dev,
 	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,
@@ -320,7 +340,11 @@ static ssize_t k90_show_current_profile(struct device *dev,
 	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,
@@ -330,16 +354,22 @@ static ssize_t k90_show_current_profile(struct device *dev,
 	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,
path: drivers/hid/hid-corsair.c