diff mbox series

i40e: fix use-after-free in i40e_sync_filters_subtask()

Message ID 20211129135201.4097648-1-zhangrui182@huawei.com
State Accepted
Delegated to: Anthony Nguyen
Headers show
Series i40e: fix use-after-free in i40e_sync_filters_subtask() | expand

Commit Message

Rui Zhang Nov. 29, 2021, 1:52 p.m. UTC
From: Di Zhu <zhudi2@huawei.com>

Using ifconfig command to delete the ipv6 address will cause 
the i40e network card driver to delete its internal mac_filter and 
i40e_service_task kernel thread will concurrently access the mac_filter.
These two processes are not protected by lock
so causing the following use-after-free problems.

 print_address_description+0x70/0x360
 ? vprintk_func+0x5e/0xf0
 kasan_report+0x1b2/0x330
 i40e_sync_vsi_filters+0x4f0/0x1850 [i40e]
 i40e_sync_filters_subtask+0xe3/0x130 [i40e]
 i40e_service_task+0x195/0x24c0 [i40e]
 process_one_work+0x3f5/0x7d0
 worker_thread+0x61/0x6c0
 ? process_one_work+0x7d0/0x7d0
 kthread+0x1c3/0x1f0
 ? kthread_park+0xc0/0xc0
 ret_from_fork+0x35/0x40

Allocated by task 2279810:
 kasan_kmalloc+0xa0/0xd0
 kmem_cache_alloc_trace+0xf3/0x1e0
 i40e_add_filter+0x127/0x2b0 [i40e]
 i40e_add_mac_filter+0x156/0x190 [i40e]
 i40e_addr_sync+0x2d/0x40 [i40e]
 __hw_addr_sync_dev+0x154/0x210
 i40e_set_rx_mode+0x6d/0xf0 [i40e]
 __dev_set_rx_mode+0xfb/0x1f0
 __dev_mc_add+0x6c/0x90
 igmp6_group_added+0x214/0x230
 __ipv6_dev_mc_inc+0x338/0x4f0
 addrconf_join_solict.part.7+0xa2/0xd0
 addrconf_dad_work+0x500/0x980
 process_one_work+0x3f5/0x7d0
 worker_thread+0x61/0x6c0
 kthread+0x1c3/0x1f0
 ret_from_fork+0x35/0x40

Freed by task 2547073:
 __kasan_slab_free+0x130/0x180
 kfree+0x90/0x1b0
 __i40e_del_filter+0xa3/0xf0 [i40e]
 i40e_del_mac_filter+0xf3/0x130 [i40e]
 i40e_addr_unsync+0x85/0xa0 [i40e]
 __hw_addr_sync_dev+0x9d/0x210
 i40e_set_rx_mode+0x6d/0xf0 [i40e]
 __dev_set_rx_mode+0xfb/0x1f0
 __dev_mc_del+0x69/0x80
 igmp6_group_dropped+0x279/0x510
 __ipv6_dev_mc_dec+0x174/0x220
 addrconf_leave_solict.part.8+0xa2/0xd0
 __ipv6_ifa_notify+0x4cd/0x570
 ipv6_ifa_notify+0x58/0x80
 ipv6_del_addr+0x259/0x4a0
 inet6_addr_del+0x188/0x260
 addrconf_del_ifaddr+0xcc/0x130
 inet6_ioctl+0x152/0x190
 sock_do_ioctl+0xd8/0x2b0
 sock_ioctl+0x2e5/0x4c0
 do_vfs_ioctl+0x14e/0xa80
 ksys_ioctl+0x7c/0xa0
 __x64_sys_ioctl+0x42/0x50
 do_syscall_64+0x98/0x2c0
 entry_SYSCALL_64_after_hwframe+0x65/0xca

Signed-off-by: Di Zhu <zhudi2@huawei.com>
Signed-off-by: Rui Zhang <zhangrui182@huawei.com>
---
 drivers/net/ethernet/intel/i40e/i40e_main.c | 24 +++++++++++++++++++++
 1 file changed, 24 insertions(+)

Comments

