-
Missing Synchronization in dvb_usb_start_feed():
When a user process issues an ioctl like DMX_SET_PES_FILTER (0x40146f2c) on /dev/dvb/adapterX/demuxY, the demux subsystem triggers dvb_usb_start_feed().
At entry, dvb_usb_start_feed() waits only for the ADAP_INIT status bit:
wait_on_bit(&adap->state_bits, ADAP_INIT, TASK_UNINTERRUPTIBLE);
if (adap->active_fe == -1)
return -EINVAL;
Critically, dvb_usb_start_feed() does not wait for ADAP_SLEEP, does not acquire a lock protecting adap->active_fe, and does not cache adap->active_fe into a local variable.
-
Race with Concurrent Frontend Sleep (dvb_usb_fe_sleep()):
If the frontend device is concurrently closed or put to sleep, dvb_usb_fe_sleep() is called:
if (!adap->suspend_resume_active) {
set_bit(ADAP_SLEEP, &adap->state_bits);
wait_on_bit(&adap->state_bits, ADAP_STREAMING, TASK_UNINTERRUPTIBLE);
}
If dvb_usb_start_feed() has passed the adap->active_fe == -1 check (where active_fe was still 0) but has not yet called set_bit(ADAP_STREAMING, ...), wait_on_bit in dvb_usb_fe_sleep() immediately proceeds without blocking. dvb_usb_fe_sleep() powers off the device via dvb_usbv2_device_power_ctrl(d, 0) and executes line 623:
adap->active_fe = -1;
-
Unchecked Return Value from usb_urb_submitv2():
Concurrently, dvb_usb_start_feed() sets ADAP_STREAMING and attempts to submit URBs:
usb_urb_submitv2(&adap->stream, &stream_props);
Because the device was powered down or transitioning to sleep, usb_submit_urb() fails on URB 0, printing:
"dvb_usb_v2: could not submit urb no. 0 - get them all back"
usb_urb_submitv2() kills the stream and returns an error code (-ESHUTDOWN / -EPROTO / -ENODEV). However, dvb_usb_start_feed() ignores the return value of usb_urb_submitv2() and proceeds directly to line 308.
-
Out-of-Bounds Indexing adap->fe[-1]:
At line 308 in dvb_usb_core.c:
if (d->props->streaming_ctrl) {
ret = d->props->streaming_ctrl(adap->fe[adap->active_fe], 1);
Since adap->active_fe was re-read from memory after dvb_usb_fe_sleep() updated it, adap->active_fe is now -1.
adap->fe[adap->active_fe] evaluates to adap->fe[-1]. In struct dvb_usb_adapter:
struct dvb_adapter dvb_adap;
struct dmxdev dmxdev;
struct dvb_demux demux;
struct dvb_net dvb_net;
struct dvb_frontend *fe[MAX_NO_OF_FE_PER_ADAP];
The field preceding adap->fe[0] is adap->dvb_net. When CONFIG_DVB_NET is disabled, struct dvb_net is defined as:
struct dvb_net {
struct dvb_device *dvbdev;
};
sizeof(struct dvb_net) is exactly 8 bytes (sizeof(void *)), and its only member dvbdev is initialized to NULL. Consequently, adap->fe[-1] directly indexes adap->dvb_net.dvbdev and yields NULL (fe = 0x0).
-
NULL Pointer Dereference in ec168_streaming_ctrl():
d->props->streaming_ctrl (ec168_streaming_ctrl) is invoked with fe = NULL:
static int ec168_streaming_ctrl(struct dvb_frontend *fe, int onoff)
{
struct dvb_usb_device *d = fe_to_d(fe);
Where fe_to_d(fe) expands to adap_to_d((struct dvb_usb_adapter *) ((fe)->dvb->priv)).
In struct dvb_frontend, the dvb pointer member lies at byte offset 776 (0x308).
Dereferencing fe->dvb with fe == NULL attempts to read memory at address 0x0000000000000308. Under KASAN, checking shadow memory for 0x308 (0xdffffc0000000061) triggers a general protection fault (KASAN: null-ptr-deref in range [0x0000000000000308-0x000000000000030f]).