Skip to contentSkip to conversation

Fix an OOB access bug in technisat_usb2_get_ir

PATCH In review Hui Peng 2 participants 3 messages Aug 20, 2019 – Aug 21, 2019 Media (V4L/DVB)

lore
Share & tools
lore

Hui Peng

author
#0 lore raw

PATCH 1/1 Fix an OOB access bug in technisat_usb2_get_ir

In the while loop of technisat_usb2_get_ir, it scans through a fix-sized buffer read from the device side, the termination condition of the loop is *b == 0xff. If no 0xff byte is read from the device side, OOB access happens.

This patch fixes the bug by adding an upper bound in the while loop.

---
 drivers/media/usb/dvb-usb/technisat-usb2.c | 5 +++++
 1 file changed, 5 insertions(+)
drivers/media/usb/dvb-usb/technisat-usb2.c +5 -0
index c659e18b358b..181f5f97af45 100644
@@ -612,6 +612,7 @@ static int technisat_usb2_get_ir(struct dvb_usb_device *d)
 	u8 *b;
 	int ret;
 	struct ir_raw_event ev;
+	int i = 0;
 
 	buf[0] = GET_IR_DATA_VENDOR_REQUEST;
 	buf[1] = 0x08;
@@ -656,11 +657,15 @@ static int technisat_usb2_get_ir(struct dvb_usb_device *d)
 
 	ev.pulse = 0;
 	while (1) {
+		// only `ret` bytes are read from the device side
+		if (i >= ret)
+			break;
 		ev.pulse = !ev.pulse;
 		ev.duration = (*b * FIRMWARE_CLOCK_DIVISOR * FIRMWARE_CLOCK_TICK) / 1000;
 		ir_raw_event_store(d->rc_dev, &ev);
 
 		b++;
+		i++;
 		if (*b == 0xff) {
 			ev.pulse = 0;
 			ev.duration = 888888*2;
Signature · 1 line
2.23.0
author
#1 lore raw

The following is the kasan report. This bug was found in v4.20-rc2, but it is present in the latest version.

BUG: KASAN: slab-out-of-bounds in technisat_usb2_get_ir
drivers/media/usb/dvb-usb/technisat-usb2.c:664 [inline]
BUG: KASAN: slab-out-of-bounds in technisat_usb2_rc_query+0x598/0x5f0
drivers/media/usb/dvb-usb/technisat-usb2.c:679
Read of size 1 at addr ffff88805ee3d3a8 by task kworker/2:3/8681

CPU: 2 PID: 8681 Comm: kworker/2:3 Not tainted 4.20.0-rc2 #1
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS
Ubuntu-1.8.2-1ubuntu1 04/01/2014
Workqueue: events dvb_usb_read_remote_control
Call Trace:
 __dump_stack lib/dump_stack.c:77 [inline]
 dump_stack+0xd2/0x148 lib/dump_stack.c:113
 print_address_description+0x71/0x239 mm/kasan/report.c:256
 kasan_report_error mm/kasan/report.c:354 [inline]
 kasan_report.cold.5+0x242/0x30b mm/kasan/report.c:412
 __asan_report_load1_noabort+0x19/0x20 mm/kasan/report.c:430
 technisat_usb2_get_ir drivers/media/usb/dvb-usb/technisat-usb2.c:664

[inline]
 technisat_usb2_rc_query+0x598/0x5f0
drivers/media/usb/dvb-usb/technisat-usb2.c:679
 dvb_usb_read_remote_control+0xbd/0x150
drivers/media/usb/dvb-usb/dvb-usb-remote.c:261

 process_one_work+0x816/0x14d0 kernel/workqueue.c:2153
 worker_thread+0x9b/0xce0 kernel/workqueue.c:2296
 kthread+0x33d/0x400 kernel/kthread.c:246
 ret_from_fork+0x3a/0x50 arch/x86/entry/entry_64.S:352
Quoted 42 lines · Hui Peng

In the while loop of technisat_usb2_get_ir, it scans through a fix-sized buffer read from the device side, the termination condition of the loop is *b == 0xff. If no 0xff byte is read from the device side, OOB access happens.

This patch fixes the bug by adding an upper bound in the while loop.

---
 drivers/media/usb/dvb-usb/technisat-usb2.c | 5 +++++
 1 file changed, 5 insertions(+)
drivers/media/usb/dvb-usb/technisat-usb2.c +5 -0
index c659e18b358b..181f5f97af45 100644
@@ -612,6 +612,7 @@ static int technisat_usb2_get_ir(struct dvb_usb_device *d)
 	u8 *b;
 	int ret;
 	struct ir_raw_event ev;
+	int i = 0;
 
 	buf[0] = GET_IR_DATA_VENDOR_REQUEST;
 	buf[1] = 0x08;
@@ -656,11 +657,15 @@ static int technisat_usb2_get_ir(struct dvb_usb_device *d)
 
 	ev.pulse = 0;
 	while (1) {
+		// only `ret` bytes are read from the device side
+		if (i >= ret)
+			break;
 		ev.pulse = !ev.pulse;
 		ev.duration = (*b * FIRMWARE_CLOCK_DIVISOR * FIRMWARE_CLOCK_TICK) / 1000;
 		ir_raw_event_store(d->rc_dev, &ev);
 
 		b++;
+		i++;
 		if (*b == 0xff) {
 			ev.pulse = 0;
 			ev.duration = 888888*2;
#2 lore raw
In reply to Hui Peng
Quoted 6 lines · Hui Peng

In the while loop of technisat_usb2_get_ir, it scans through a fix-sized buffer read from the device side, the termination condition of the loop is *b == 0xff. If no 0xff byte is read from the device side, OOB access happens.

This patch fixes the bug by adding an upper bound in the while loop.

This issue was fixed in:

https://git.linuxtv.org/media_tree.git/commit/drivers/media/usb/dvb-usb/technisat-usb2.c?id=0c4df39e504bf925ab666132ac3c98d6cbbe380b

Note this limits the loop to the size of the buffer; using the ret return value might be better.

Sean

Quoted 38 lines
---
 drivers/media/usb/dvb-usb/technisat-usb2.c | 5 +++++
 1 file changed, 5 insertions(+)
drivers/media/usb/dvb-usb/technisat-usb2.c +5 -0
index c659e18b358b..181f5f97af45 100644
@@ -612,6 +612,7 @@ static int technisat_usb2_get_ir(struct dvb_usb_device *d)
 	u8 *b;
 	int ret;
 	struct ir_raw_event ev;
+	int i = 0;
 
 	buf[0] = GET_IR_DATA_VENDOR_REQUEST;
 	buf[1] = 0x08;
@@ -656,11 +657,15 @@ static int technisat_usb2_get_ir(struct dvb_usb_device *d)
 
 	ev.pulse = 0;
 	while (1) {
+		// only `ret` bytes are read from the device side
+		if (i >= ret)
+			break;
 		ev.pulse = !ev.pulse;
 		ev.duration = (*b * FIRMWARE_CLOCK_DIVISOR * FIRMWARE_CLOCK_TICK) / 1000;
 		ir_raw_event_store(d->rc_dev, &ev);
 
 		b++;
+		i++;
 		if (*b == 0xff) {
 			ev.pulse = 0;
 			ev.duration = 888888*2;
Signature · 1 line
2.23.0