In the Linux kernel, the following vulnerability has been resolved:
net: dsa: free routing table on probe failure
If complete = true in dsatreesetup(), it means that we are the last switch of the tree which is successfully probing, and we should be setting up all switches from our probe path.
After "complete" becomes true, dsatreesetupcpuports() or any subsequent function may fail. If that happens, the entire tree setup is in limbo: the first N-1 switches have successfully finished probing (doing nothing but having allocated persistent memory in the tree's dst->ports, and maybe dst->rtable), and switch N failed to probe, ending the tree setup process before anything is tangible from the user's PoV.
If switch N fails to probe, its memory (ports) will be freed and removed from dst->ports. However, the dst->rtable elements pointing to its ports, as created by dsalinktouch(), will remain there, and will lead to use-after-free if dereferenced.
If dsatreesetupswitches() returns -EPROBEDEFER, which is entirely possible because that is where ds->ops->setup() is, we get a kasan report like this:
================================================================== BUG: KASAN: slab-use-after-free in mv88e6xxxsetupupstream_port+0x240/0x568 Read of size 8 at addr ffff000004f56020 by task kworker/u8:3/42
Call trace: _asanreportload8noabort+0x20/0x30 mv88e6xxxsetupupstreamport+0x240/0x568 mv88e6xxxsetup+0xebc/0x1eb0 dsaregisterswitch+0x1af4/0x2ae0 mv88e6xxxregisterswitch+0x1b8/0x2a8 mv88e6xxxprobe+0xc4c/0xf60 mdioprobe+0x78/0xb8 reallyprobe+0x2b8/0x5a8 _driverprobedevice+0x164/0x298 driverprobedevice+0x78/0x258 _deviceattach_driver+0x274/0x350
Allocated by task 42: _kasankmalloc+0x84/0xa0 _kmalloccachenoprof+0x298/0x490 dsaswitchtouchports+0x174/0x3d8 dsaregisterswitch+0x800/0x2ae0 mv88e6xxxregisterswitch+0x1b8/0x2a8 mv88e6xxxprobe+0xc4c/0xf60 mdioprobe+0x78/0xb8 reallyprobe+0x2b8/0x5a8 _driverprobedevice+0x164/0x298 driverprobedevice+0x78/0x258 _deviceattach_driver+0x274/0x350
Freed by task 42: _kasanslabfree+0x48/0x68 kfree+0x138/0x418 dsaregisterswitch+0x2694/0x2ae0 mv88e6xxxregisterswitch+0x1b8/0x2a8 mv88e6xxxprobe+0xc4c/0xf60 mdioprobe+0x78/0xb8 reallyprobe+0x2b8/0x5a8 _driverprobedevice+0x164/0x298 driverprobedevice+0x78/0x258 _deviceattachdriver+0x274/0x350
The simplest way to fix the bug is to delete the routing table in its entirety. dsatreesetuproutingtable() has no problem in regenerating it even if we deleted links between ports other than those of switch N, because dsalinktouch() first checks whether the port pair already exists in dst->rtable, allocating if not.
The deletion of the routing table in its entirety already exists in dsatreeteardown(), so refactor that into a function that can also be called from the tree setup error path.
In my analysis of the commit to blame, it is the one which added dsa_link elements to dst->rtable. Prior to that, each switch had its own ds->rtable which is freed when the switch fails to probe. But the tree is potentially persistent memory.