After refcnt reaches zero, vlan_vid_del() could free dev->vlan_info via RCU: RCU_INIT_POINTER(dev->vlan_info, NULL); call_rcu(&vlan_info->rcu, vlan_info_rcu_free); However, the pointer 'grp' still points to that memory since it is set before vlan_vid_del(): vlan_info = rtnl_dereference(dev->vlan_info); if (!vlan_info) goto out; grp = &vlan_info->grp; Depends on when that RCU callback is scheduled, we could trigger a use-after-free in vlan_group_for_each_dev() right following this vlan_vid_del(). Fix it by moving vlan_vid_del() before setting grp. This is also symmetric to the vlan_vid_add() we call in vlan_device_event().
Unified · Split
| @@ -376,6 +376,9 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event, · 375 unmodified lines | ||
| 376 | 376 | dev->name); |
| 377 | 377 | vlan_vid_add(dev, htons(ETH_P_8021Q), 0); |
| 378 | 378 | } |
| 379 | + if (event == NETDEV_DOWN && | |
| 380 | + (dev->features & NETIF_F_HW_VLAN_CTAG_FILTER)) | |
| 381 | + vlan_vid_del(dev, htons(ETH_P_8021Q), 0); | |
| 379 | 382 | |
| 380 | 383 | vlan_info = rtnl_dereference(dev->vlan_info); |
| 381 | 384 | if (!vlan_info) |
| @@ -423,9 +426,6 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event, · 41 unmodified lines | ||
| 423 | 426 | struct net_device *tmp; |
| 424 | 427 | LIST_HEAD(close_list); |
| 425 | 428 | |
| 426 | − if (dev->features & NETIF_F_HW_VLAN_CTAG_FILTER) | |
| 427 | − vlan_vid_del(dev, htons(ETH_P_8021Q), 0); | |
| 428 | − | |
| 429 | 429 | /* Put all VLANs for this dev in the down state too. */ |
| 430 | 430 | vlan_group_for_each_dev(grp, i, vlandev) { |
| 431 | 431 | flgs = vlandev->flags; |