CVE-2025-22111

Source
https://nvd.nist.gov/vuln/detail/CVE-2025-22111
Import Source
https://storage.googleapis.com/cve-osv-conversion/osv-output/CVE-2025-22111.json
JSON Data
https://api.osv.dev/v1/vulns/CVE-2025-22111
Downstream
Related
Published
2025-04-16T15:16:05Z
Modified
2025-08-30T18:00:20Z
Summary
[none]
Details

In the Linux kernel, the following vulnerability has been resolved:

net: Remove RTNL dance for SIOCBRADDIF and SIOCBRDELIF.

SIOCBRDELIF is passed to devioctl() first and later forwarded to brioctl_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 devioctl(), Thread A bumps the bridge device's refcnt by netdevhold() and releases RTNL because the following brioctlcall() also re-acquires RTNL.

In the race window, Thread B could acquire RTNL and try to remove the bridge device. Then, rtnlunlock() by Thread B will release RTNL and wait for netdevput() 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) ---------------------- ---------------------- sockioctl sockioctl - sock_do_ioctl- brioctlcall - dev_ioctl- brioctlstub |- rtnllock | |- devifsioc ' ' |- dev = _devgetbyname(...) |- netdevhold(dev, ...) . / |- rtnlunlock ------. | | |- brioctlcall ---> |- rtnl_lock Race | |- brioctlstub |- brdelbridge Window | | | |- dev = _devgetbyname(...) | | | May take long | - br_dev_delete(dev, ...) | | | under RTNL pressure |- unregisternetdevicequeue(dev, ...) | | | | - rtnl_unlock \ | |- rtnl_lock <-'- netdevruntodo | |- ... - netdev_run_todo |- rtnlunlock |- _rtnlunlock | |- netdevwaitallrefsany |- netdev_put(dev, ...) <----------------' Wait refcnt decrement and log splat below

To avoid blocking SIOCBRDELBR unnecessarily, let's not call dev_ioctl() for SIOCBRADDIF and SIOCBRDELIF.

In the dev_ioctl() path, we do the following:

  1. Copy struct ifreq by getuserifreq in sockdoioctl()
  2. Check CAPNETADMIN in dev_ioctl()
  3. Call devload() in devioctl()
  4. Fetch the master dev from ifr.ifrname in devifsioc()

    1. can be done by requestmodule() in brioctlcall(), so we move 1., 2., and 4. to brioctl_stub().

Note that 2. is also checked later in adddelif(), but it's better performed before RTNL.

SIOCBRADDIF and SIOCBRDELIF have been processed in dev_ioctl() since the pre-git era, and there seems to be no specific reason to process them there.

reftracker: wpan3@ffff8880662d8608 has 1/1 users at _netdevtrackeralloc include/linux/netdevice.h:4282 [inline] netdevhold include/linux/netdevice.h:4311 [inline] devifsioc+0xc6a/0x1160 net/core/devioctl.c:624 devioctl+0x255/0x10c0 net/core/devioctl.c:826 sockdoioctl+0x1ca/0x260 net/socket.c:1213 sockioctl+0x23a/0x6c0 net/socket.c:1318 vfsioctl fs/ioctl.c:51 [inline] _dosysioctl fs/ioctl.c:906 [inline] _sesysioctl fs/ioctl.c:892 [inline] _x64sysioctl+0x1a4/0x210 fs/ioctl.c:892 dosyscallx64 arch/x86/entry/common.c:52 [inline] dosyscall64+0xcb/0x250 arch/x86/entry/common.c:83 entrySYSCALL64afterhwframe+0x77/0x7f

References

Affected packages