G, GurucharanX Dec. 22, 2021, 4:24 a.m. UTC | #1
> -----Original Message-----
> From: Rui Zhang <zhangrui182@huawei.com>
> Sent: Monday, November 29, 2021 7:22 PM
> To: Brandeburg, Jesse <jesse.brandeburg@intel.com>; Nguyen, Anthony L
> <anthony.l.nguyen@intel.com>; intel-wired-lan@lists.osuosl.org
> Cc: rose.chen@huawei.com; yanan@huawei.com; zhudi2@huawei.com;
> zhangrui182@huawei.com
> Subject: [PATCH] i40e: fix use-after-free in i40e_sync_filters_subtask()
> 
> From: Di Zhu <zhudi2@huawei.com>
> 
> Using ifconfig command to delete the ipv6 address will cause the i40e network
> card driver to delete its internal mac_filter and i40e_service_task kernel
> thread will concurrently access the mac_filter.
> These two processes are not protected by lock so causing the following use-
> after-free problems.
> 
>  print_address_description+0x70/0x360
>  ? vprintk_func+0x5e/0xf0
>  kasan_report+0x1b2/0x330
>  i40e_sync_vsi_filters+0x4f0/0x1850 [i40e]
>  i40e_sync_filters_subtask+0xe3/0x130 [i40e]
>  i40e_service_task+0x195/0x24c0 [i40e]
>  process_one_work+0x3f5/0x7d0
>  worker_thread+0x61/0x6c0
>  ? process_one_work+0x7d0/0x7d0
>  kthread+0x1c3/0x1f0
>  ? kthread_park+0xc0/0xc0
>  ret_from_fork+0x35/0x40
> 
> Allocated by task 2279810:
>  kasan_kmalloc+0xa0/0xd0
>  kmem_cache_alloc_trace+0xf3/0x1e0
>  i40e_add_filter+0x127/0x2b0 [i40e]
>  i40e_add_mac_filter+0x156/0x190 [i40e]
>  i40e_addr_sync+0x2d/0x40 [i40e]
>  __hw_addr_sync_dev+0x154/0x210
>  i40e_set_rx_mode+0x6d/0xf0 [i40e]
>  __dev_set_rx_mode+0xfb/0x1f0
>  __dev_mc_add+0x6c/0x90
>  igmp6_group_added+0x214/0x230
>  __ipv6_dev_mc_inc+0x338/0x4f0
>  addrconf_join_solict.part.7+0xa2/0xd0
>  addrconf_dad_work+0x500/0x980
>  process_one_work+0x3f5/0x7d0
>  worker_thread+0x61/0x6c0
>  kthread+0x1c3/0x1f0
>  ret_from_fork+0x35/0x40
> 
> Freed by task 2547073:
>  __kasan_slab_free+0x130/0x180
>  kfree+0x90/0x1b0
>  __i40e_del_filter+0xa3/0xf0 [i40e]
>  i40e_del_mac_filter+0xf3/0x130 [i40e]
>  i40e_addr_unsync+0x85/0xa0 [i40e]
>  __hw_addr_sync_dev+0x9d/0x210
>  i40e_set_rx_mode+0x6d/0xf0 [i40e]
>  __dev_set_rx_mode+0xfb/0x1f0
>  __dev_mc_del+0x69/0x80
>  igmp6_group_dropped+0x279/0x510
>  __ipv6_dev_mc_dec+0x174/0x220
>  addrconf_leave_solict.part.8+0xa2/0xd0
>  __ipv6_ifa_notify+0x4cd/0x570
>  ipv6_ifa_notify+0x58/0x80
>  ipv6_del_addr+0x259/0x4a0
>  inet6_addr_del+0x188/0x260
>  addrconf_del_ifaddr+0xcc/0x130
>  inet6_ioctl+0x152/0x190
>  sock_do_ioctl+0xd8/0x2b0
>  sock_ioctl+0x2e5/0x4c0
>  do_vfs_ioctl+0x14e/0xa80
>  ksys_ioctl+0x7c/0xa0
>  __x64_sys_ioctl+0x42/0x50
>  do_syscall_64+0x98/0x2c0
>  entry_SYSCALL_64_after_hwframe+0x65/0xca
> 
> Signed-off-by: Di Zhu <zhudi2@huawei.com>
> Signed-off-by: Rui Zhang <zhangrui182@huawei.com>
> ---
>  drivers/net/ethernet/intel/i40e/i40e_main.c | 24 +++++++++++++++++++++
>  1 file changed, 24 insertions(+)
> 

Tested-by: Gurucharan G <gurucharanx.g@intel.com> (A Contingent worker at Intel)
diff mbox series

Patch

diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
index 8221c3364..11c1e9421 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_main.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
@@ -96,6 +96,24 @@  MODULE_VERSION(DRV_VERSION);
 
 static struct workqueue_struct *i40e_wq;
 
+static void netdev_hw_addr_refcnt(struct i40e_mac_filter *f,
+				  struct net_device *netdev, int delta)
+{
+	struct netdev_hw_addr *ha;
+
+	if (f == NULL || netdev == NULL)
+		return;
+
+	netdev_for_each_mc_addr(ha, netdev) {
+		if (ether_addr_equal(ha->addr, f->macaddr)) {
+			ha->refcount += delta;
+			if (ha->refcount <= 0)
+				ha->refcount = 1;
+			break;
+		}
+	}
+}
+
 /**
  * i40e_allocate_dma_mem_d - OS specific memory alloc for shared code
  * @hw:   pointer to the HW structure
@@ -1994,6 +2012,7 @@  static void i40e_undo_add_filter_entries(struct i40e_vsi *vsi,
 	hlist_for_each_entry_safe(new, h, from, hlist) {
 		/* We can simply free the wrapper structure */
 		hlist_del(&new->hlist);
+		netdev_hw_addr_refcnt(new->f, vsi->netdev, -1);
 		kfree(new);
 	}
 }
@@ -2330,6 +2349,10 @@  int i40e_sync_vsi_filters(struct i40e_vsi *vsi)
 						       &tmp_add_list,
 						       &tmp_del_list,
 						       vlan_filters);
+
+		hlist_for_each_entry(new, &tmp_add_list, hlist)
+			netdev_hw_addr_refcnt(new->f, vsi->netdev, 1);
+
 		if (retval)
 			goto err_no_memory_locked;
 
@@ -2462,6 +2485,7 @@  int i40e_sync_vsi_filters(struct i40e_vsi *vsi)
 			if (new->f->state == I40E_FILTER_NEW)
 				new->f->state = new->state;
 			hlist_del(&new->hlist);
+			netdev_hw_addr_refcnt(new->f, vsi->netdev, -1);
 			kfree(new);
 		}
 		spin_unlock_bh(&vsi->mac_filter_hash_lock);