Commit dccc0c3d authored by Sean Young's avatar Sean Young Committed by Hans Verkuil
Browse files

media: rc: fix race between unregister and urb/irq callbacks



Some rc device drivers have a race condition between rc_unregister_device()
and irq or urb callbacks. This is because rc_unregister_device() does two
things, it marks the device as unregistered so no new commands can be
issued and then it calls rc_free_device(). This means the driver has no
chance to cancel any pending urb callbacks or interrupts after the device
has been marked as unregistered. Those callbacks may access struct rc_dev
or its members (e.g. struct ir_raw_event_ctrl), which have been freed by
rc_free_device().

This change removes the implicit call to rc_free_device() from
rc_unregister_device(). This means that device drivers can call
rc_unregister_device() in their remove or disconnect function, then cancel
all the urbs and interrupts before explicitly calling rc_free_device().

Note this is an alternative fix for an issue found by Haotian Zhang, see
the Closes: tags.

Reported-by: default avatarHaotian Zhang <vulab@iscas.ac.cn>
Closes: https://lore.kernel.org/linux-media/20251114101432.2566-1-vulab@iscas.ac.cn/
Closes: https://lore.kernel.org/linux-media/20251114101418.2548-1-vulab@iscas.ac.cn/
Closes: https://lore.kernel.org/linux-media/20251114101346.2530-1-vulab@iscas.ac.cn/
Closes: https://lore.kernel.org/linux-media/20251114090605.2413-1-vulab@iscas.ac.cn/


Reviewed-by: default avatarPatrice Chotard <patrice.chotard@foss.st.com>
Signed-off-by: default avatarSean Young <sean@mess.org>
Signed-off-by: default avatarHans Verkuil <hverkuil+cisco@kernel.org>
parent a5dcbff7
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -2221,6 +2221,7 @@ static void sii8620_detach(struct drm_bridge *bridge)
		return;

	rc_unregister_device(ctx->rc_dev);
	rc_free_device(ctx->rc_dev);
}

static int sii8620_is_packing_required(struct sii8620 *ctx,
+1 −0
Original line number Diff line number Diff line
@@ -134,5 +134,6 @@ void picolcd_exit_cir(struct picolcd_data *data)

	data->rc_dev = NULL;
	rc_unregister_device(rdev);
	rc_free_device(rdev);
}
+1 −1
Original line number Diff line number Diff line
@@ -338,8 +338,8 @@ int cec_register_adapter(struct cec_adapter *adap,
	res = cec_devnode_register(&adap->devnode, adap->owner);
	if (res) {
#ifdef CONFIG_MEDIA_CEC_RC
		/* Note: rc_unregister also calls rc_free */
		rc_unregister_device(adap->rc);
		rc_free_device(adap->rc);
		adap->rc = NULL;
#endif
		return res;
+1 −0
Original line number Diff line number Diff line
@@ -92,6 +92,7 @@ int sms_ir_init(struct smscore_device_t *coredev)
void sms_ir_exit(struct smscore_device_t *coredev)
{
	rc_unregister_device(coredev->ir.dev);
	rc_free_device(coredev->ir.dev);

	pr_debug("\n");
}
+2 −0
Original line number Diff line number Diff line
@@ -355,6 +355,7 @@ static void ir_work(struct work_struct *work)
		mutex_unlock(&ir->lock);
		if (rc == -ENODEV) {
			rc_unregister_device(ir->rc);
			rc_free_device(ir->rc);
			ir->rc = NULL;
			return;
		}
@@ -972,6 +973,7 @@ static void ir_remove(struct i2c_client *client)
	i2c_unregister_device(ir->tx_c);

	rc_unregister_device(ir->rc);
	rc_free_device(ir->rc);
}

static const struct i2c_device_id ir_kbd_id[] = {
Loading