CVE-2023-53580CVE-2023-53580是Linux内核USB Gadget核心子系统中存在的一个高可用性影响漏洞。该漏洞由Avichal Rakesh报告,涉及USB Gadget框架在UVC(USB Video Class)gadget驱动从gadget配置中移除时触发的内核panic问题。
在Linux内核的USB Gadget核心中,gadget_unbind_driver()函数在调用驱动的->unbind()回调时持有udc->connect_lock互斥锁。然而,当gadget驱动的unbind过程中调用usb_gadget_deactivate()函数时,该函数会尝试获取同一把互斥锁,从而导致死锁问题。这种死锁最终会引发内核panic,造成系统不可用。
该漏洞的根本原因在于代码设计上的不一致性:->bind()回调在调用时不持有互斥锁,而->unbind()回调却持有互斥锁。这种不一致性使得在unbind过程中调用usb_gadget_deactivate()或usb_gadget_activate()成为不可能,因为这些函数需要在进程上下文中运行并获取connect_lock。
该漏洞的CVSS评分为5.5,属于中危级别。虽然利用条件需要本地低权限访问且无需用户交互,但由于其可能导致系统完全不可用(高可用性影响),仍然是一个需要关注的安全问题。漏洞影响范围涵盖多个Linux内核稳定版本,需要通过内核补丁进行修复。
从技术层面分析,该漏洞的核心问题在于USB Gadget核心代码中的锁管理不当。
漏洞原理:
在drivers/usb/gadget/udc/core.c中,gadget_unbind_driver()函数的实现存在锁顺序问题。该函数在调用driver->unbind()回调前后都持有udc->connect_lock互斥锁。然而,usb_gadget_deactivate()函数内部需要获取同一个互斥锁(udc->connect_lock),以确保在停用gadget期间不会有并发的连接操作。
当UVC gadget驱动的unbind回调中调用usb_gadget_deactivate()时,会形成自死锁:
1. gadget_unbind_driver()获取connect_lock
2. 调用driver->unbind()
3. unbind()内部调用usb_gadget_deactivate()
4. usb_gadget_deactivate()尝试获取connect_lock
5. 由于该锁已被持有,线程进入等待状态,形成死锁
利用方式:
该漏洞的触发需要满足以下条件:
1. 系统使用USB Gadget框架(常见于嵌入式设备、Android设备等)
2. 配置了UVC功能(将设备模拟为USB摄像头)
3. 本地低权限用户能够触发UVC gadget驱动的解绑操作
4. 在解绑过程中,驱动代码调用了usb_gadget_deactivate()
修复方案:
内核开发者采用的修复方案是在gadget_unbind_driver()中调用->unbind()回调之前释放connect_lock互斥锁,调用完成后再重新获取。这样既保持了调用->bind()和->unbind()时的一致性(都不持有锁),又避免了死锁问题。同时,在usb_gadget_activate()和usb_gadget_deactivate()函数中添加了注释,说明这两个函数必须在进程上下文中调用,不能从可能运行在中断上下文中的->disconnect()回调中调用。