| From foo@baz Tue Nov 21 15:37:44 CET 2017 |
| From: Cong Wang <xiyou.wangcong@gmail.com> |
| Date: Thu, 9 Nov 2017 16:43:13 -0800 |
| Subject: vlan: fix a use-after-free in vlan_device_event() |
| |
| From: Cong Wang <xiyou.wangcong@gmail.com> |
| |
| |
| [ Upstream commit 052d41c01b3a2e3371d66de569717353af489d63 ] |
| |
| 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(). |
| |
| Reported-by: Fengguang Wu <fengguang.wu@intel.com> |
| Fixes: efc73f4bbc23 ("net: Fix memory leak - vlan_info struct") |
| Cc: Alexander Duyck <alexander.duyck@gmail.com> |
| Cc: Linus Torvalds <torvalds@linux-foundation.org> |
| Cc: Girish Moodalbail <girish.moodalbail@oracle.com> |
| Signed-off-by: Cong Wang <xiyou.wangcong@gmail.com> |
| Reviewed-by: Girish Moodalbail <girish.moodalbail@oracle.com> |
| Tested-by: Fengguang Wu <fengguang.wu@intel.com> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| net/8021q/vlan.c | 6 +++--- |
| 1 file changed, 3 insertions(+), 3 deletions(-) |
| |
| --- a/net/8021q/vlan.c |
| +++ b/net/8021q/vlan.c |
| @@ -376,6 +376,9 @@ static int vlan_device_event(struct noti |
| dev->name); |
| vlan_vid_add(dev, htons(ETH_P_8021Q), 0); |
| } |
| + if (event == NETDEV_DOWN && |
| + (dev->features & NETIF_F_HW_VLAN_CTAG_FILTER)) |
| + vlan_vid_del(dev, htons(ETH_P_8021Q), 0); |
| |
| vlan_info = rtnl_dereference(dev->vlan_info); |
| if (!vlan_info) |
| @@ -420,9 +423,6 @@ static int vlan_device_event(struct noti |
| break; |
| |
| case NETDEV_DOWN: |
| - if (dev->features & NETIF_F_HW_VLAN_CTAG_FILTER) |
| - vlan_vid_del(dev, htons(ETH_P_8021Q), 0); |
| - |
| /* Put all VLANs for this dev in the down state too. */ |
| vlan_group_for_each_dev(grp, i, vlandev) { |
| flgs = vlandev->flags; |