CVE-2017-8062 (C, published 2017-04-23T05:59Z)
Description: value: drivers/media/usb/dvb-usb/dw2102.c in the Linux kernel 4.9.x and 4.10.x before 4.10.4 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.
Severity: HIGH (CVSS v3 base score: 7.8)
Weakness: CWE-119 \u2014 Improper Restriction of Operations within the Bounds of a Memory Buffer
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.
Repository: https://github.com/torvalds/linux
Files changed:
drivers/media/usb/dvb-usb/dw2102.c
Commit message: [media] dw2102: don't do DMA on stack

On Kernel 4.9, WARNINGs about doing DMA on stack are hit at
the dw2102 driver: one in su3000_power_ctrl() and the other in tt_s2_4600_frontend_attach().

Both were due to the use of buffers on the stack as parameters to
dvb_usb_generic_rw() and the resulting attempt to do DMA with them.

The device was non-functional as a result.

So, switch this driver over to use a buffer within the device state
structure, as has been done with other DVB-USB drivers.

Tested with TechnoTrend TT-connect S2-4600.

[mchehab@osg.samsung.com: fixed a warning at su3000_i2c_transfer() that
 state var were dereferenced before check 'd']
Signed-off-by: Jonathan McDowell <noodles@earth.li>
Cc: <stable@vger.kernel.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>


Vulnerable code:
	u8 obuf[0x40], ibuf[0x40];
			obuf[0] = msg[0].buf[0] + 0x36;
			obuf[1] = 3;
			obuf[2] = 0;
			if (dvb_usb_generic_rw(d, obuf, 3, ibuf, 0, 0) < 0)
			obuf[0] = 0x10;
			if (dvb_usb_generic_rw(d, obuf, 1, ibuf, 2, 0) < 0)
			msg[0].buf[1] = ibuf[0];
			msg[0].buf[0] = ibuf[1];
			obuf[0] = 0x08;
			obuf[1] = msg[0].addr;
			obuf[2] = msg[0].len;
			memcpy(&obuf[3], msg[0].buf, msg[0].len);
			if (dvb_usb_generic_rw(d, obuf, msg[0].len + 3,
						ibuf, 1, 0) < 0)
		obuf[0] = 0x09;
		obuf[1] = msg[0].len;
		obuf[2] = msg[1].len;
		obuf[3] = msg[0].addr;
		memcpy(&obuf[4], msg[0].buf, msg[0].len);

		if (dvb_usb_generic_rw(d, obuf, msg[0].len + 4,
					ibuf, msg[1].len + 1, 0) < 0)
		memcpy(msg[1].buf, &ibuf[1], msg[1].len);
	u8 obuf[] = {0xde, 0};
		return dvb_usb_generic_rw(d, obuf, 2, NULL, 0, 0);
	return 0;
