Vulnerability: drivers/media/usb/dvb-usb/dvb-usb-firmware.c in the Linux kernel 4.9.x and 4.10.x before 4.10.7 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-8061
CWE: CWE-119
Severity score: 10
Project: https://github.com/torvalds/linux
Published: 2017-04-23
File: drivers/media/usb/dvb-usb/dvb-usb-firmware.c
Commit message: [media] dvb-usb-firmware: don't do DMA on stack

The buffer allocation for the firmware data was changed in
commit 43fab9793c1f ("[media] dvb-usb: don't use stack for firmware load")
but the same applies for the reset value.

Fixes: 43fab9793c1f ("[media] dvb-usb: don't use stack for firmware load")
Cc: stable@vger.kernel.org
Signed-off-by: Stefan Brüns <stefan.bruens@rwth-aachen.de>
Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
Commit author: Stefan Brüns

Vulnerable version (before fix):
 static int usb_cypress_writemem(struct usb_device *udev,u16 addr,u8 *data, u8 le int usb_cypress_load_firmware(struct usb_device *udev, const struct firmware *fw, int type) { 	struct hexline *hx; 	u8 reset; 	int ret,pos=0;  	hx = kmalloc(sizeof(*hx), GFP_KERNEL); 	if (!hx) 		return -ENOMEM;  	/* stop the CPU */ 	reset = 1; 	if ((ret = usb_cypress_writemem(udev,cypress[type].cpu_cs_register,&reset,1)) != 1) 		err("could not stop the USB controller CPU.");  	while ((ret = dvb_usb_get_hexline(fw, hx, &pos)) > 0) { int usb_cypress_load_firmware(struct usb_device *udev, const struct firmware *fw 	} 	if (ret < 0) { 		err("firmware download failed at %d with %d",pos,ret); 		kfree(hx); 		return ret; 	}  	if (ret == 0) { 		/* restart the CPU */ 		reset = 0; 		if (ret || usb_cypress_writemem(udev,cypress[type].cpu_cs_register,&reset,1) != 1) { 			err("could not restart the USB controller CPU."); 			ret = -EINVAL; 		} 	} else 		ret = -EIO;  	kfree(hx);  	return ret; }

Fixed version (after fix):
 static int usb_cypress_writemem(struct usb_device *udev,u16 addr,u8 *data, u8 le int usb_cypress_load_firmware(struct usb_device *udev, const struct firmware *fw, int type) { 	struct hexline *hx; 	u8 *buf; 	int ret, pos = 0; 	u16 cpu_cs_register = cypress[type].cpu_cs_register;  	buf = kmalloc(sizeof(*hx), GFP_KERNEL); 	if (!buf) 		return -ENOMEM; 	hx = (struct hexline *)buf;  	/* stop the CPU */ 	buf[0] = 1; 	if (usb_cypress_writemem(udev, cpu_cs_register, buf, 1) != 1) 		err("could not stop the USB controller CPU.");  	while ((ret = dvb_usb_get_hexline(fw, hx, &pos)) > 0) { int usb_cypress_load_firmware(struct usb_device *udev, const struct firmware *fw 	} 	if (ret < 0) { 		err("firmware download failed at %d with %d",pos,ret); 		kfree(buf); 		return ret; 	}  	if (ret == 0) { 		/* restart the CPU */ 		buf[0] = 0; 		if (usb_cypress_writemem(udev, cpu_cs_register, buf, 1) != 1) { 			err("could not restart the USB controller CPU."); 			ret = -EINVAL; 		} 	} else 		ret = -EIO;  	kfree(buf);  	return ret; }

Patch:
patch: @@ -36,16 +36,18 @@ static int usb_cypress_writemem(struct usb_device *udev,u16 addr,u8 *data, u8 le
 int usb_cypress_load_firmware(struct usb_device *udev, const struct firmware *fw, int type)
 {
 	struct hexline *hx;
-	u8 reset;
-	int ret,pos=0;
+	u8 *buf;
+	int ret, pos = 0;
+	u16 cpu_cs_register = cypress[type].cpu_cs_register;
 
-	hx = kmalloc(sizeof(*hx), GFP_KERNEL);
-	if (!hx)
+	buf = kmalloc(sizeof(*hx), GFP_KERNEL);
+	if (!buf)
 		return -ENOMEM;
+	hx = (struct hexline *)buf;
 
 	/* stop the CPU */
-	reset = 1;
-	if ((ret = usb_cypress_writemem(udev,cypress[type].cpu_cs_register,&reset,1)) != 1)
+	buf[0] = 1;
+	if (usb_cypress_writemem(udev, cpu_cs_register, buf, 1) != 1)
 		err("could not stop the USB controller CPU.");
 
 	while ((ret = dvb_usb_get_hexline(fw, hx, &pos)) > 0) {
@@ -61,21 +63,21 @@ int usb_cypress_load_firmware(struct usb_device *udev, const struct firmware *fw
 	}
 	if (ret < 0) {
 		err("firmware download failed at %d with %d",pos,ret);
-		kfree(hx);
+		kfree(buf);
 		return ret;
 	}
 
 	if (ret == 0) {
 		/* restart the CPU */
-		reset = 0;
-		if (ret || usb_cypress_writemem(udev,cypress[type].cpu_cs_register,&reset,1) != 1) {
+		buf[0] = 0;
+		if (usb_cypress_writemem(udev, cpu_cs_register, buf, 1) != 1) {
 			err("could not restart the USB controller CPU.");
 			ret = -EINVAL;
 		}
 	} else
 		ret = -EIO;
 
-	kfree(hx);
+	kfree(buf);
 
 	return ret;
 }
path: drivers/media/usb/dvb-usb/dvb-usb-firmware.c