]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
6.12-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 30 Sep 2025 11:09:07 +0000 (13:09 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 30 Sep 2025 11:09:07 +0000 (13:09 +0200)
added patches:
iommufd-fix-race-during-abort-for-file-descriptors.patch

queue-6.12/iommufd-fix-race-during-abort-for-file-descriptors.patch [new file with mode: 0644]
queue-6.12/series

diff --git a/queue-6.12/iommufd-fix-race-during-abort-for-file-descriptors.patch b/queue-6.12/iommufd-fix-race-during-abort-for-file-descriptors.patch
new file mode 100644 (file)
index 0000000..902f200
--- /dev/null
@@ -0,0 +1,171 @@
+From stable+bounces-181955-greg=kroah.com@vger.kernel.org Mon Sep 29 18:12:39 2025
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 29 Sep 2025 12:12:30 -0400
+Subject: iommufd: Fix race during abort for file descriptors
+To: stable@vger.kernel.org
+Cc: Jason Gunthorpe <jgg@nvidia.com>, Nicolin Chen <nicolinc@nvidia.com>, Nirmoy Das <nirmoyd@nvidia.com>, Kevin Tian <kevin.tian@intel.com>, syzbot+80620e2d0d0a33b09f93@syzkaller.appspotmail.com, Sasha Levin <sashal@kernel.org>
+Message-ID: <20250929161230.149933-1-sashal@kernel.org>
+
+From: Jason Gunthorpe <jgg@nvidia.com>
+
+[ Upstream commit 4e034bf045b12852a24d5d33f2451850818ba0c1 ]
+
+fput() doesn't actually call file_operations release() synchronously, it
+puts the file on a work queue and it will be released eventually.
+
+This is normally fine, except for iommufd the file and the iommufd_object
+are tied to gether. The file has the object as it's private_data and holds
+a users refcount, while the object is expected to remain alive as long as
+the file is.
+
+When the allocation of a new object aborts before installing the file it
+will fput() the file and then go on to immediately kfree() the obj. This
+causes a UAF once the workqueue completes the fput() and tries to
+decrement the users refcount.
+
+Fix this by putting the core code in charge of the file lifetime, and call
+__fput_sync() during abort to ensure that release() is called before
+kfree. __fput_sync() is a bit too tricky to open code in all the object
+implementations. Instead the objects tell the core code where the file
+pointer is and the core will take care of the life cycle.
+
+If the object is successfully allocated then the file will hold a users
+refcount and the iommufd_object cannot be destroyed.
+
+It is worth noting that close(); ioctl(IOMMU_DESTROY); doesn't have an
+issue because close() is already using a synchronous version of fput().
+
+The UAF looks like this:
+
+    BUG: KASAN: slab-use-after-free in iommufd_eventq_fops_release+0x45/0xc0 drivers/iommu/iommufd/eventq.c:376
+    Write of size 4 at addr ffff888059c97804 by task syz.0.46/6164
+
+    CPU: 0 UID: 0 PID: 6164 Comm: syz.0.46 Not tainted syzkaller #0 PREEMPT(full)
+    Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 08/18/2025
+    Call Trace:
+     <TASK>
+     __dump_stack lib/dump_stack.c:94 [inline]
+     dump_stack_lvl+0x116/0x1f0 lib/dump_stack.c:120
+     print_address_description mm/kasan/report.c:378 [inline]
+     print_report+0xcd/0x630 mm/kasan/report.c:482
+     kasan_report+0xe0/0x110 mm/kasan/report.c:595
+     check_region_inline mm/kasan/generic.c:183 [inline]
+     kasan_check_range+0x100/0x1b0 mm/kasan/generic.c:189
+     instrument_atomic_read_write include/linux/instrumented.h:96 [inline]
+     atomic_fetch_sub_release include/linux/atomic/atomic-instrumented.h:400 [inline]
+     __refcount_dec include/linux/refcount.h:455 [inline]
+     refcount_dec include/linux/refcount.h:476 [inline]
+     iommufd_eventq_fops_release+0x45/0xc0 drivers/iommu/iommufd/eventq.c:376
+     __fput+0x402/0xb70 fs/file_table.c:468
+     task_work_run+0x14d/0x240 kernel/task_work.c:227
+     resume_user_mode_work include/linux/resume_user_mode.h:50 [inline]
+     exit_to_user_mode_loop+0xeb/0x110 kernel/entry/common.c:43
+     exit_to_user_mode_prepare include/linux/irq-entry-common.h:225 [inline]
+     syscall_exit_to_user_mode_work include/linux/entry-common.h:175 [inline]
+     syscall_exit_to_user_mode include/linux/entry-common.h:210 [inline]
+     do_syscall_64+0x41c/0x4c0 arch/x86/entry/syscall_64.c:100
+     entry_SYSCALL_64_after_hwframe+0x77/0x7f
+
+Link: https://patch.msgid.link/r/1-v1-02cd136829df+31-iommufd_syz_fput_jgg@nvidia.com
+Cc: stable@vger.kernel.org
+Fixes: 07838f7fd529 ("iommufd: Add iommufd fault object")
+Reviewed-by: Nicolin Chen <nicolinc@nvidia.com>
+Reviewed-by: Nirmoy Das <nirmoyd@nvidia.com>
+Reviewed-by: Kevin Tian <kevin.tian@intel.com>
+Tested-by: Nicolin Chen <nicolinc@nvidia.com>
+Reported-by: syzbot+80620e2d0d0a33b09f93@syzkaller.appspotmail.com
+Closes: https://lore.kernel.org/r/68c8583d.050a0220.2ff435.03a2.GAE@google.com
+Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
+[ applied eventq.c changes to fault.c, drop veventq bits ]
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/iommu/iommufd/fault.c |    4 +---
+ drivers/iommu/iommufd/main.c  |   34 +++++++++++++++++++++++++++++++---
+ 2 files changed, 32 insertions(+), 6 deletions(-)
+
+--- a/drivers/iommu/iommufd/fault.c
++++ b/drivers/iommu/iommufd/fault.c
+@@ -415,7 +415,7 @@ int iommufd_fault_alloc(struct iommufd_u
+       fdno = get_unused_fd_flags(O_CLOEXEC);
+       if (fdno < 0) {
+               rc = fdno;
+-              goto out_fput;
++              goto out_abort;
+       }
+       cmd->out_fault_id = fault->obj.id;
+@@ -431,8 +431,6 @@ int iommufd_fault_alloc(struct iommufd_u
+       return 0;
+ out_put_fdno:
+       put_unused_fd(fdno);
+-out_fput:
+-      fput(filep);
+ out_abort:
+       iommufd_object_abort_and_destroy(ucmd->ictx, &fault->obj);
+--- a/drivers/iommu/iommufd/main.c
++++ b/drivers/iommu/iommufd/main.c
+@@ -23,6 +23,7 @@
+ #include "iommufd_test.h"
+ struct iommufd_object_ops {
++      size_t file_offset;
+       void (*destroy)(struct iommufd_object *obj);
+       void (*abort)(struct iommufd_object *obj);
+ };
+@@ -97,10 +98,30 @@ void iommufd_object_abort(struct iommufd
+ void iommufd_object_abort_and_destroy(struct iommufd_ctx *ictx,
+                                     struct iommufd_object *obj)
+ {
+-      if (iommufd_object_ops[obj->type].abort)
+-              iommufd_object_ops[obj->type].abort(obj);
++      const struct iommufd_object_ops *ops = &iommufd_object_ops[obj->type];
++
++      if (ops->file_offset) {
++              struct file **filep = ((void *)obj) + ops->file_offset;
++
++              /*
++               * A file should hold a users refcount while the file is open
++               * and put it back in its release. The file should hold a
++               * pointer to obj in their private data. Normal fput() is
++               * deferred to a workqueue and can get out of order with the
++               * following kfree(obj). Using the sync version ensures the
++               * release happens immediately. During abort we require the file
++               * refcount is one at this point - meaning the object alloc
++               * function cannot do anything to allow another thread to take a
++               * refcount prior to a guaranteed success.
++               */
++              if (*filep)
++                      __fput_sync(*filep);
++      }
++
++      if (ops->abort)
++              ops->abort(obj);
+       else
+-              iommufd_object_ops[obj->type].destroy(obj);
++              ops->destroy(obj);
+       iommufd_object_abort(ictx, obj);
+ }
+@@ -498,6 +519,12 @@ void iommufd_ctx_put(struct iommufd_ctx
+ }
+ EXPORT_SYMBOL_NS_GPL(iommufd_ctx_put, IOMMUFD);
++#define IOMMUFD_FILE_OFFSET(_struct, _filep, _obj)                           \
++      .file_offset = (offsetof(_struct, _filep) +                          \
++                      BUILD_BUG_ON_ZERO(!__same_type(                      \
++                              struct file *, ((_struct *)NULL)->_filep)) + \
++                      BUILD_BUG_ON_ZERO(offsetof(_struct, _obj)))
++
+ static const struct iommufd_object_ops iommufd_object_ops[] = {
+       [IOMMUFD_OBJ_ACCESS] = {
+               .destroy = iommufd_access_destroy_object,
+@@ -518,6 +545,7 @@ static const struct iommufd_object_ops i
+       },
+       [IOMMUFD_OBJ_FAULT] = {
+               .destroy = iommufd_fault_destroy,
++              IOMMUFD_FILE_OFFSET(struct iommufd_fault, filep, obj),
+       },
+ #ifdef CONFIG_IOMMUFD_TEST
+       [IOMMUFD_OBJ_SELFTEST] = {
index 5ae0eb422899729b4a74848beee6d09835c7f17d..8d439ea9837576ebd1d658b7960e4298aae4fc1d 100644 (file)
@@ -85,3 +85,4 @@ kmsan-fix-out-of-bounds-access-to-shadow-memory.patch
 mm-hugetlb-fix-folio-is-still-mapped-when-deleted.patch
 fbcon-fix-integer-overflow-in-fbcon_do_set_font.patch
 fbcon-fix-oob-access-in-font-allocation.patch
+iommufd-fix-race-during-abort-for-file-descriptors.patch