Patch "RDMA/ucma: Fix use-after-free bug in ucma_create_uevent" has been added to the 5.11-stable tree

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This is a note to let you know that I've just added the patch titled

    RDMA/ucma: Fix use-after-free bug in ucma_create_uevent

to the 5.11-stable tree which can be found at:
    http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git;a=summary

The filename of the patch is:
     rdma-ucma-fix-use-after-free-bug-in-ucma_create_ueve.patch
and it can be found in the queue-5.11 subdirectory.

If you, or anyone else, feels it should not be added to the stable tree,
please let <stable@xxxxxxxxxxxxxxx> know about it.



commit 6cfc38e602604c1154e66f7287f0bb0112123ba3
Author: Avihai Horon <avihaih@xxxxxxxxxx>
Date:   Thu Feb 11 11:05:17 2021 +0200

    RDMA/ucma: Fix use-after-free bug in ucma_create_uevent
    
    [ Upstream commit fe454dc31e84f8c14cb8942fcb61666c9f40745b ]
    
    ucma_process_join() allocates struct ucma_multicast mc and frees it if an
    error occurs during its run.  Specifically, if an error occurs in
    copy_to_user(), a use-after-free might happen in the following scenario:
    
    1. mc struct is allocated.
    2. rdma_join_multicast() is called and succeeds. During its run,
       cma_iboe_join_multicast() enqueues a work that will later use the
       aforementioned mc struct.
    3. copy_to_user() is called and fails.
    4. mc struct is deallocated.
    5. The work that was enqueued by cma_iboe_join_multicast() is run and
       calls ucma_create_uevent() which tries to access mc struct (which is
       freed by now).
    
    Fix this bug by cancelling the work enqueued by cma_iboe_join_multicast().
    Since cma_work_handler() frees struct cma_work, we don't use it in
    cma_iboe_join_multicast() so we can safely cancel the work later.
    
    The following syzkaller report revealed it:
    
       BUG: KASAN: use-after-free in ucma_create_uevent+0x2dd/0x;3f0 drivers/infiniband/core/ucma.c:272
       Read of size 8 at addr ffff88810b3ad110 by task kworker/u8:1/108
    
       CPU: 1 PID: 108 Comm: kworker/u8:1 Not tainted 5.10.0-rc6+ #257
       Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS   rel-1.13.0-0-gf21b5a4aeb02-prebuilt.qemu.org 04/01/2014
       Workqueue: rdma_cm cma_work_handler
       Call Trace:
        __dump_stack lib/dump_stack.c:77 [inline]
        dump_stack+0xbe/0xf9 lib/dump_stack.c:118
        print_address_description.constprop.0+0x3e/0Ã?60 mm/kasan/report.c:385
        __kasan_report mm/kasan/report.c:545 [inline]
        kasan_report.cold+0x1f/0Ã?37 mm/kasan/report.c:562
        ucma_create_uevent+0x2dd/0Ã?3f0 drivers/infiniband/core/ucma.c:272
        ucma_event_handler+0xb7/0Ã?3c0 drivers/infiniband/core/ucma.c:349
        cma_cm_event_handler+0x5d/0Ã?1c0 drivers/infiniband/core/cma.c:1977
        cma_work_handler+0xfa/0Ã?190 drivers/infiniband/core/cma.c:2718
        process_one_work+0x54c/0Ã?930 kernel/workqueue.c:2272
        worker_thread+0x82/0Ã?830 kernel/workqueue.c:2418
        kthread+0x1ca/0Ã?220 kernel/kthread.c:292
        ret_from_fork+0x1f/0Ã?30 arch/x86/entry/entry_64.S:296
    
       Allocated by task 359:
         kasan_save_stack+0x1b/0Ã?40 mm/kasan/common.c:48
         kasan_set_track mm/kasan/common.c:56 [inline]
         __kasan_kmalloc mm/kasan/common.c:461 [inline]
         __kasan_kmalloc.constprop.0+0xc2/0xd0 mm/kasan/common.c:434
         kmalloc include/linux/slab.h:552 [inline]
         kzalloc include/linux/slab.h:664 [inline]
         ucma_process_join+0x16e/0Ã?3f0 drivers/infiniband/core/ucma.c:1453
         ucma_join_multicast+0xda/0Ã?140 drivers/infiniband/core/ucma.c:1538
         ucma_write+0x1f7/0Ã?280 drivers/infiniband/core/ucma.c:1724
         vfs_write fs/read_write.c:603 [inline]
         vfs_write+0x191/0Ã?4c0 fs/read_write.c:585
         ksys_write+0x1a1/0Ã?1e0 fs/read_write.c:658
         do_syscall_64+0x2d/0Ã?40 arch/x86/entry/common.c:46
         entry_SYSCALL_64_after_hwframe+0x44/0xa9
    
       Freed by task 359:
         kasan_save_stack+0x1b/0Ã?40 mm/kasan/common.c:48
         kasan_set_track+0x1c/0Ã?30 mm/kasan/common.c:56
         kasan_set_free_info+0x1b/0Ã?30 mm/kasan/generic.c:355
         __kasan_slab_free+0x112/0Ã?160 mm/kasan/common.c:422
         slab_free_hook mm/slub.c:1544 [inline]
         slab_free_freelist_hook mm/slub.c:1577 [inline]
         slab_free mm/slub.c:3142 [inline]
         kfree+0xb3/0Ã?3e0 mm/slub.c:4124
         ucma_process_join+0x22d/0Ã?3f0 drivers/infiniband/core/ucma.c:1497
         ucma_join_multicast+0xda/0Ã?140 drivers/infiniband/core/ucma.c:1538
         ucma_write+0x1f7/0Ã?280 drivers/infiniband/core/ucma.c:1724
         vfs_write fs/read_write.c:603 [inline]
         vfs_write+0x191/0Ã?4c0 fs/read_write.c:585
         ksys_write+0x1a1/0Ã?1e0 fs/read_write.c:658
         do_syscall_64+0x2d/0Ã?40 arch/x86/entry/common.c:46
         entry_SYSCALL_64_after_hwframe+0x44/0xa9
         The buggy address belongs to the object at ffff88810b3ad100
         which belongs to the cache kmalloc-192 of size 192
         The buggy address is located 16 bytes inside of
         192-byte region [ffff88810b3ad100, ffff88810b3ad1c0)
    
    Fixes: b5de0c60cc30 ("RDMA/cma: Fix use after free race in roce multicast join")
    Link: https://lore.kernel.org/r/20210211090517.1278415-1-leon@xxxxxxxxxx
    Reported-by: Amit Matityahu <mitm@xxxxxxxxxx>
    Signed-off-by: Avihai Horon <avihaih@xxxxxxxxxx>
    Signed-off-by: Leon Romanovsky <leonro@xxxxxxxxxx>
    Signed-off-by: Jason Gunthorpe <jgg@xxxxxxxxxx>
    Signed-off-by: Sasha Levin <sashal@xxxxxxxxxx>

diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c
index c51b84b2d2f37..e3638f80e1d52 100644
--- a/drivers/infiniband/core/cma.c
+++ b/drivers/infiniband/core/cma.c
@@ -352,7 +352,13 @@ struct ib_device *cma_get_ib_dev(struct cma_device *cma_dev)
 
 struct cma_multicast {
 	struct rdma_id_private *id_priv;
-	struct ib_sa_multicast *sa_mc;
+	union {
+		struct ib_sa_multicast *sa_mc;
+		struct {
+			struct work_struct work;
+			struct rdma_cm_event event;
+		} iboe_join;
+	};
 	struct list_head	list;
 	void			*context;
 	struct sockaddr_storage	addr;
@@ -1823,6 +1829,8 @@ static void destroy_mc(struct rdma_id_private *id_priv,
 			cma_igmp_send(ndev, &mgid, false);
 			dev_put(ndev);
 		}
+
+		cancel_work_sync(&mc->iboe_join.work);
 	}
 	kfree(mc);
 }
@@ -2683,6 +2691,28 @@ static int cma_query_ib_route(struct rdma_id_private *id_priv,
 	return (id_priv->query_id < 0) ? id_priv->query_id : 0;
 }
 
+static void cma_iboe_join_work_handler(struct work_struct *work)
+{
+	struct cma_multicast *mc =
+		container_of(work, struct cma_multicast, iboe_join.work);
+	struct rdma_cm_event *event = &mc->iboe_join.event;
+	struct rdma_id_private *id_priv = mc->id_priv;
+	int ret;
+
+	mutex_lock(&id_priv->handler_mutex);
+	if (READ_ONCE(id_priv->state) == RDMA_CM_DESTROYING ||
+	    READ_ONCE(id_priv->state) == RDMA_CM_DEVICE_REMOVAL)
+		goto out_unlock;
+
+	ret = cma_cm_event_handler(id_priv, event);
+	WARN_ON(ret);
+
+out_unlock:
+	mutex_unlock(&id_priv->handler_mutex);
+	if (event->event == RDMA_CM_EVENT_MULTICAST_JOIN)
+		rdma_destroy_ah_attr(&event->param.ud.ah_attr);
+}
+
 static void cma_work_handler(struct work_struct *_work)
 {
 	struct cma_work *work = container_of(_work, struct cma_work, work);
@@ -4478,10 +4508,7 @@ static int cma_ib_mc_handler(int status, struct ib_sa_multicast *multicast)
 	cma_make_mc_event(status, id_priv, multicast, &event, mc);
 	ret = cma_cm_event_handler(id_priv, &event);
 	rdma_destroy_ah_attr(&event.param.ud.ah_attr);
-	if (ret) {
-		destroy_id_handler_unlock(id_priv);
-		return 0;
-	}
+	WARN_ON(ret);
 
 out:
 	mutex_unlock(&id_priv->handler_mutex);
@@ -4604,7 +4631,6 @@ static void cma_iboe_set_mgid(struct sockaddr *addr, union ib_gid *mgid,
 static int cma_iboe_join_multicast(struct rdma_id_private *id_priv,
 				   struct cma_multicast *mc)
 {
-	struct cma_work *work;
 	struct rdma_dev_addr *dev_addr = &id_priv->id.route.addr.dev_addr;
 	int err = 0;
 	struct sockaddr *addr = (struct sockaddr *)&mc->addr;
@@ -4618,10 +4644,6 @@ static int cma_iboe_join_multicast(struct rdma_id_private *id_priv,
 	if (cma_zero_addr(addr))
 		return -EINVAL;
 
-	work = kzalloc(sizeof *work, GFP_KERNEL);
-	if (!work)
-		return -ENOMEM;
-
 	gid_type = id_priv->cma_dev->default_gid_type[id_priv->id.port_num -
 		   rdma_start_port(id_priv->cma_dev->device)];
 	cma_iboe_set_mgid(addr, &ib.rec.mgid, gid_type);
@@ -4632,10 +4654,9 @@ static int cma_iboe_join_multicast(struct rdma_id_private *id_priv,
 
 	if (dev_addr->bound_dev_if)
 		ndev = dev_get_by_index(dev_addr->net, dev_addr->bound_dev_if);
-	if (!ndev) {
-		err = -ENODEV;
-		goto err_free;
-	}
+	if (!ndev)
+		return -ENODEV;
+
 	ib.rec.rate = iboe_get_rate(ndev);
 	ib.rec.hop_limit = 1;
 	ib.rec.mtu = iboe_get_mtu(ndev->mtu);
@@ -4653,24 +4674,15 @@ static int cma_iboe_join_multicast(struct rdma_id_private *id_priv,
 			err = -ENOTSUPP;
 	}
 	dev_put(ndev);
-	if (err || !ib.rec.mtu) {
-		if (!err)
-			err = -EINVAL;
-		goto err_free;
-	}
+	if (err || !ib.rec.mtu)
+		return err ?: -EINVAL;
+
 	rdma_ip2gid((struct sockaddr *)&id_priv->id.route.addr.src_addr,
 		    &ib.rec.port_gid);
-	work->id = id_priv;
-	INIT_WORK(&work->work, cma_work_handler);
-	cma_make_mc_event(0, id_priv, &ib, &work->event, mc);
-	/* Balances with cma_id_put() in cma_work_handler */
-	cma_id_get(id_priv);
-	queue_work(cma_wq, &work->work);
+	INIT_WORK(&mc->iboe_join.work, cma_iboe_join_work_handler);
+	cma_make_mc_event(0, id_priv, &ib, &mc->iboe_join.event, mc);
+	queue_work(cma_wq, &mc->iboe_join.work);
 	return 0;
-
-err_free:
-	kfree(work);
-	return err;
 }
 
 int rdma_join_multicast(struct rdma_cm_id *id, struct sockaddr *addr,



[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux