* [PATCH v2 0/6] media: az6007/drxk/dvb-core: cope with a tuner unplugged while in use
@ 2026-10-04 10:07 Josef Schlehofer
2026-10-04 10:07 ` [PATCH v2 1/6] media: az6007: fix CAM status polling after disconnect Josef Schlehofer
` (5 more replies)
0 siblings, 6 replies; 7+ messages in thread
From: Josef Schlehofer @ 2026-10-04 10:07 UTC (permalink / raw)
To: Mauro Carvalho Chehab
Cc: linux-media, linux-kernel, Hyunwoo Kim, Hans Verkuil
Unplugging an az6007 based tuner while it is in use can leave parts of
the DVB stack stuck on the disconnected device:
- drxk keeps accessing the device and hides the resulting errors,
- the CA thread can stay stuck polling the removed CAM slot, which
keeps the disconnect from completing,
- userspace waiting on the DVB devices is neither woken up nor told
that the device is gone, so the disconnect can wait indefinitely for
it to close the devices.
This series fixes these issues by:
- treating an unreadable CAM slot status as no CAM,
- propagating -ENODEV from az6007 to drxk and stopping further device
access,
- waking up the users of the demux, dvr and CA devices and returning
-ENODEV or EPOLLERR to them.
Tested on:
- Linux 6.18.44 on a Turris 1.x (v1) and Linux 6.6.151 on a Turris
Omnia (v2, series backported), both with a TechniSat CableStar Combo
HD CI and a CAM,
- v7.3-rc2 in QEMU with vidtv and two vidtv fixes from media next [2]
(v1 and v2).
The tests covered streaming and scanning in tvheadend, blocked read(),
poll() and epoll_wait() calls, sysfs unbind, and unplugging the tuner
or the whole USB hub. In all of them the blocked calls returned and the
disconnect completed. Without the series, unbinding vidtv with a
blocked reader hung, which is the hang that the commit message of the
second vidtv fix [2] asks about.
Changes in v2:
- Patch 5: check dmxdev->exit in the readers instead of storing -ENODEV
in the buffer error, which a concurrent reader could clear, as
reported by Sashiko [1]. With a test-only delay, two readers of one
file reproduced that race in QEMU with v1 but not with v2.
- Patches 1-4 and 6 are unchanged.
v1: https://lore.kernel.org/r/20260923001410.30297-1-pepe.schlehofer@gmail.com
[1] https://linuxtv.org/mailman3/hyperkitty/list/media-ci@linuxtv.org/message/N3XJE5MO4HP2LJYOKFHIRB564HATZCTI/
[2] "media: vidtv: fix frontend reference leak on unbind" and
"media: vidtv: fix uaf in vidtv_bridge_on_new_pkts_avail"
Josef Schlehofer (6):
media: az6007: fix CAM status polling after disconnect
media: az6007: propagate USB errors from I2C transfers
media: drxk: stop retrying after disconnect
media: drxk: stop accessing a disconnected device
media: dvb-core: dmxdev: wake up readers on release
media: dvb-core: wake up CA users on release
drivers/media/dvb-core/dmxdev.c | 36 +++++++++++++++----
drivers/media/dvb-core/dvb_ca_en50221.c | 24 ++++++++++++-
drivers/media/dvb-frontends/drxk_hard.c | 48 +++++++++++++++++++------
drivers/media/dvb-frontends/drxk_hard.h | 2 +-
drivers/media/usb/dvb-usb-v2/az6007.c | 23 ++++++------
5 files changed, 105 insertions(+), 28 deletions(-)
base-commit: df2908090cda368b01ff43709f51890076c56157
--
2.54.0 (Apple Git-157)
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v2 1/6] media: az6007: fix CAM status polling after disconnect
2026-10-04 10:07 [PATCH v2 0/6] media: az6007/drxk/dvb-core: cope with a tuner unplugged while in use Josef Schlehofer
@ 2026-10-04 10:07 ` Josef Schlehofer
2026-10-04 10:07 ` [PATCH v2 2/6] media: az6007: propagate USB errors from I2C transfers Josef Schlehofer
` (4 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Josef Schlehofer @ 2026-10-04 10:07 UTC (permalink / raw)
To: Mauro Carvalho Chehab
Cc: linux-media, linux-kernel, Hyunwoo Kim, Hans Verkuil, stable
az6007_ci_poll_slot_status() returns -EIO when the status request fails.
dvb_ca_en50221 treats the return value as a bitmask, so -EIO is
interpreted as CAM_PRESENT|CAM_CHANGED. The CA state machine then
handles a CAM change for every failed poll, without sleeping in between.
After an unplug, every poll fails this way and the CA thread can spin
forever in dvb_ca_en50221_thread_state_machine(), preventing the USB
disconnect from completing. This happened when unplugging a TechniSat
CableStar Combo HD CI with a CAM inserted, and rcu_sched reported a
stall.
Report no CAM when the status request or buffer allocation fails, and
only inspect the status byte when the request returned data. A read
error while a CAM is present is then treated as a removal and the CAM is
reinitialised after the next successful poll.
Fixes: 962f8f67e486 ("[media] Add CI support to az6007 driver")
Cc: stable@vger.kernel.org
Signed-off-by: Josef Schlehofer <pepe.schlehofer@gmail.com>
---
drivers/media/usb/dvb-usb-v2/az6007.c | 16 +++++++++-------
1 file changed, 9 insertions(+), 7 deletions(-)
diff --git a/drivers/media/usb/dvb-usb-v2/az6007.c b/drivers/media/usb/dvb-usb-v2/az6007.c
index 65ef045b74ca..4ce1afe01c3b 100644
--- a/drivers/media/usb/dvb-usb-v2/az6007.c
+++ b/drivers/media/usb/dvb-usb-v2/az6007.c
@@ -526,7 +526,7 @@ static int az6007_ci_poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int o
b = kmalloc(12, GFP_KERNEL);
if (!b)
- return -ENOMEM;
+ return 0;
mutex_lock(&state->ca_mutex);
req = 0xC5;
@@ -535,16 +535,18 @@ static int az6007_ci_poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int o
blen = 1;
ret = az6007_read(d, req, value, index, b, blen);
- if (ret < 0) {
+ if (ret < 0)
pr_warn("usb in operation failed. (%d)\n", ret);
- ret = -EIO;
- } else
- ret = 0;
- if (!ret && b[0] == 1) {
+ /*
+ * The return value is a mask of DVB_CA_EN50221_POLL_* flags, not an
+ * error code, so report no CAM when the status cannot be read.
+ */
+ if (ret > 0 && b[0] == 1)
ret = DVB_CA_EN50221_POLL_CAM_PRESENT |
DVB_CA_EN50221_POLL_CAM_READY;
- }
+ else
+ ret = 0;
mutex_unlock(&state->ca_mutex);
kfree(b);
--
2.54.0 (Apple Git-157)
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v2 2/6] media: az6007: propagate USB errors from I2C transfers
2026-10-04 10:07 [PATCH v2 0/6] media: az6007/drxk/dvb-core: cope with a tuner unplugged while in use Josef Schlehofer
2026-10-04 10:07 ` [PATCH v2 1/6] media: az6007: fix CAM status polling after disconnect Josef Schlehofer
@ 2026-10-04 10:07 ` Josef Schlehofer
2026-10-04 10:07 ` [PATCH v2 3/6] media: drxk: stop retrying after disconnect Josef Schlehofer
` (3 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Josef Schlehofer @ 2026-10-04 10:07 UTC (permalink / raw)
To: Mauro Carvalho Chehab
Cc: linux-media, linux-kernel, Hyunwoo Kim, Hans Verkuil
__az6007_read(), __az6007_write() and az6007_i2c_xfer() currently map
usb_control_msg() failures to -EIO. After disconnect, USB reports
-ENODEV, but drxk cannot distinguish a gone device from a transfer
error.
Propagate the error reported by usb_control_msg() instead. Keep short
transfers mapped to -EIO as before.
This lets drxk stop retrying and accessing the device after disconnect.
Signed-off-by: Josef Schlehofer <pepe.schlehofer@gmail.com>
---
drivers/media/usb/dvb-usb-v2/az6007.c | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/drivers/media/usb/dvb-usb-v2/az6007.c b/drivers/media/usb/dvb-usb-v2/az6007.c
index 4ce1afe01c3b..5449ceda87e8 100644
--- a/drivers/media/usb/dvb-usb-v2/az6007.c
+++ b/drivers/media/usb/dvb-usb-v2/az6007.c
@@ -109,7 +109,7 @@ static int __az6007_read(struct usb_device *udev, u8 req, u16 value,
value, index, b, blen, 5000);
if (ret < 0) {
pr_warn("usb read operation failed. (%d)\n", ret);
- return -EIO;
+ return ret;
}
if (az6007_xfer_debug) {
@@ -163,7 +163,7 @@ static int __az6007_write(struct usb_device *udev, u8 req, u16 value,
value, index, b, blen, 5000);
if (ret != blen) {
pr_err("usb write operation failed. (%d)\n", ret);
- return -EIO;
+ return ret < 0 ? ret : -EIO;
}
return 0;
@@ -782,8 +782,9 @@ static int az6007_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
if (ret >= len) {
for (j = 0; j < len; j++)
msgs[i + 1].buf[j] = st->data[j + 5];
- } else
+ } else if (ret >= 0) {
ret = -EIO;
+ }
i++;
} else if (!(msgs[i].flags & I2C_M_RD)) {
/* write bytes */
--
2.54.0 (Apple Git-157)
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v2 3/6] media: drxk: stop retrying after disconnect
2026-10-04 10:07 [PATCH v2 0/6] media: az6007/drxk/dvb-core: cope with a tuner unplugged while in use Josef Schlehofer
2026-10-04 10:07 ` [PATCH v2 1/6] media: az6007: fix CAM status polling after disconnect Josef Schlehofer
2026-10-04 10:07 ` [PATCH v2 2/6] media: az6007: propagate USB errors from I2C transfers Josef Schlehofer
@ 2026-10-04 10:07 ` Josef Schlehofer
2026-10-04 10:07 ` [PATCH v2 4/6] media: drxk: stop accessing a disconnected device Josef Schlehofer
` (2 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Josef Schlehofer @ 2026-10-04 10:07 UTC (permalink / raw)
To: Mauro Carvalho Chehab
Cc: linux-media, linux-kernel, Hyunwoo Kim, Hans Verkuil
drxk's wait loops retry I2C transfers on every error. In
dvbt_sc_command(), read16() can leave cur_cmd unchanged when the read
fails, so the loop can either exit early or retry until the maximum when
the device has disappeared.
Stop retrying on -ENODEV and return the error instead of continuing with
the command. In power_up_device(), check the final transfer status after
the shortened loop.
-ENODEV is only returned here after a USB device has been disconnected,
so this does not change behaviour while the device is present.
Signed-off-by: Josef Schlehofer <pepe.schlehofer@gmail.com>
---
drivers/media/dvb-frontends/drxk_hard.c | 19 ++++++++++++-------
1 file changed, 12 insertions(+), 7 deletions(-)
diff --git a/drivers/media/dvb-frontends/drxk_hard.c b/drivers/media/dvb-frontends/drxk_hard.c
index 47786d0610f0..0815083e54eb 100644
--- a/drivers/media/dvb-frontends/drxk_hard.c
+++ b/drivers/media/dvb-frontends/drxk_hard.c
@@ -467,9 +467,9 @@ static int power_up_device(struct drxk_state *state)
continue;
status = i2c_read1(state, state->demod_address,
&data);
- } while (status < 0 &&
+ } while (status < 0 && status != -ENODEV &&
(retry_count < DRXK_MAX_RETRIES_POWERUP));
- if (status < 0 && retry_count >= DRXK_MAX_RETRIES_POWERUP)
+ if (status < 0)
goto error;
}
@@ -992,7 +992,8 @@ static int hi_command(struct drxk_state *state, u16 cmd, u16 *p_result)
retry_count += 1;
status = read16(state, SIO_HI_RA_RAM_CMD__A,
&wait_cmd);
- } while ((status < 0 || wait_cmd) && (retry_count < DRXK_MAX_RETRIES));
+ } while ((status < 0 || wait_cmd) && status != -ENODEV &&
+ (retry_count < DRXK_MAX_RETRIES));
if (status < 0)
goto error;
status = read16(state, SIO_HI_RA_RAM_RES__A, p_result);
@@ -3199,8 +3200,10 @@ static int dvbt_sc_command(struct drxk_state *state,
usleep_range(1000, 2000);
status = read16(state, OFDM_SC_RA_RAM_CMD__A, &cur_cmd);
retry_cnt++;
- } while ((cur_cmd != 0) && (retry_cnt < DRXK_MAX_RETRIES));
- if (retry_cnt >= DRXK_MAX_RETRIES && (status < 0))
+ } while ((cur_cmd != 0) && status != -ENODEV &&
+ (retry_cnt < DRXK_MAX_RETRIES));
+ if (status == -ENODEV ||
+ (retry_cnt >= DRXK_MAX_RETRIES && status < 0))
goto error;
/* Write sub-command */
@@ -3252,8 +3255,10 @@ static int dvbt_sc_command(struct drxk_state *state,
usleep_range(1000, 2000);
status = read16(state, OFDM_SC_RA_RAM_CMD__A, &cur_cmd);
retry_cnt++;
- } while ((cur_cmd != 0) && (retry_cnt < DRXK_MAX_RETRIES));
- if (retry_cnt >= DRXK_MAX_RETRIES && (status < 0))
+ } while ((cur_cmd != 0) && status != -ENODEV &&
+ (retry_cnt < DRXK_MAX_RETRIES));
+ if (status == -ENODEV ||
+ (retry_cnt >= DRXK_MAX_RETRIES && status < 0))
goto error;
/* Check for illegal cmd */
--
2.54.0 (Apple Git-157)
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v2 4/6] media: drxk: stop accessing a disconnected device
2026-10-04 10:07 [PATCH v2 0/6] media: az6007/drxk/dvb-core: cope with a tuner unplugged while in use Josef Schlehofer
` (2 preceding siblings ...)
2026-10-04 10:07 ` [PATCH v2 3/6] media: drxk: stop retrying after disconnect Josef Schlehofer
@ 2026-10-04 10:07 ` Josef Schlehofer
2026-10-04 10:07 ` [PATCH v2 5/6] media: dvb-core: dmxdev: wake up readers on release Josef Schlehofer
2026-10-04 10:07 ` [PATCH v2 6/6] media: dvb-core: wake up CA users " Josef Schlehofer
5 siblings, 0 replies; 7+ messages in thread
From: Josef Schlehofer @ 2026-10-04 10:07 UTC (permalink / raw)
To: Mauro Carvalho Chehab
Cc: linux-media, linux-kernel, Hyunwoo Kim, Hans Verkuil
drxk continues frontend status polling after its USB device disappears.
read_status() keeps issuing I2C accesses, and drxk_get_stats() ignores
the error from get_lock_status(), so callers see "no signal".
Enter DRXK_NO_DEV when an I2C transfer returns -ENODEV and reject
further hardware accesses in that state. Propagate -ENODEV from
drxk_get_stats() so read_status() reports the disconnect.
Also check the state after the tuner's gate control in
drxk_set_parameters() before continuing demodulator setup.
Signed-off-by: Josef Schlehofer <pepe.schlehofer@gmail.com>
---
drivers/media/dvb-frontends/drxk_hard.c | 29 ++++++++++++++++++++++---
drivers/media/dvb-frontends/drxk_hard.h | 2 +-
2 files changed, 27 insertions(+), 4 deletions(-)
diff --git a/drivers/media/dvb-frontends/drxk_hard.c b/drivers/media/dvb-frontends/drxk_hard.c
index 0815083e54eb..df5f5ea959ce 100644
--- a/drivers/media/dvb-frontends/drxk_hard.c
+++ b/drivers/media/dvb-frontends/drxk_hard.c
@@ -208,10 +208,27 @@ static void drxk_i2c_unlock(struct drxk_state *state)
static int drxk_i2c_transfer(struct drxk_state *state, struct i2c_msg *msgs,
unsigned len)
{
+ int status;
+
+ /* Don't touch the bus once the device is known to be gone */
+ if (state->m_drxk_state == DRXK_NO_DEV)
+ return -ENODEV;
+
if (state->drxk_i2c_exclusive_lock)
- return __i2c_transfer(state->i2c, msgs, len);
+ status = __i2c_transfer(state->i2c, msgs, len);
else
- return i2c_transfer(state->i2c, msgs, len);
+ status = i2c_transfer(state->i2c, msgs, len);
+
+ /*
+ * -ENODEV from the I2C adapter means that the device is gone for
+ * good, e.g. an unplugged USB bridge. Stop accessing it from now on.
+ */
+ if (status == -ENODEV) {
+ state->m_drxk_state = DRXK_NO_DEV;
+ pr_warn("device is gone, stopping all I2C access\n");
+ }
+
+ return status;
}
static int i2c_read1(struct drxk_state *state, u8 adr, u8 *val)
@@ -6319,6 +6336,10 @@ static int drxk_set_parameters(struct dvb_frontend *fe)
if (fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 0);
+ /* The device may have disappeared while the tuner was programmed */
+ if (state->m_drxk_state == DRXK_NO_DEV)
+ return -ENODEV;
+
old_delsys = state->props.delivery_system;
state->props = *p;
@@ -6487,7 +6508,9 @@ static int drxk_get_stats(struct dvb_frontend *fe)
/* get status */
state->fe_status = 0;
- get_lock_status(state, &stat);
+ status = get_lock_status(state, &stat);
+ if (status == -ENODEV)
+ return status;
if (stat == MPEG_LOCK)
state->fe_status |= 0x1f;
if (stat == FEC_LOCK)
diff --git a/drivers/media/dvb-frontends/drxk_hard.h b/drivers/media/dvb-frontends/drxk_hard.h
index a850a876deee..dc6ee0bb6854 100644
--- a/drivers/media/dvb-frontends/drxk_hard.h
+++ b/drivers/media/dvb-frontends/drxk_hard.h
@@ -106,7 +106,7 @@ enum e_drxk_state {
DRXK_DTV_STARTED,
DRXK_ATV_STARTED,
DRXK_POWERED_DOWN,
- DRXK_NO_DEV /* If drxk init failed */
+ DRXK_NO_DEV /* drxk init failed or device gone */
};
enum e_drxk_coef_array_index {
--
2.54.0 (Apple Git-157)
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v2 5/6] media: dvb-core: dmxdev: wake up readers on release
2026-10-04 10:07 [PATCH v2 0/6] media: az6007/drxk/dvb-core: cope with a tuner unplugged while in use Josef Schlehofer
` (3 preceding siblings ...)
2026-10-04 10:07 ` [PATCH v2 4/6] media: drxk: stop accessing a disconnected device Josef Schlehofer
@ 2026-10-04 10:07 ` Josef Schlehofer
2026-10-04 10:07 ` [PATCH v2 6/6] media: dvb-core: wake up CA users " Josef Schlehofer
5 siblings, 0 replies; 7+ messages in thread
From: Josef Schlehofer @ 2026-10-04 10:07 UTC (permalink / raw)
To: Mauro Carvalho Chehab
Cc: linux-media, linux-kernel, Hyunwoo Kim, Hans Verkuil, stable
dvb_dmxdev_release() sets dmxdev->exit and waits for open demux and DVR
files to close, but readers blocked in dvb_dmxdev_buffer_read() wait
only for data or buffer->error. Setting exit therefore does not wake a
blocked read or epoll wait. The release, for example on a USB
disconnect, then waits forever.
Wake the DVR and open filter wait queues on release, and make
dvb_dmxdev_buffer_read() also wait for exit and return -ENODEV once it
is set. Also return -ENODEV from dvb_demux_read() once exit is set, as
dvb_dvr_read() already does.
Check exit instead of storing -ENODEV in buffer->error. The buffer error
is cleared by the reader that reports it, and the section timeout and
ioctls such as DMX_STOP can overwrite or clear it. A second reader of
the same file could then miss the release and keep it waiting.
Blocking DMX_DQBUF waiters are not covered.
Fixes: 57861b432bda ("V4L/DVB (5511): Fix 2/3 for bug 7819: demux and dvr")
Cc: stable@vger.kernel.org
Signed-off-by: Josef Schlehofer <pepe.schlehofer@gmail.com>
---
v2: check dmxdev->exit in the readers instead of storing -ENODEV in the
buffer error. A reader could clear an -ENODEV set concurrently by the
release, as reported by Sashiko [1]. The section timeout and ioctls
such as DMX_STOP could also overwrite or clear it.
Pre-existing cases remain where the release waits for users that are
not woken up, such as a blocking DMX_DQBUF, or an ioctl that waits for
the filter mutex of a blocked read() while holding dmxdev->mutex. The
unlocked users check in dvb_dmxdev_release() also still races with a
concurrent close(), and waking the readers makes that race easier to
hit.
v1: https://lore.kernel.org/r/20260923001410.30297-6-pepe.schlehofer@gmail.com
[1] https://linuxtv.org/mailman3/hyperkitty/list/media-ci@linuxtv.org/message/N3XJE5MO4HP2LJYOKFHIRB564HATZCTI/
drivers/media/dvb-core/dmxdev.c | 36 +++++++++++++++++++++++++++------
1 file changed, 30 insertions(+), 6 deletions(-)
diff --git a/drivers/media/dvb-core/dmxdev.c b/drivers/media/dvb-core/dmxdev.c
index 6a825d9bae43..8a744a96bc08 100644
--- a/drivers/media/dvb-core/dmxdev.c
+++ b/drivers/media/dvb-core/dmxdev.c
@@ -50,7 +50,8 @@ static int dvb_dmxdev_buffer_write(struct dvb_ringbuffer *buf,
return dvb_ringbuffer_write(buf, src, len);
}
-static ssize_t dvb_dmxdev_buffer_read(struct dvb_ringbuffer *src,
+static ssize_t dvb_dmxdev_buffer_read(struct dmxdev *dmxdev,
+ struct dvb_ringbuffer *src,
int non_blocking, char __user *buf,
size_t count, loff_t *ppos)
{
@@ -75,10 +76,16 @@ static ssize_t dvb_dmxdev_buffer_read(struct dvb_ringbuffer *src,
ret = wait_event_interruptible(src->queue,
!dvb_ringbuffer_empty(src) ||
- (src->error != 0));
+ (src->error != 0) ||
+ dmxdev->exit);
if (ret < 0)
break;
+ if (dmxdev->exit) {
+ ret = -ENODEV;
+ break;
+ }
+
if (src->error) {
ret = src->error;
dvb_ringbuffer_flush(src);
@@ -274,7 +281,7 @@ static ssize_t dvb_dvr_read(struct file *file, char __user *buf, size_t count,
if (dmxdev->exit)
return -ENODEV;
- return dvb_dmxdev_buffer_read(&dmxdev->dvr_buffer,
+ return dvb_dmxdev_buffer_read(dmxdev, &dmxdev->dvr_buffer,
file->f_flags & O_NONBLOCK,
buf, count, ppos);
}
@@ -998,7 +1005,7 @@ static ssize_t dvb_dmxdev_read_sec(struct dmxdev_filter *dfil,
hcount = 3 + dfil->todo;
if (hcount > count)
hcount = count;
- result = dvb_dmxdev_buffer_read(&dfil->buffer,
+ result = dvb_dmxdev_buffer_read(dfil->dev, &dfil->buffer,
file->f_flags & O_NONBLOCK,
buf, hcount, ppos);
if (result < 0) {
@@ -1019,7 +1026,7 @@ static ssize_t dvb_dmxdev_read_sec(struct dmxdev_filter *dfil,
}
if (count > dfil->todo)
count = dfil->todo;
- result = dvb_dmxdev_buffer_read(&dfil->buffer,
+ result = dvb_dmxdev_buffer_read(dfil->dev, &dfil->buffer,
file->f_flags & O_NONBLOCK,
buf, count, ppos);
if (result < 0)
@@ -1035,13 +1042,17 @@ dvb_demux_read(struct file *file, char __user *buf, size_t count,
struct dmxdev_filter *dmxdevfilter = file->private_data;
int ret;
+ if (dmxdevfilter->dev->exit)
+ return -ENODEV;
+
if (mutex_lock_interruptible(&dmxdevfilter->mutex))
return -ERESTARTSYS;
if (dmxdevfilter->type == DMXDEV_TYPE_SEC)
ret = dvb_dmxdev_read_sec(dmxdevfilter, file, buf, count, ppos);
else
- ret = dvb_dmxdev_buffer_read(&dmxdevfilter->buffer,
+ ret = dvb_dmxdev_buffer_read(dmxdevfilter->dev,
+ &dmxdevfilter->buffer,
file->f_flags & O_NONBLOCK,
buf, count, ppos);
@@ -1457,8 +1468,21 @@ EXPORT_SYMBOL(dvb_dmxdev_init);
void dvb_dmxdev_release(struct dmxdev *dmxdev)
{
+ int i;
+
mutex_lock(&dmxdev->mutex);
dmxdev->exit = 1;
+
+ /*
+ * Wake up everyone blocked in read() or poll() on the demux and dvr
+ * devices, so that they get -ENODEV or EPOLLERR, close their file
+ * handles and let the waits below finish.
+ */
+ wake_up_all(&dmxdev->dvr_buffer.queue);
+ for (i = 0; i < dmxdev->filternum; i++) {
+ if (dmxdev->filter[i].state >= DMXDEV_STATE_ALLOCATED)
+ wake_up_all(&dmxdev->filter[i].buffer.queue);
+ }
mutex_unlock(&dmxdev->mutex);
if (dmxdev->dvbdev->users > 1) {
--
2.54.0 (Apple Git-157)
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v2 6/6] media: dvb-core: wake up CA users on release
2026-10-04 10:07 [PATCH v2 0/6] media: az6007/drxk/dvb-core: cope with a tuner unplugged while in use Josef Schlehofer
` (4 preceding siblings ...)
2026-10-04 10:07 ` [PATCH v2 5/6] media: dvb-core: dmxdev: wake up readers on release Josef Schlehofer
@ 2026-10-04 10:07 ` Josef Schlehofer
5 siblings, 0 replies; 7+ messages in thread
From: Josef Schlehofer @ 2026-10-04 10:07 UTC (permalink / raw)
To: Mauro Carvalho Chehab
Cc: linux-media, linux-kernel, Hyunwoo Kim, Hans Verkuil, stable
dvb_ca_en50221_release() sets ca->exit and waits for open users to close
the CA device, but read, write, poll and ioctl can continue and blocked
readers and pollers are not woken.
Return -ENODEV from CA read, write and ioctl operations and EPOLLERR
from poll after ca->exit is set, and wake the corresponding wait queues.
Keep the release wait so the existing lifetime protection remains in
place.
tvheadend keeps the CA device open and waits for it with epoll, so it
needs to observe the disconnect before it can close the device.
Fixes: 280a8ab81733 ("media: dvb-core: Fix use-after-free due to race condition at dvb_ca_en50221")
Cc: stable@vger.kernel.org
Signed-off-by: Josef Schlehofer <pepe.schlehofer@gmail.com>
---
The unlocked users check in dvb_ca_en50221_release() still races with
a concurrent close(), and waking the users makes that race easier to
hit.
drivers/media/dvb-core/dvb_ca_en50221.c | 24 +++++++++++++++++++++++-
1 file changed, 23 insertions(+), 1 deletion(-)
diff --git a/drivers/media/dvb-core/dvb_ca_en50221.c b/drivers/media/dvb-core/dvb_ca_en50221.c
index 1b91ebb8f667..d478f995d5a9 100644
--- a/drivers/media/dvb-core/dvb_ca_en50221.c
+++ b/drivers/media/dvb-core/dvb_ca_en50221.c
@@ -1353,6 +1353,9 @@ static int dvb_ca_en50221_io_do_ioctl(struct file *file,
dprintk("%s\n", __func__);
+ if (ca->exit)
+ return -ENODEV;
+
if (mutex_lock_interruptible(&ca->ioctl_mutex))
return -ERESTARTSYS;
@@ -1460,6 +1463,9 @@ static ssize_t dvb_ca_en50221_io_write(struct file *file,
dprintk("%s\n", __func__);
+ if (ca->exit)
+ return -ENODEV;
+
/*
* Incoming packet has a 2 byte header.
* hdr[0] = slot_id, hdr[1] = connection_id
@@ -1618,6 +1624,9 @@ static ssize_t dvb_ca_en50221_io_read(struct file *file, char __user *buf,
dprintk("%s\n", __func__);
+ if (ca->exit)
+ return -ENODEV;
+
/*
* Outgoing packet has a 2 byte header.
* hdr[0] = slot_id, hdr[1] = connection_id
@@ -1635,8 +1644,11 @@ static ssize_t dvb_ca_en50221_io_read(struct file *file, char __user *buf,
/* wait for some data */
status = wait_event_interruptible(ca->wait_queue,
dvb_ca_en50221_io_read_condition
- (ca, &result, &slot));
+ (ca, &result, &slot) ||
+ ca->exit);
}
+ if (ca->exit)
+ return -ENODEV;
if ((status < 0) || (result < 0)) {
if (result)
return result;
@@ -1819,6 +1831,9 @@ static __poll_t dvb_ca_en50221_io_poll(struct file *file, poll_table *wait)
poll_wait(file, &ca->wait_queue, wait);
+ if (ca->exit)
+ return EPOLLERR;
+
if (dvb_ca_en50221_io_read_condition(ca, &result, &slot) == 1)
mask |= EPOLLIN;
@@ -1965,6 +1980,13 @@ void dvb_ca_en50221_release(struct dvb_ca_en50221 *pubca)
ca->exit = 1;
mutex_unlock(&ca->remove_mutex);
+ /*
+ * Wake up everyone blocked in read() or poll() on the CA device, so
+ * that they see the error and close it. The wait below cannot finish
+ * before the last user has closed the device.
+ */
+ wake_up_interruptible_all(&ca->wait_queue);
+
if (ca->dvbdev->users < 1)
wait_event(ca->dvbdev->wait_queue,
ca->dvbdev->users == 1);
--
2.54.0 (Apple Git-157)
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-10-04 10:07 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-04 10:07 [PATCH v2 0/6] media: az6007/drxk/dvb-core: cope with a tuner unplugged while in use Josef Schlehofer
2026-10-04 10:07 ` [PATCH v2 1/6] media: az6007: fix CAM status polling after disconnect Josef Schlehofer
2026-10-04 10:07 ` [PATCH v2 2/6] media: az6007: propagate USB errors from I2C transfers Josef Schlehofer
2026-10-04 10:07 ` [PATCH v2 3/6] media: drxk: stop retrying after disconnect Josef Schlehofer
2026-10-04 10:07 ` [PATCH v2 4/6] media: drxk: stop accessing a disconnected device Josef Schlehofer
2026-10-04 10:07 ` [PATCH v2 5/6] media: dvb-core: dmxdev: wake up readers on release Josef Schlehofer
2026-10-04 10:07 ` [PATCH v2 6/6] media: dvb-core: wake up CA users " Josef Schlehofer
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®