The following problem was reported by Roland Dreier based
on code inspection:

        I do see what seems like an exploitable race in
        ucma_create_id():

        - one thread create an id with an invalid userspace
          pointer (so the copy_to_user in ucma_create_id
          returns -EFAULT and calls rdma_destroy_id before
          idr_remove)
        - another thread guess the id that is going to be
          returned and call ucma_destroy_id()

        if the second thread hits the window where the cm_id is
        destroyed but the ctx is still in the idr, it can
        trigger a double free.

There is an issue here that the second thread can try to use
the new rdma_cm_id in another call after it has been destroyed.
(The problem isn't just restricted to the second thread calling
ucma_destroy_id.)  Fix this by holding the file->mux around
the entire creation process, so another thread cannot find the
id until it has been fully created.

Signed-off-by: Sean Hefty <[email protected]>
---
 drivers/infiniband/core/ucma.c |   17 +++++++++--------
 1 files changed, 9 insertions(+), 8 deletions(-)

diff --git a/drivers/infiniband/core/ucma.c b/drivers/infiniband/core/ucma.c
index ca12acf..fd6b980 100644
--- a/drivers/infiniband/core/ucma.c
+++ b/drivers/infiniband/core/ucma.c
@@ -384,31 +384,32 @@ static ssize_t ucma_create_id(struct ucma_file *file,
 
        mutex_lock(&file->mut);
        ctx = ucma_alloc_ctx(file);
-       mutex_unlock(&file->mut);
-       if (!ctx)
+       if (!ctx) {
+               mutex_unlock(&file->mut);
                return -ENOMEM;
+       }
 
        ctx->uid = cmd.uid;
        ctx->cm_id = rdma_create_id(ucma_event_handler, ctx, cmd.ps);
        if (IS_ERR(ctx->cm_id)) {
                ret = PTR_ERR(ctx->cm_id);
-               goto err1;
+               goto err;
        }
 
        resp.id = ctx->id;
        if (copy_to_user((void __user *)(unsigned long)cmd.response,
                         &resp, sizeof(resp))) {
                ret = -EFAULT;
-               goto err2;
+               goto err;
        }
+       mutex_unlock(&file->mut);
        return 0;
 
-err2:
-       rdma_destroy_id(ctx->cm_id);
-err1:
-       mutex_lock(&mut);
+err:
        idr_remove(&ctx_idr, ctx->id);
        mutex_unlock(&mut);
+       if (!IS_ERR(ctx->cm_id))
+               rdma_destroy_id(ctx->cm_id);
        kfree(ctx);
        return ret;
 }


--
To unsubscribe from this list: send the line "unsubscribe linux-rdma" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to