static int su3000_frontend_attach(struct dvb_usb_adapter *d)
	u8 obuf[3] = { 0xe, 0x80, 0 };
	u8 ibuf[] = { 0 };
	if (dvb_usb_generic_rw(d->dev, obuf, 3, ibuf, 1, 0) < 0)
	obuf[0] = 0xe;
	obuf[1] = 0x02;
	obuf[2] = 1;
	if (dvb_usb_generic_rw(d->dev, obuf, 3, ibuf, 1, 0) < 0)
	obuf[0] = 0xe;
	obuf[1] = 0x83;
	obuf[2] = 0;
	if (dvb_usb_generic_rw(d->dev, obuf, 3, ibuf, 1, 0) < 0)
	obuf[0] = 0xe;
	obuf[1] = 0x83;
	obuf[2] = 1;
	if (dvb_usb_generic_rw(d->dev, obuf, 3, ibuf, 1, 0) < 0)
	obuf[0] = 0x51;
	if (dvb_usb_generic_rw(d->dev, obuf, 1, ibuf, 1, 0) < 0)
	d->fe_adap[0].fe = dvb_attach(ds3000_attach, &su3000_ds3000_config,
					&d->dev->i2c_adap);
	if (d->fe_adap[0].fe == NULL)
	if (dvb_attach(ts2020_attach, d->fe_adap[0].fe,
				&d->dev->i2c_adap)) {
static int t220_frontend_attach(struct dvb_usb_adapter *d)
	u8 obuf[3] = { 0xe, 0x87, 0 };
	u8 ibuf[] = { 0 };
	if (dvb_usb_generic_rw(d->dev, obuf, 3, ibuf, 1, 0) < 0)
	obuf[0] = 0xe;
	obuf[1] = 0x86;
	obuf[2] = 1;
	if (dvb_usb_generic_rw(d->dev, obuf, 3, ibuf, 1, 0) < 0)
	obuf[0] = 0xe;
	obuf[1] = 0x80;
	obuf[2] = 0;
	if (dvb_usb_generic_rw(d->dev, obuf, 3, ibuf, 1, 0) < 0)
	obuf[0] = 0xe;
	obuf[1] = 0x80;
	obuf[2] = 1;
	if (dvb_usb_generic_rw(d->dev, obuf, 3, ibuf, 1, 0) < 0)
	obuf[0] = 0x51;
	if (dvb_usb_generic_rw(d->dev, obuf, 1, ibuf, 1, 0) < 0)
	d->fe_adap[0].fe = dvb_attach(cxd2820r_attach, &cxd2820r_config,
					&d->dev->i2c_adap, NULL);
	if (d->fe_adap[0].fe != NULL) {
		if (dvb_attach(tda18271_attach, d->fe_adap[0].fe, 0x60,
					&d->dev->i2c_adap, &tda18271_config)) {
static int m88rs2000_frontend_attach(struct dvb_usb_adapter *d)
	u8 obuf[] = { 0x51 };
	u8 ibuf[] = { 0 };
	if (dvb_usb_generic_rw(d->dev, obuf, 1, ibuf, 1, 0) < 0)
	d->fe_adap[0].fe = dvb_attach(m88rs2000_attach, &s421_m88rs2000_config,
					&d->dev->i2c_adap);
	if (d->fe_adap[0].fe == NULL)
	if (dvb_attach(ts2020_attach, d->fe_adap[0].fe,
				&d->dev->i2c_adap)) {
	u8 obuf[3] = { 0xe, 0x80, 0 };
	u8 ibuf[] = { 0 };
	if (dvb_usb_generic_rw(d, obuf, 3, ibuf, 1, 0) < 0)
	obuf[0] = 0xe;
	obuf[1] = 0x02;
	obuf[2] = 1;
	if (dvb_usb_generic_rw(d, obuf, 3, ibuf, 1, 0) < 0)
	obuf[0] = 0xe;
	obuf[1] = 0x83;
	obuf[2] = 0;
	if (dvb_usb_generic_rw(d, obuf, 3, ibuf, 1, 0) < 0)
	obuf[0] = 0xe;
	obuf[1] = 0x83;
	obuf[2] = 1;
	if (dvb_usb_generic_rw(d, obuf, 3, ibuf, 1, 0) < 0)
	obuf[0] = 0x51;
	if (dvb_usb_generic_rw(d, obuf, 1, ibuf, 1, 0) < 0)

Fixed code:
	u8 data[MAX_XFER_SIZE + 4];
	struct dw2102_state *state;

	state = d->priv;

	if (mutex_lock_interruptible(&d->data_mutex) < 0) {
		mutex_unlock(&d->i2c_mutex);
		return -EAGAIN;
	}
			state->data[0] = msg[0].buf[0] + 0x36;
			state->data[1] = 3;
			state->data[2] = 0;
			if (dvb_usb_generic_rw(d, state->data, 3,
					state->data, 0, 0) < 0)
			state->data[0] = 0x10;
			if (dvb_usb_generic_rw(d, state->data, 1,
					state->data, 2, 0) < 0)
			msg[0].buf[1] = state->data[0];
			msg[0].buf[0] = state->data[1];
			state->data[0] = 0x08;
			state->data[1] = msg[0].addr;
			state->data[2] = msg[0].len;
			memcpy(&state->data[3], msg[0].buf, msg[0].len);
			if (dvb_usb_generic_rw(d, state->data, msg[0].len + 3,
						state->data, 1, 0) < 0)
		state->data[0] = 0x09;
		state->data[1] = msg[0].len;
		state->data[2] = msg[1].len;
		state->data[3] = msg[0].addr;
		memcpy(&state->data[4], msg[0].buf, msg[0].len);

		if (dvb_usb_generic_rw(d, state->data, msg[0].len + 4,
					state->data, msg[1].len + 1, 0) < 0)
		memcpy(msg[1].buf, &state->data[1], msg[1].len);
	mutex_unlock(&d->data_mutex);
	int ret = 0;
		mutex_lock(&d->data_mutex);

		state->data[0] = 0xde;
		state->data[1] = 0;

		ret = dvb_usb_generic_rw(d, state->data, 2, NULL, 0, 0);
		mutex_unlock(&d->data_mutex);
	return ret;
static int su3000_frontend_attach(struct dvb_usb_adapter *adap)
	struct dvb_usb_device *d = adap->dev;
	struct dw2102_state *state = d->priv;

	mutex_lock(&d->data_mutex);

	state->data[0] = 0xe;
	state->data[1] = 0x80;
	state->data[2] = 0;
	if (dvb_usb_generic_rw(d, state->data, 3, state->data, 1, 0) < 0)
	state->data[0] = 0xe;
	state->data[1] = 0x02;
	state->data[2] = 1;
	if (dvb_usb_generic_rw(d, state->data, 3, state->data, 1, 0) < 0)
	state->data[0] = 0xe;
	state->data[1] = 0x83;
	state->data[2] = 0;
	if (dvb_usb_generic_rw(d, state->data, 3, state->data, 1, 0) < 0)
	state->data[0] = 0xe;
	state->data[1] = 0x83;
	state->data[2] = 1;
	if (dvb_usb_generic_rw(d, state->data, 3, state->data, 1, 0) < 0)
	state->data[0] = 0x51;
	if (dvb_usb_generic_rw(d, state->data, 1, state->data, 1, 0) < 0)
	mutex_unlock(&d->data_mutex);

	adap->fe_adap[0].fe = dvb_attach(ds3000_attach, &su3000_ds3000_config,
					&d->i2c_adap);
	if (adap->fe_adap[0].fe == NULL)
	if (dvb_attach(ts2020_attach, adap->fe_adap[0].fe,
				&d->i2c_adap)) {
static int t220_frontend_attach(struct dvb_usb_adapter *adap)
	struct dvb_usb_device *d = adap->dev;
	struct dw2102_state *state = d->priv;

	mutex_lock(&d->data_mutex);
	state->data[0] = 0xe;
	state->data[1] = 0x87;
	state->data[2] = 0x0;

	if (dvb_usb_generic_rw(d, state->data, 3, state->data, 1, 0) < 0)
	state->data[0] = 0xe;
	state->data[1] = 0x86;
	state->data[2] = 1;
	if (dvb_usb_generic_rw(d, state->data, 3, state->data, 1, 0) < 0)
	state->data[0] = 0xe;
	state->data[1] = 0x80;
	state->data[2] = 0;
	if (dvb_usb_generic_rw(d, state->data, 3, state->data, 1, 0) < 0)
	state->data[0] = 0xe;
	state->data[1] = 0x80;
	state->data[2] = 1;
	if (dvb_usb_generic_rw(d, state->data, 3, state->data, 1, 0) < 0)
	state->data[0] = 0x51;
	if (dvb_usb_generic_rw(d, state->data, 1, state->data, 1, 0) < 0)
	mutex_unlock(&d->data_mutex);

	adap->fe_adap[0].fe = dvb_attach(cxd2820r_attach, &cxd2820r_config,
					&d->i2c_adap, NULL);
	if (adap->fe_adap[0].fe != NULL) {
		if (dvb_attach(tda18271_attach, adap->fe_adap[0].fe, 0x60,
					&d->i2c_adap, &tda18271_config)) {
static int m88rs2000_frontend_attach(struct dvb_usb_adapter *adap)
	struct dvb_usb_device *d = adap->dev;
	struct dw2102_state *state = d->priv;

	mutex_lock(&d->data_mutex);
	state->data[0] = 0x51;

	if (dvb_usb_generic_rw(d, state->data, 1, state->data, 1, 0) < 0)
	mutex_unlock(&d->data_mutex);
	adap->fe_adap[0].fe = dvb_attach(m88rs2000_attach,
					&s421_m88rs2000_config,
					&d->i2c_adap);

	if (adap->fe_adap[0].fe == NULL)
	if (dvb_attach(ts2020_attach, adap->fe_adap[0].fe,
				&d->i2c_adap)) {
	mutex_lock(&d->data_mutex);

	state->data[0] = 0xe;
	state->data[1] = 0x80;
	state->data[2] = 0x0;

	if (dvb_usb_generic_rw(d, state->data, 3, state->data, 1, 0) < 0)
	state->data[0] = 0xe;
	state->data[1] = 0x02;
	state->data[2] = 1;
	if (dvb_usb_generic_rw(d, state->data, 3, state->data, 1, 0) < 0)
	state->data[0] = 0xe;
	state->data[1] = 0x83;
	state->data[2] = 0;
	if (dvb_usb_generic_rw(d, state->data, 3, state->data, 1, 0) < 0)
	state->data[0] = 0xe;
	state->data[1] = 0x83;
	state->data[2] = 1;
	if (dvb_usb_generic_rw(d, state->data, 3, state->data, 1, 0) < 0)
	state->data[0] = 0x51;
	if (dvb_usb_generic_rw(d, state->data, 1, state->data, 1, 0) < 0)
	mutex_unlock(&d->data_mutex);