| From 9c17b6e2262e08ccb55909b5427759652734f928 Mon Sep 17 00:00:00 2001 |
| From: Feras Daoud <ferasda@mellanox.com> |
| Date: Thu, 24 Jan 2019 14:33:19 +0200 |
| Subject: IB/ipoib: Fix for use-after-free in ipoib_cm_tx_start |
| |
| [ Upstream commit 6ab4aba00f811a5265acc4d3eb1863bb3ca60562 ] |
| |
| The following BUG was reported by kasan: |
| |
| BUG: KASAN: use-after-free in ipoib_cm_tx_start+0x430/0x1390 [ib_ipoib] |
| Read of size 80 at addr ffff88034c30bcd0 by task kworker/u16:1/24020 |
| |
| Workqueue: ipoib_wq ipoib_cm_tx_start [ib_ipoib] |
| Call Trace: |
| dump_stack+0x9a/0xeb |
| print_address_description+0xe3/0x2e0 |
| kasan_report+0x18a/0x2e0 |
| ? ipoib_cm_tx_start+0x430/0x1390 [ib_ipoib] |
| memcpy+0x1f/0x50 |
| ipoib_cm_tx_start+0x430/0x1390 [ib_ipoib] |
| ? kvm_clock_read+0x1f/0x30 |
| ? ipoib_cm_skb_reap+0x610/0x610 [ib_ipoib] |
| ? __lock_is_held+0xc2/0x170 |
| ? process_one_work+0x880/0x1960 |
| ? process_one_work+0x912/0x1960 |
| process_one_work+0x912/0x1960 |
| ? wq_pool_ids_show+0x310/0x310 |
| ? lock_acquire+0x145/0x440 |
| worker_thread+0x87/0xbb0 |
| ? process_one_work+0x1960/0x1960 |
| kthread+0x314/0x3d0 |
| ? kthread_create_worker_on_cpu+0xc0/0xc0 |
| ret_from_fork+0x3a/0x50 |
| |
| Allocated by task 0: |
| kasan_kmalloc+0xa0/0xd0 |
| kmem_cache_alloc_trace+0x168/0x3e0 |
| path_rec_create+0xa2/0x1f0 [ib_ipoib] |
| ipoib_start_xmit+0xa98/0x19e0 [ib_ipoib] |
| dev_hard_start_xmit+0x159/0x8d0 |
| sch_direct_xmit+0x226/0xb40 |
| __dev_queue_xmit+0x1d63/0x2950 |
| neigh_update+0x889/0x1770 |
| arp_process+0xc47/0x21f0 |
| arp_rcv+0x462/0x760 |
| __netif_receive_skb_core+0x1546/0x2da0 |
| netif_receive_skb_internal+0xf2/0x590 |
| napi_gro_receive+0x28e/0x390 |
| ipoib_ib_handle_rx_wc_rss+0x873/0x1b60 [ib_ipoib] |
| ipoib_rx_poll_rss+0x17d/0x320 [ib_ipoib] |
| net_rx_action+0x427/0xe30 |
| __do_softirq+0x28e/0xc42 |
| |
| Freed by task 26680: |
| __kasan_slab_free+0x11d/0x160 |
| kfree+0xf5/0x360 |
| ipoib_flush_paths+0x532/0x9d0 [ib_ipoib] |
| ipoib_set_mode_rss+0x1ad/0x560 [ib_ipoib] |
| set_mode+0xc8/0x150 [ib_ipoib] |
| kernfs_fop_write+0x279/0x440 |
| __vfs_write+0xd8/0x5c0 |
| vfs_write+0x15e/0x470 |
| ksys_write+0xb8/0x180 |
| do_syscall_64+0x9b/0x420 |
| entry_SYSCALL_64_after_hwframe+0x49/0xbe |
| |
| The buggy address belongs to the object at ffff88034c30bcc8 |
| which belongs to the cache kmalloc-512 of size 512 |
| The buggy address is located 8 bytes inside of |
| 512-byte region [ffff88034c30bcc8, ffff88034c30bec8) |
| The buggy address belongs to the page: |
| |
| The following race between change mode and xmit flow is the reason for |
| this use-after-free: |
| |
| Change mode Send packet 1 to GID XX Send packet 2 to GID XX |
| | | | |
| start | | |
| | | | |
| | | | |
| | Create new path for GID XX | |
| | and update neigh path | |
| | | | |
| | | | |
| | | | |
| flush_paths | | |
| | | |
| queue_work(cm.start_task) | |
| | Path for GID XX not found |
| | create new path |
| | |
| | |
| start_task runs with old |
| released path |
| |
| There is no locking to protect the lifetime of the path through the |
| ipoib_cm_tx struct, so delete it entirely and always use the newly looked |
| up path under the priv->lock. |
| |
| Fixes: 546481c2816e ("IB/ipoib: Fix memory corruption in ipoib cm mode connect flow") |
| Signed-off-by: Feras Daoud <ferasda@mellanox.com> |
| Reviewed-by: Erez Shitrit <erezsh@mellanox.com> |
| Signed-off-by: Leon Romanovsky <leonro@mellanox.com> |
| Signed-off-by: Jason Gunthorpe <jgg@mellanox.com> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| drivers/infiniband/ulp/ipoib/ipoib.h | 1 - |
| drivers/infiniband/ulp/ipoib/ipoib_cm.c | 3 +-- |
| 2 files changed, 1 insertion(+), 3 deletions(-) |
| |
| diff --git a/drivers/infiniband/ulp/ipoib/ipoib.h b/drivers/infiniband/ulp/ipoib/ipoib.h |
| index 1abe3c62f106..b22d02c9de90 100644 |
| --- a/drivers/infiniband/ulp/ipoib/ipoib.h |
| +++ b/drivers/infiniband/ulp/ipoib/ipoib.h |
| @@ -248,7 +248,6 @@ struct ipoib_cm_tx { |
| struct list_head list; |
| struct net_device *dev; |
| struct ipoib_neigh *neigh; |
| - struct ipoib_path *path; |
| struct ipoib_tx_buf *tx_ring; |
| unsigned int tx_head; |
| unsigned int tx_tail; |
| diff --git a/drivers/infiniband/ulp/ipoib/ipoib_cm.c b/drivers/infiniband/ulp/ipoib/ipoib_cm.c |
| index 0428e01e8f69..aa9dcfc36cd3 100644 |
| --- a/drivers/infiniband/ulp/ipoib/ipoib_cm.c |
| +++ b/drivers/infiniband/ulp/ipoib/ipoib_cm.c |
| @@ -1312,7 +1312,6 @@ struct ipoib_cm_tx *ipoib_cm_create_tx(struct net_device *dev, struct ipoib_path |
| |
| neigh->cm = tx; |
| tx->neigh = neigh; |
| - tx->path = path; |
| tx->dev = dev; |
| list_add(&tx->list, &priv->cm.start_list); |
| set_bit(IPOIB_FLAG_INITIALIZED, &tx->flags); |
| @@ -1371,7 +1370,7 @@ static void ipoib_cm_tx_start(struct work_struct *work) |
| neigh->daddr + QPN_AND_OPTIONS_OFFSET); |
| goto free_neigh; |
| } |
| - memcpy(&pathrec, &p->path->pathrec, sizeof(pathrec)); |
| + memcpy(&pathrec, &path->pathrec, sizeof(pathrec)); |
| |
| spin_unlock_irqrestore(&priv->lock, flags); |
| netif_tx_unlock_bh(dev); |
| -- |
| 2.19.1 |
| |