[PATCH] media: az6007: fix WARNING in az6007_i2c_xfer from mutex reinitialization
From: Zhang Shurong
Date: Sat Sep 05 2026 - 05:51:29 EST
az6007_power_ctrl() calls mutex_init(&state->mutex) every time it takes
the !state->warm branch. When a control transfer fails before the device
is marked warm, the probe cleanup path invokes az6007_power_ctrl(d, 0),
which re-enters that branch and initializes the same mutex a second time.
That second mutex_init() can race with an I2C transfer. Once
dvb_usbv2_i2c_init() has registered the i2c adapter, userspace may open
/dev/i2c-N and hold state->mutex inside az6007_i2c_xfer() while blocked
in usb_control_msg(). Initializing a locked mutex clears its owner, so
the mutex_unlock() in az6007_i2c_xfer() then trips:
DEBUG_LOCKS_WARN_ON(__owner_task(owner) != get_current())
WARNING: kernel/locking/mutex.c at __mutex_unlock_slowpath
Move the mutex_init() into the driver's probe callback. The callback runs
once per probe, immediately after private data allocation and before
identify_state() and i2c adapter registration, at which point no user of
state->mutex can exist. This mirrors mxl111sf_probe(), which initializes
its state lock in the same callback.
Fixes: a2c35d346d9e ("[media] az6007: Protect read/write calls with a mutex")
Cc: stable@xxxxxxxxxxxxxxx
Reported-by: syzbot+4ac6df95b7f516179c07d6b8fcd77d81ec45e7f3@xxxxxxxxxxxxxxxxxxxxxxxxx
Closes: https://syzkaller.appspot.com/bug?id=4ac6df95b7f516179c07d6b8fcd77d81ec45e7f3
Signed-off-by: Zhang Shurong <zhang_shurong@xxxxxxxxxxx>
---
drivers/media/usb/dvb-usb-v2/az6007.c | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/drivers/media/usb/dvb-usb-v2/az6007.c b/drivers/media/usb/dvb-usb-v2/az6007.c
index 65ef045b74ca..60af125ae6dd 100644
--- a/drivers/media/usb/dvb-usb-v2/az6007.c
+++ b/drivers/media/usb/dvb-usb-v2/az6007.c
@@ -694,8 +694,6 @@ static int az6007_power_ctrl(struct dvb_usb_device *d, int onoff)
pr_debug("%s()\n", __func__);
if (!state->warm) {
- mutex_init(&state->mutex);
-
ret = az6007_write(d, AZ6007_POWER, 0, 2, NULL, 0);
if (ret < 0)
return ret;
@@ -889,12 +887,22 @@ static int az6007_download_firmware(struct dvb_usb_device *d,
return cypress_load_firmware(d->udev, fw, CYPRESS_FX2);
}
+static int az6007_probe(struct dvb_usb_device *d)
+{
+ struct az6007_device_state *state = d_to_priv(d);
+
+ mutex_init(&state->mutex);
+
+ return 0;
+}
+
/* DVB USB Driver stuff */
static struct dvb_usb_device_properties az6007_props = {
.driver_name = KBUILD_MODNAME,
.owner = THIS_MODULE,
.firmware = AZ6007_FIRMWARE,
+ .probe = az6007_probe,
.adapter_nr = adapter_nr,
.size_of_priv = sizeof(struct az6007_device_state),
.i2c_algo = &az6007_i2c_algo,
@@ -917,6 +925,7 @@ static struct dvb_usb_device_properties az6007_cablestar_hdci_props = {
.owner = THIS_MODULE,
.firmware = AZ6007_FIRMWARE,
+ .probe = az6007_probe,
.adapter_nr = adapter_nr,
.size_of_priv = sizeof(struct az6007_device_state),
.i2c_algo = &az6007_i2c_algo,
--
2.39.5