media: edia: dvbdev: fix a use-after-free
In dvb_register_device, *pdvbdev is set equal to dvbdev, which is freed
in several error-handling paths. However, *pdvbdev is not set to NULL
after dvbdev's deallocation, causing use-after-frees in many places,
for example, in the following call chain:
budget_register
|-> dvb_dmxdev_init
|-> dvb_register_device
|-> dvb_dmxdev_release
|-> dvb_unregister_device
|-> dvb_remove_device
|-> dvb_device_put
|-> kref_put
When calling dvb_unregister_device, dmxdev->dvbdev (i.e. *pdvbdev in
dvb_register_device) could point to memory that had been freed in
dvb_register_device. Thereafter, this pointer is transferred to
kref_put and triggering a use-after-free.
Link: https://lore.kernel.org/linux-media/20240203134046.3120099-1-alexious@zju.edu.cn
Fixes: b619010247
("V4L/DVB (5244): Dvbdev: fix illegal re-usage of fileoperations struct")
Signed-off-by: Zhipeng Lu <alexious@zju.edu.cn>
Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
This commit is contained in:
parent
9651d3d2d6
commit
8c64f4cdf4
@ -490,6 +490,7 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev,
|
|||||||
dvbdevfops = kmemdup(template->fops, sizeof(*dvbdevfops), GFP_KERNEL);
|
dvbdevfops = kmemdup(template->fops, sizeof(*dvbdevfops), GFP_KERNEL);
|
||||||
if (!dvbdevfops) {
|
if (!dvbdevfops) {
|
||||||
kfree(dvbdev);
|
kfree(dvbdev);
|
||||||
|
*pdvbdev = NULL;
|
||||||
mutex_unlock(&dvbdev_register_lock);
|
mutex_unlock(&dvbdev_register_lock);
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
@ -498,6 +499,7 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev,
|
|||||||
if (!new_node) {
|
if (!new_node) {
|
||||||
kfree(dvbdevfops);
|
kfree(dvbdevfops);
|
||||||
kfree(dvbdev);
|
kfree(dvbdev);
|
||||||
|
*pdvbdev = NULL;
|
||||||
mutex_unlock(&dvbdev_register_lock);
|
mutex_unlock(&dvbdev_register_lock);
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
@ -531,6 +533,7 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev,
|
|||||||
}
|
}
|
||||||
list_del(&dvbdev->list_head);
|
list_del(&dvbdev->list_head);
|
||||||
kfree(dvbdev);
|
kfree(dvbdev);
|
||||||
|
*pdvbdev = NULL;
|
||||||
up_write(&minor_rwsem);
|
up_write(&minor_rwsem);
|
||||||
mutex_unlock(&dvbdev_register_lock);
|
mutex_unlock(&dvbdev_register_lock);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
@ -553,6 +556,7 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev,
|
|||||||
dvb_media_device_free(dvbdev);
|
dvb_media_device_free(dvbdev);
|
||||||
list_del(&dvbdev->list_head);
|
list_del(&dvbdev->list_head);
|
||||||
kfree(dvbdev);
|
kfree(dvbdev);
|
||||||
|
*pdvbdev = NULL;
|
||||||
mutex_unlock(&dvbdev_register_lock);
|
mutex_unlock(&dvbdev_register_lock);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -571,6 +575,7 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev,
|
|||||||
dvb_media_device_free(dvbdev);
|
dvb_media_device_free(dvbdev);
|
||||||
list_del(&dvbdev->list_head);
|
list_del(&dvbdev->list_head);
|
||||||
kfree(dvbdev);
|
kfree(dvbdev);
|
||||||
|
*pdvbdev = NULL;
|
||||||
mutex_unlock(&dvbdev_register_lock);
|
mutex_unlock(&dvbdev_register_lock);
|
||||||
return PTR_ERR(clsdev);
|
return PTR_ERR(clsdev);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user