From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-qk1-f182.google.com (mail-qk1-f182.google.com [209.85.222.182]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3F0C148F832 for ; Thu, 15 Oct 2026 08:36:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.222.182 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1792053405; cv=none; b=XiyH+ttcMy0iSjbaiQ5H4wJO/iZ1UwRVUZ+PmWsTP8wgqTilOHvxtb9wLGXCsrkfvxt+sEYBhJqfsN4I7qxuy4r+73EphYuY5ZrmHqBwHWcOPWBcEQJulBFMBeuwP8UeIyJGHgwrixj1DSVVGcfm37GlY7bLtBEXkrCJFAYH7TY= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1792053405; c=relaxed/simple; bh=npDvVQ5z9FFSw1dO7sjs27nSs1ONKFM4kOi8CDIPTR0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=AYy2KVBfdDO9OC0htRJ2/IACw45tj5oN9uCl4k7rYg3vdjpnebM+tsN5oAarRQNciBv9/TCT18qNywUoK+jGmimuKfqOcg48wkWUJTKxb4V2yu4W88oMRhg97UPasriTHRYW2mOXRCHwWrnGyWx6zOP0Xol0qefFv60OZb2frcM= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=KYaLFmqB; arc=none smtp.client-ip=209.85.222.182 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="KYaLFmqB" Received: by mail-qk1-f182.google.com with SMTP id af79cd13be357-93cc1ab5f6cso595009485a.0 for ; Thu, 15 Oct 2026 01:36:42 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1792053401; x=1792658201; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=2STKe93BsxxSjKyZn2b4YvS4oxKmV5oT37N/2nRdsNg=; b=KYaLFmqBNd+aGu7grYEMuYvaaGaG4dnqsPkMqYul2qlRkqBTqRYJof9a2EuPWSUr7X rNMJ4+5+eGRwvOh3V65EzpOarY7SftF8Eu9c9lHM8BrKCNv014HN6wR7P9ScxMnZ5NBB y7qOAVLGsZDcti9Usp0s6Eut/hFKrj0Ic0PEtcIwgdC4irOyU7HMjmGd1sYHggWSEIX5 5lfzRi2YFlwT1EH67fKMJHtkwWCnPs8ozVaIjwm/h0aAP6EfCFipiPCOWb4wsUOMmcV7 h8Kc9AQqFTab0aN7XVoPQZPsruKO9qyz/A0WgpsnRZOmkoq8WRR287fmrlW9ilw48W5+ WmXA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1792053401; x=1792658201; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=2STKe93BsxxSjKyZn2b4YvS4oxKmV5oT37N/2nRdsNg=; b=XwBCWjRO5gfViutvqLcGugNJcOyaOSfksJkkLJ1BGD4CLdnkKZPM6/vp1WskcyLhwb 2AsPjGxTrYatkIVMUba2Uo/ZEe1H1WCgaVCzVcIluufLhdzmhM4Y8XisDkuXte/zUySA 7QgvULTAyWkx0F8UsbL2Stg9PwYiiqffPUYxXOC/LxTwT0M2fNPByqOg1vxZzuxQem+y 1Ucft7KkEv6aADwBf4/hTvkgWSLcUyUOJ7gxQUtT/KQZKALKnOUr6e69GwNl5ypT87j1 giLvArLo5aGZ6efSP2mWfakjo+gOBeB7cPzjkhJxg25b6OwZJ3bwEtUpO1KX+qV6CD/0 0NYQ== X-Forwarded-Encrypted: i=1; AKwUvBzAuF7YXJ/IHNjgurv5wM+glpBI+165gL9eekX+0AAUdCcgWPnZlzdKTdCXpvLv3zSA1Jqx/TQTCH0PDr0=@vger.kernel.org X-Gm-Message-State: AFq9FYKCDOlShHJkUvb5IK7Ja6BPACvecJ0B27Rmj9s038+yspKP8pSQ +Lz98DMjeDIzw2xl+Mv+QqOIVQwmG9sgJtg0vEuLdGK9tdE63BbvYzua X-Gm-Gg: AYBFou1jnHeq9mEEj+fWiUqJJVOwJPtb5yz3gkCtFzEFBb6nplf9nPwe2JN1hOrwzf5 5ZxmSgFwdTVxfekZSxVXQXHHAzgg6EtMdNElXhjj3e9hfTn/pAmbOcWwx4Rrv4dKrQFhSMje6JS QRHPEojoGynJzvj8gJPbit7L7J93EVbRs1DCTHqP9mVL+QearE9uOt5jLESkynL+PcqRUOjCT3U 6R1j920I9rw34S3ogYqO2wcAgsdyz8FIwMxYEUwf2CKMMKBWYToDGg544faMYs4qCwkbJ3BTLSl PYZiu9CYJnSw3ZrExup12XeD9c1y9+9AwjqUvmdbO1M9DLvR9KpVPewTsyYZhJocy3TkP5HsaR9 Sk5Y+jIg6ry4qsGJ/lbszqY5S1EVjUEwzHfXJcRe109/reZ5/giCN+/dkz9vu9i5tC2BbE+qG08 tDLAR/F3uFahAlc1I0sxs328nLXoTz/Oa5vQn+fXhXtS/1khWtj8aKurp+M9RGMfus4n3TQmm7V p1cWWq8tdWi/Wy5DDlxCwohvc9cuKTCcAxQ/+oy7A5P9prRUsCoCjHAo9vB6Opp3wQsUgBUjFO3 7vvwUEsPvTdB+WeGo3pq45nu X-Received: by 2002:a05:620a:462b:b0:93d:cbe6:c236 with SMTP id af79cd13be357-93ebd4a97edmr3010062885a.66.1792053400997; Thu, 15 Oct 2026 01:36:40 -0700 (PDT) Received: from ip-172-31-15-253.ec2.internal (ec2-44-200-54-59.compute-1.amazonaws.com. [44.200.54.59]) by smtp.gmail.com with ESMTPSA id af79cd13be357-93ef5090050sm711843985a.40.2026.10.15.01.36.40 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 15 Oct 2026 01:36:40 -0700 (PDT) From: Deep Shah To: Jason Gunthorpe , Leon Romanovsky Cc: linux-rdma@vger.kernel.org, Yishai Hadas , Edward Srouji , Sumit Semwal , =?UTF-8?q?Christian=20K=C3=B6nig?= , linux-kernel@vger.kernel.org, linux-media@vger.kernel.org, dri-devel@lists.freedesktop.org, linaro-mm-sig@lists.linaro.org, Deep Shah , stable@vger.kernel.org Subject: [PATCH rdma-rc 2/2] RDMA/uverbs: Fix DMABUF use-after-free during ufile cleanup Date: Thu, 15 Oct 2026 08:36:37 +0000 Message-ID: <20261015083637.200958-3-deepshah146@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20261015083637.200958-1-deepshah146@gmail.com> References: <20261015083637.200958-1-deepshah146@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit The DMABUF release can race with ufile cleanup. When the cleanup holds hw_destroy_rwsem for write, uverbs_uobject_release() fails its trylock and leaves the uobject on the ufile list, but the dma_buf is freed as soon as the release returns. The cleanup then destroys the uobject and uses the freed dma_buf: BUG: KASAN: slab-use-after-free in uverbs_dmabuf_fd_destroy_uobj+0x46/0x200 Read of size 8 at addr ff11000109cf14b0 by task repro/435 Call Trace: uverbs_dmabuf_fd_destroy_uobj+0x46/0x200 destroy_hw_fd_uobject+0x3f/0x50 uverbs_destroy_uobject+0xc5/0x380 __uverbs_cleanup_ufile+0x1a8/0x260 uverbs_destroy_ufile_hw+0x54/0x160 ib_uverbs_close+0x34/0x110 Allocated by task 435: dma_buf_export+0x1b5/0x530 ib_uverbs_handler_UVERBS_METHOD_DMABUF_ALLOC+0x223/0x3f0 Freed by task 436: kfree+0x301/0x530 dentry_kill+0x14c/0x560 __fput+0x23a/0x4a0 rdma_user_mmap_entry_remove() dereferences the same pointer. Protect the stored dma_buf pointer with a mutex and clear it before release returns. Use a shared revoke helper so uobject destruction and mmap entry removal both skip a cleared pointer. Hold a temporary uobject reference across uverbs_uobject_release() and the pointer update. Use a separate mutex because uobject destruction can free the mmap entry before release finishes. Map and unmap use attachment->dmabuf directly, so they do not need to take the new mutex while holding the reservation lock. Fixes: 0ac6f4056c4a ("RDMA/uverbs: Add DMABUF object type and operations") Cc: stable@vger.kernel.org Assisted-by: LLM Signed-off-by: Deep Shah --- drivers/infiniband/core/ib_core_uverbs.c | 10 +---- drivers/infiniband/core/uverbs.h | 37 ++++++++++++++++++ .../infiniband/core/uverbs_std_types_dmabuf.c | 39 +++++++++++-------- 3 files changed, 61 insertions(+), 25 deletions(-) diff --git a/drivers/infiniband/core/ib_core_uverbs.c b/drivers/infiniband/core/ib_core_uverbs.c index 41c84ffe8c09..2bdb6972fe1a 100644 --- a/drivers/infiniband/core/ib_core_uverbs.c +++ b/drivers/infiniband/core/ib_core_uverbs.c @@ -244,16 +244,8 @@ void rdma_user_mmap_entry_remove(struct rdma_user_mmap_entry *entry) entry->driver_removed = true; xa_unlock(&entry->ucontext->mmap_xa); list_for_each_entry_safe(uverbs_dmabuf, tmp, &entry->dmabufs, dmabufs_elm) { - dma_resv_lock(uverbs_dmabuf->dmabuf->resv, NULL); list_del(&uverbs_dmabuf->dmabufs_elm); - uverbs_dmabuf->revoked = true; - dma_buf_invalidate_mappings(uverbs_dmabuf->dmabuf); - dma_resv_wait_timeout(uverbs_dmabuf->dmabuf->resv, - DMA_RESV_USAGE_BOOKKEEP, false, - MAX_SCHEDULE_TIMEOUT); - dma_resv_unlock(uverbs_dmabuf->dmabuf->resv); - kref_put(&uverbs_dmabuf->kref, ib_uverbs_dmabuf_done); - wait_for_completion(&uverbs_dmabuf->comp); + ib_uverbs_dmabuf_revoke(uverbs_dmabuf); } mutex_unlock(&entry->dmabufs_lock); diff --git a/drivers/infiniband/core/uverbs.h b/drivers/infiniband/core/uverbs.h index c64dd6b94e10..728e09b41992 100644 --- a/drivers/infiniband/core/uverbs.h +++ b/drivers/infiniband/core/uverbs.h @@ -42,6 +42,8 @@ #include #include #include +#include +#include #include #include @@ -135,6 +137,13 @@ struct ib_uverbs_completion_event_file { struct ib_uverbs_dmabuf_file { struct ib_uobject uobj; + /* + * Serializes users of dmabuf with the dma_buf's release, which sets it + * to NULL. The uobject can outlive the dma_buf while it waits to be + * destroyed. The map and unmap callbacks use attachment->dmabuf + * instead of taking this lock. + */ + struct mutex dmabuf_release_lock; struct dma_buf *dmabuf; struct list_head dmabufs_elm; struct rdma_user_mmap_entry *mmap_entry; @@ -359,6 +368,34 @@ static inline void ib_uverbs_dmabuf_done(struct kref *kref) complete(&priv->comp); } +/* + * Revoke the dmabuf and wait for all of its DMA mappings to be gone. Must be + * called with the mmap entry's dmabufs_lock held, after the dmabuf has been + * removed from the entry's dmabufs list, so it runs once per dmabuf. Lock + * order is dmabufs_lock, dmabuf_release_lock, then the dma_resv lock. + */ +static inline void ib_uverbs_dmabuf_revoke(struct ib_uverbs_dmabuf_file *priv) +{ + mutex_lock(&priv->dmabuf_release_lock); + if (priv->dmabuf) { + dma_resv_lock(priv->dmabuf->resv, NULL); + priv->revoked = true; + dma_buf_invalidate_mappings(priv->dmabuf); + dma_resv_wait_timeout(priv->dmabuf->resv, + DMA_RESV_USAGE_BOOKKEEP, false, + MAX_SCHEDULE_TIMEOUT); + dma_resv_unlock(priv->dmabuf->resv); + } else { + /* A released dma_buf has no attachments left to invalidate */ + priv->revoked = true; + } + mutex_unlock(&priv->dmabuf_release_lock); + + kref_put(&priv->kref, ib_uverbs_dmabuf_done); + /* Wait till all DMA unmaps are completed */ + wait_for_completion(&priv->comp); +} + int __uverbs_cleanup_ufile(struct ib_uverbs_file *ufile, enum rdma_remove_reason reason); diff --git a/drivers/infiniband/core/uverbs_std_types_dmabuf.c b/drivers/infiniband/core/uverbs_std_types_dmabuf.c index d88d30f1e42f..8d8c997a4883 100644 --- a/drivers/infiniband/core/uverbs_std_types_dmabuf.c +++ b/drivers/infiniband/core/uverbs_std_types_dmabuf.c @@ -28,7 +28,7 @@ uverbs_dmabuf_map(struct dma_buf_attachment *attachment, struct ib_uverbs_dmabuf_file *priv = attachment->dmabuf->priv; struct sg_table *ret; - dma_resv_assert_held(priv->dmabuf->resv); + dma_resv_assert_held(attachment->dmabuf->resv); if (priv->revoked) return ERR_PTR(-ENODEV); @@ -49,7 +49,7 @@ static void uverbs_dmabuf_unmap(struct dma_buf_attachment *attachment, { struct ib_uverbs_dmabuf_file *priv = attachment->dmabuf->priv; - dma_resv_assert_held(priv->dmabuf->resv); + dma_resv_assert_held(attachment->dmabuf->resv); dma_buf_free_sgt(attachment, sgt, dir); kref_put(&priv->kref, ib_uverbs_dmabuf_done); } @@ -74,7 +74,25 @@ static void uverbs_dmabuf_release(struct dma_buf *dmabuf) if (!priv) return; + /* + * uverbs_uobject_release() may drop the last reference to the uobject, + * keep it around until the dmabuf pointer below has been cleared. + */ + uverbs_uobject_get(&priv->uobj); uverbs_uobject_release(&priv->uobj); + + /* + * The dma_buf is freed once this function returns. If the uobject was + * not destroyed above, because the ufile is concurrently being cleaned + * up, it is still reachable through the ufile's uobjects list and the + * mmap entry's dmabufs list. Make sure those paths are not in the + * middle of using the dma_buf and cannot use it afterwards. + */ + mutex_lock(&priv->dmabuf_release_lock); + priv->dmabuf = NULL; + mutex_unlock(&priv->dmabuf_release_lock); + + uverbs_uobject_put(&priv->uobj); } static const struct dma_buf_ops uverbs_dmabuf_ops = { @@ -100,6 +118,8 @@ static int UVERBS_HANDLER(UVERBS_METHOD_DMABUF_ALLOC)( off_t pg_off; int ret; + mutex_init(&uverbs_dmabuf->dmabuf_release_lock); + ret = uverbs_get_const(&pg_off, attrs, UVERBS_ATTR_ALLOC_DMABUF_PGOFF); if (ret) return ret; @@ -165,24 +185,11 @@ static void uverbs_dmabuf_fd_destroy_uobj(struct ib_uobject *uobj, { struct ib_uverbs_dmabuf_file *uverbs_dmabuf = container_of(uobj, struct ib_uverbs_dmabuf_file, uobj); - bool wait_for_comp = false; mutex_lock(&uverbs_dmabuf->mmap_entry->dmabufs_lock); - dma_resv_lock(uverbs_dmabuf->dmabuf->resv, NULL); if (!uverbs_dmabuf->revoked) { - uverbs_dmabuf->revoked = true; list_del(&uverbs_dmabuf->dmabufs_elm); - dma_buf_invalidate_mappings(uverbs_dmabuf->dmabuf); - dma_resv_wait_timeout(uverbs_dmabuf->dmabuf->resv, - DMA_RESV_USAGE_BOOKKEEP, false, - MAX_SCHEDULE_TIMEOUT); - wait_for_comp = true; - } - dma_resv_unlock(uverbs_dmabuf->dmabuf->resv); - if (wait_for_comp) { - kref_put(&uverbs_dmabuf->kref, ib_uverbs_dmabuf_done); - /* Let's wait till all DMA unmap are completed. */ - wait_for_completion(&uverbs_dmabuf->comp); + ib_uverbs_dmabuf_revoke(uverbs_dmabuf); } mutex_unlock(&uverbs_dmabuf->mmap_entry->dmabufs_lock);