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 | dev->name); | 376 | dev->name); |
| 377 | vlan_vid_add(dev, htons(ETH_P_8021Q), 0); | 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 | vlan_info = rtnl_dereference(dev->vlan_info); | 383 | vlan_info = rtnl_dereference(dev->vlan_info); |
| 381 | if (!vlan_info) | 384 | if (!vlan_info) |
| @@ -423,9 +426,6 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event, · 41 unmodified lines | |||
| 423 | struct net_device *tmp; | 426 | struct net_device *tmp; |
| 424 | LIST_HEAD(close_list); | 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 | /* Put all VLANs for this dev in the down state too. */ | 429 | /* Put all VLANs for this dev in the down state too. */ |
| 430 | vlan_group_for_each_dev(grp, i, vlandev) { | 430 | vlan_group_for_each_dev(grp, i, vlandev) { |
| 431 | flgs = vlandev->flags; | 431 | flgs = vlandev->flags; |