In the Linux kernel, the following vulnerability has been resolved:
net: Remove RTNL dance for SIOCBRADDIF and SIOCBRDELIF.
SIOCBRDELIF is passed to dev_ioctl() first and later forwarded to br_ioctl_call(), which causes unnecessary RTNL dance and the splat below [0] under RTNL pressure.
Let's say Thread A is trying to detach a device from a bridge and Thread B is trying to remove the bridge.
In dev_ioctl(), Thread A bumps the bridge device's refcnt by netdev_hold() and releases RTNL because the following br_ioctl_call() also re-acquires RTNL.
In the race window, Thread B could acquire RTNL and try to remove the bridge device. Then, rtnl_unlock() by Thread B will release RTNL and wait for netdev_put() by Thread A.
Thread A, however, must hold RTNL after the unlock in dev_ifsioc(), which may take long under RTNL pressure, resulting in the splat by Thread B.
Thread A (SIOCBRDELIF) Thread B (SIOCBRDELBR)
sock_ioctl sock_ioctl
- sock_do_ioctl - br_ioctl_call
- dev_ioctl - br_ioctl_stub
|- rtnl_lock |
|- dev_ifsioc '
' |- dev = __dev_get_by_name(...)
|- netdev_hold(dev, ...) .
/ |- rtnl_unlock ------. |
| |- br_ioctl_call ---> |- rtnl_lock Race | | - br_ioctl_stub |- br_del_bridge
Window | | | |- dev = __dev_get_by_name(...)
| | | May take long | - br_dev_delete(dev, ...) | | | under RTNL pressure | - unregister_netdevice_queue(dev, ...)
| | | | - rtnl_unlock \ | |- rtnl_lock <-' - netdev_run_todo
| |- ... - netdev_run_todo | - rtnl_unlock |- __rtnl_unlock
|...