dri-devel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/3] drm/drm_exec, drm/drm_kunit: Fix / WA for uaf and lock alloc tracking.
@ 2023-09-05  8:58 Thomas Hellström
  2023-09-05  8:58 ` [PATCH 1/3] drm/kunit: Avoid a driver uaf Thomas Hellström
                   ` (2 more replies)
  0 siblings, 3 replies; 19+ messages in thread
From: Thomas Hellström @ 2023-09-05  8:58 UTC (permalink / raw)
  To: intel-xe, dri-devel
  Cc: Thomas Hellström, Boris Brezillon, Danilo Krummrich,
	Christian König

While trying to replicate a weird drm_exec lock alloc tracking warning
using the drm_exec kunit test, the warning was shadowed by a UAF warning
due to a bug in the drm kunit helpers.

Patch 1 fixes that drm kunit UAF.
Patch 2 introduces a drm_exec kunit subtest that shows the weird lock
alloc tracking warning if CONFIG_DEBUG_LOCK_ALLOC is set.
Patch 3 modifies drm_exec to avoid that lock alloc tracking warning. Although
the proper fix would probably be to modify lockdep to track the lock held
status differently, this workaround at pushes the problem out of drm_exec,
so that any drm_exec users relying on its refcounting don't run into this.

Cc: Christian König <christian.koenig@amd.com>
Cc: Boris Brezillon <boris.brezillon@collabora.com>
Cc: Danilo Krummrich <dakr@redhat.com>
Cc: dri-devel@lists.freedesktop.org

Thomas Hellström (3):
  drm/kunit: Avoid a driver uaf
  drm/tests/drm_exec: Add a test for object freeing within
    drm_exec_fini()
  drm/drm_exec: Work around a WW mutex lockdep oddity

 drivers/gpu/drm/drm_exec.c            |  2 +-
 drivers/gpu/drm/tests/drm_exec_test.c | 47 +++++++++++++++++++++++++++
 include/drm/drm_exec.h                | 35 +++++++++++++++++---
 include/drm/drm_kunit_helpers.h       |  4 ++-
 4 files changed, 82 insertions(+), 6 deletions(-)

-- 
2.41.0


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH 1/3] drm/kunit: Avoid a driver uaf
  2023-09-05  8:58 [PATCH 0/3] drm/drm_exec, drm/drm_kunit: Fix / WA for uaf and lock alloc tracking Thomas Hellström
@ 2023-09-05  8:58 ` Thomas Hellström
  2023-09-05 12:06   ` [Intel-xe] " Maxime Ripard
  2023-09-05  8:58 ` [PATCH 2/3] drm/tests/drm_exec: Add a test for object freeing within drm_exec_fini() Thomas Hellström
  2023-09-05  8:58 ` [PATCH 3/3] drm/drm_exec: Work around a WW mutex lockdep oddity Thomas Hellström
  2 siblings, 1 reply; 19+ messages in thread
From: Thomas Hellström @ 2023-09-05  8:58 UTC (permalink / raw)
  To: intel-xe, dri-devel
  Cc: Thomas Hellström, Thomas Zimmermann, Maxime Ripard

when using __drm_kunit_helper_alloc_drm_device() the driver may be
dereferenced by device-managed resources up until the device is
freed, which is typically later than the kunit-managed resource code
frees it. Fix this by simply make the driver device-managed as well.

Related KASAN message:
[55272.551542] ==================================================================
[55272.551551] BUG: KASAN: slab-use-after-free in drm_dev_put.part.0+0xd4/0xe0 [drm]
[55272.551603] Read of size 8 at addr ffff888127502828 by task kunit_try_catch/10353

[55272.551612] CPU: 4 PID: 10353 Comm: kunit_try_catch Tainted: G     U           N 6.5.0-rc7+ #155
[55272.551620] Hardware name: ASUS System Product Name/PRIME B560M-A AC, BIOS 0403 01/26/2021
[55272.551626] Call Trace:
[55272.551629]  <TASK>
[55272.551633]  dump_stack_lvl+0x57/0x90
[55272.551639]  print_report+0xcf/0x630
[55272.551645]  ? _raw_spin_lock_irqsave+0x5f/0x70
[55272.551652]  ? drm_dev_put.part.0+0xd4/0xe0 [drm]
[55272.551694]  kasan_report+0xd7/0x110
[55272.551699]  ? drm_dev_put.part.0+0xd4/0xe0 [drm]
[55272.551742]  drm_dev_put.part.0+0xd4/0xe0 [drm]
[55272.551783]  devres_release_all+0x15d/0x1f0
[55272.551790]  ? __pfx_devres_release_all+0x10/0x10
[55272.551797]  device_unbind_cleanup+0x16/0x1a0
[55272.551802]  device_release_driver_internal+0x3e5/0x540
[55272.551808]  ? kobject_put+0x5d/0x4b0
[55272.551814]  bus_remove_device+0x1f1/0x3f0
[55272.551819]  device_del+0x342/0x910
[55272.551826]  ? __pfx_device_del+0x10/0x10
[55272.551830]  ? lock_release+0x339/0x5e0
[55272.551836]  ? kunit_remove_resource+0x128/0x290 [kunit]
[55272.551845]  ? __pfx_lock_release+0x10/0x10
[55272.551851]  platform_device_del.part.0+0x1f/0x1e0
[55272.551856]  ? _raw_spin_unlock_irqrestore+0x30/0x60
[55272.551863]  kunit_remove_resource+0x195/0x290 [kunit]
[55272.551871]  ? _raw_spin_unlock_irqrestore+0x30/0x60
[55272.551877]  kunit_cleanup+0x78/0x120 [kunit]
[55272.551885]  ? __kthread_parkme+0xc1/0x1f0
[55272.551891]  ? __pfx_kunit_try_run_case_cleanup+0x10/0x10 [kunit]
[55272.551900]  ? __pfx_kunit_generic_run_threadfn_adapter+0x10/0x10 [kunit]
[55272.551909]  kunit_generic_run_threadfn_adapter+0x4a/0x90 [kunit]
[55272.551919]  kthread+0x2e7/0x3c0
[55272.551924]  ? __pfx_kthread+0x10/0x10
[55272.551929]  ret_from_fork+0x2d/0x70
[55272.551935]  ? __pfx_kthread+0x10/0x10
[55272.551940]  ret_from_fork_asm+0x1b/0x30
[55272.551948]  </TASK>

[55272.551953] Allocated by task 10351:
[55272.551956]  kasan_save_stack+0x1c/0x40
[55272.551962]  kasan_set_track+0x21/0x30
[55272.551966]  __kasan_kmalloc+0x8b/0x90
[55272.551970]  __kmalloc+0x5e/0x160
[55272.551976]  kunit_kmalloc_array+0x1c/0x50 [kunit]
[55272.551984]  drm_exec_test_init+0xfa/0x2c0 [drm_exec_test]
[55272.551991]  kunit_try_run_case+0xdd/0x250 [kunit]
[55272.551999]  kunit_generic_run_threadfn_adapter+0x4a/0x90 [kunit]
[55272.552008]  kthread+0x2e7/0x3c0
[55272.552012]  ret_from_fork+0x2d/0x70
[55272.552017]  ret_from_fork_asm+0x1b/0x30

[55272.552024] Freed by task 10353:
[55272.552027]  kasan_save_stack+0x1c/0x40
[55272.552032]  kasan_set_track+0x21/0x30
[55272.552036]  kasan_save_free_info+0x27/0x40
[55272.552041]  __kasan_slab_free+0x106/0x180
[55272.552046]  slab_free_freelist_hook+0xb3/0x160
[55272.552051]  __kmem_cache_free+0xb2/0x290
[55272.552056]  kunit_remove_resource+0x195/0x290 [kunit]
[55272.552064]  kunit_cleanup+0x78/0x120 [kunit]
[55272.552072]  kunit_generic_run_threadfn_adapter+0x4a/0x90 [kunit]
[55272.552080]  kthread+0x2e7/0x3c0
[55272.552085]  ret_from_fork+0x2d/0x70
[55272.552089]  ret_from_fork_asm+0x1b/0x30

[55272.552096] The buggy address belongs to the object at ffff888127502800
                which belongs to the cache kmalloc-512 of size 512
[55272.552105] The buggy address is located 40 bytes inside of
                freed 512-byte region [ffff888127502800, ffff888127502a00)

[55272.552115] The buggy address belongs to the physical page:
[55272.552119] page:00000000af6c70ff refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x127500
[55272.552127] head:00000000af6c70ff order:3 entire_mapcount:0 nr_pages_mapped:0 pincount:0
[55272.552133] anon flags: 0x17ffffc0010200(slab|head|node=0|zone=2|lastcpupid=0x1fffff)
[55272.552141] page_type: 0xffffffff()
[55272.552145] raw: 0017ffffc0010200 ffff888100042c80 0000000000000000 dead000000000001
[55272.552152] raw: 0000000000000000 0000000080200020 00000001ffffffff 0000000000000000
[55272.552157] page dumped because: kasan: bad access detected

[55272.552163] Memory state around the buggy address:
[55272.552167]  ffff888127502700: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[55272.552173]  ffff888127502780: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[55272.552178] >ffff888127502800: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[55272.552184]                                   ^
[55272.552187]  ffff888127502880: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[55272.552193]  ffff888127502900: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[55272.552198] ==================================================================
[55272.552203] Disabling lock debugging due to kernel taint

Cc: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Cc: Maxime Ripard <mripard@kernel.org>
Cc: Thomas Zimmermann <tzimmermann@suse.de>
Cc: David Airlie <airlied@gmail.com>
Cc: Daniel Vetter <daniel@ffwll.ch>
Cc: dri-devel@lists.freedesktop.org

Signed-off-by: Thomas Hellström <thomas.hellstrom@linux.intel.com>
---
 include/drm/drm_kunit_helpers.h | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/include/drm/drm_kunit_helpers.h b/include/drm/drm_kunit_helpers.h
index 514c8a7a32f0..ba483c87f0e7 100644
--- a/include/drm/drm_kunit_helpers.h
+++ b/include/drm/drm_kunit_helpers.h
@@ -3,6 +3,8 @@
 #ifndef DRM_KUNIT_HELPERS_H_
 #define DRM_KUNIT_HELPERS_H_
 
+#include <linux/device.h>
+
 #include <kunit/test.h>
 
 struct drm_device;
@@ -51,7 +53,7 @@ __drm_kunit_helper_alloc_drm_device(struct kunit *test,
 {
 	struct drm_driver *driver;
 
-	driver = kunit_kzalloc(test, sizeof(*driver), GFP_KERNEL);
+	driver = devm_kzalloc(dev, sizeof(*driver), GFP_KERNEL);
 	KUNIT_ASSERT_NOT_NULL(test, driver);
 
 	driver->driver_features = features;
-- 
2.41.0


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH 2/3] drm/tests/drm_exec: Add a test for object freeing within drm_exec_fini()
  2023-09-05  8:58 [PATCH 0/3] drm/drm_exec, drm/drm_kunit: Fix / WA for uaf and lock alloc tracking Thomas Hellström
  2023-09-05  8:58 ` [PATCH 1/3] drm/kunit: Avoid a driver uaf Thomas Hellström
@ 2023-09-05  8:58 ` Thomas Hellström
  2023-09-05 12:05   ` [Intel-xe] " Maxime Ripard
  2023-09-05  8:58 ` [PATCH 3/3] drm/drm_exec: Work around a WW mutex lockdep oddity Thomas Hellström
  2 siblings, 1 reply; 19+ messages in thread
From: Thomas Hellström @ 2023-09-05  8:58 UTC (permalink / raw)
  To: intel-xe, dri-devel; +Cc: Thomas Hellström, Christian König

Check that object freeing from within drm_exec_fini() works as expected
and doesn't generate any warnings.

Cc: Christian König <christian.koenig@amd.com>
Cc: dri-devel@lists.freedesktop.org
Signed-off-by: Thomas Hellström <thomas.hellstrom@linux.intel.com>
---
 drivers/gpu/drm/tests/drm_exec_test.c | 47 +++++++++++++++++++++++++++
 1 file changed, 47 insertions(+)

diff --git a/drivers/gpu/drm/tests/drm_exec_test.c b/drivers/gpu/drm/tests/drm_exec_test.c
index 563949d777dd..294c25f49cc7 100644
--- a/drivers/gpu/drm/tests/drm_exec_test.c
+++ b/drivers/gpu/drm/tests/drm_exec_test.c
@@ -170,6 +170,52 @@ static void test_prepare_array(struct kunit *test)
 	drm_gem_private_object_fini(&gobj2);
 }
 
+static const struct drm_gem_object_funcs put_funcs = {
+	.free = (void *)kfree,
+};
+
+/*
+ * Check that freeing objects from within drm_exec_fini()
+ * behaves as expected.
+ */
+static void test_early_put(struct kunit *test)
+{
+	struct drm_exec_priv *priv = test->priv;
+	struct drm_gem_object *gobj1;
+	struct drm_gem_object *gobj2;
+	struct drm_gem_object *array[2];
+	struct drm_exec exec;
+	int ret;
+
+	gobj1 = kzalloc(sizeof(*gobj1), GFP_KERNEL);
+	KUNIT_EXPECT_NOT_ERR_OR_NULL(test, gobj1);
+	if (!gobj1)
+		return;
+
+	gobj2 = kzalloc(sizeof(*gobj2), GFP_KERNEL);
+	KUNIT_EXPECT_NOT_ERR_OR_NULL(test, gobj2);
+	if (!gobj2) {
+		kfree(gobj1);
+		return;
+	}
+
+	gobj1->funcs = &put_funcs;
+	gobj2->funcs = &put_funcs;
+	array[0] = gobj1;
+	array[1] = gobj2;
+	drm_gem_private_object_init(priv->drm, gobj1, PAGE_SIZE);
+	drm_gem_private_object_init(priv->drm, gobj2, PAGE_SIZE);
+
+	drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT);
+	drm_exec_until_all_locked(&exec)
+		ret = drm_exec_prepare_array(&exec, array, ARRAY_SIZE(array),
+					     1);
+	KUNIT_EXPECT_EQ(test, ret, 0);
+	drm_gem_object_put(gobj1);
+	drm_gem_object_put(gobj2);
+	drm_exec_fini(&exec);
+}
+
 static void test_multiple_loops(struct kunit *test)
 {
 	struct drm_exec exec;
@@ -198,6 +244,7 @@ static struct kunit_case drm_exec_tests[] = {
 	KUNIT_CASE(test_prepare),
 	KUNIT_CASE(test_prepare_array),
 	KUNIT_CASE(test_multiple_loops),
+	KUNIT_CASE(test_early_put),
 	{}
 };
 
-- 
2.41.0


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH 3/3] drm/drm_exec: Work around a WW mutex lockdep oddity
  2023-09-05  8:58 [PATCH 0/3] drm/drm_exec, drm/drm_kunit: Fix / WA for uaf and lock alloc tracking Thomas Hellström
  2023-09-05  8:58 ` [PATCH 1/3] drm/kunit: Avoid a driver uaf Thomas Hellström
  2023-09-05  8:58 ` [PATCH 2/3] drm/tests/drm_exec: Add a test for object freeing within drm_exec_fini() Thomas Hellström
@ 2023-09-05  8:58 ` Thomas Hellström
  2023-09-05  9:22   ` Boris Brezillon
                     ` (2 more replies)
  2 siblings, 3 replies; 19+ messages in thread
From: Thomas Hellström @ 2023-09-05  8:58 UTC (permalink / raw)
  To: intel-xe, dri-devel
  Cc: Thomas Hellström, Boris Brezillon, Danilo Krummrich,
	Christian König

If *any* object of a certain WW mutex class is locked, lockdep will
consider *all* mutexes of that class as locked. Also the lock allocation
tracking code will apparently register only the address of the first
mutex locked in a sequence.
This has the odd consequence that if that first mutex is unlocked and
its memory then freed, the lock alloc tracking code will assume that memory
is freed with a held lock in there.

For now, work around that for drm_exec by releasing the first grabbed
object lock last.

Related lock alloc tracking warning:
[  322.660067] =========================
[  322.660070] WARNING: held lock freed!
[  322.660074] 6.5.0-rc7+ #155 Tainted: G     U           N
[  322.660078] -------------------------
[  322.660081] kunit_try_catch/4981 is freeing memory ffff888112adc000-ffff888112adc3ff, with a lock still held there!
[  322.660089] ffff888112adc1a0 (reservation_ww_class_mutex){+.+.}-{3:3}, at: drm_exec_lock_obj+0x11a/0x600 [drm_exec]
[  322.660104] 2 locks held by kunit_try_catch/4981:
[  322.660108]  #0: ffffc9000343fe18 (reservation_ww_class_acquire){+.+.}-{0:0}, at: test_early_put+0x22f/0x490 [drm_exec_test]
[  322.660123]  #1: ffff888112adc1a0 (reservation_ww_class_mutex){+.+.}-{3:3}, at: drm_exec_lock_obj+0x11a/0x600 [drm_exec]
[  322.660135]
               stack backtrace:
[  322.660139] CPU: 7 PID: 4981 Comm: kunit_try_catch Tainted: G     U           N 6.5.0-rc7+ #155
[  322.660146] Hardware name: ASUS System Product Name/PRIME B560M-A AC, BIOS 0403 01/26/2021
[  322.660152] Call Trace:
[  322.660155]  <TASK>
[  322.660158]  dump_stack_lvl+0x57/0x90
[  322.660164]  debug_check_no_locks_freed+0x20b/0x2b0
[  322.660172]  slab_free_freelist_hook+0xa1/0x160
[  322.660179]  ? drm_exec_unlock_all+0x168/0x2a0 [drm_exec]
[  322.660186]  __kmem_cache_free+0xb2/0x290
[  322.660192]  drm_exec_unlock_all+0x168/0x2a0 [drm_exec]
[  322.660200]  drm_exec_fini+0xf/0x1c0 [drm_exec]
[  322.660206]  test_early_put+0x289/0x490 [drm_exec_test]
[  322.660215]  ? __pfx_test_early_put+0x10/0x10 [drm_exec_test]
[  322.660222]  ? __kasan_check_byte+0xf/0x40
[  322.660227]  ? __ksize+0x63/0x140
[  322.660233]  ? drmm_add_final_kfree+0x3e/0xa0 [drm]
[  322.660289]  ? _raw_spin_unlock_irqrestore+0x30/0x60
[  322.660294]  ? lockdep_hardirqs_on+0x7d/0x100
[  322.660301]  ? __pfx_kunit_try_run_case+0x10/0x10 [kunit]
[  322.660310]  ? __pfx_kunit_generic_run_threadfn_adapter+0x10/0x10 [kunit]
[  322.660319]  kunit_generic_run_threadfn_adapter+0x4a/0x90 [kunit]
[  322.660328]  kthread+0x2e7/0x3c0
[  322.660334]  ? __pfx_kthread+0x10/0x10
[  322.660339]  ret_from_fork+0x2d/0x70
[  322.660345]  ? __pfx_kthread+0x10/0x10
[  322.660349]  ret_from_fork_asm+0x1b/0x30
[  322.660358]  </TASK>
[  322.660818]     ok 8 test_early_put

Cc: Christian König <christian.koenig@amd.com>
Cc: Boris Brezillon <boris.brezillon@collabora.com>
Cc: Danilo Krummrich <dakr@redhat.com>
Cc: dri-devel@lists.freedesktop.org
Signed-off-by: Thomas Hellström <thomas.hellstrom@linux.intel.com>
---
 drivers/gpu/drm/drm_exec.c |  2 +-
 include/drm/drm_exec.h     | 35 +++++++++++++++++++++++++++++++----
 2 files changed, 32 insertions(+), 5 deletions(-)

diff --git a/drivers/gpu/drm/drm_exec.c b/drivers/gpu/drm/drm_exec.c
index ff69cf0fb42a..5d2809de4517 100644
--- a/drivers/gpu/drm/drm_exec.c
+++ b/drivers/gpu/drm/drm_exec.c
@@ -56,7 +56,7 @@ static void drm_exec_unlock_all(struct drm_exec *exec)
 	struct drm_gem_object *obj;
 	unsigned long index;
 
-	drm_exec_for_each_locked_object(exec, index, obj) {
+	drm_exec_for_each_locked_object_reverse(exec, index, obj) {
 		dma_resv_unlock(obj->resv);
 		drm_gem_object_put(obj);
 	}
diff --git a/include/drm/drm_exec.h b/include/drm/drm_exec.h
index e0462361adf9..55764cf7c374 100644
--- a/include/drm/drm_exec.h
+++ b/include/drm/drm_exec.h
@@ -51,6 +51,20 @@ struct drm_exec {
 	struct drm_gem_object *prelocked;
 };
 
+/**
+ * drm_exec_obj() - Return the object for a give drm_exec index
+ * @exec: Pointer to the drm_exec context
+ * @index: The index.
+ *
+ * Return: Pointer to the locked object corresponding to @index if
+ * index is within the number of locked objects. NULL otherwise.
+ */
+static inline struct drm_gem_object *
+drm_exec_obj(struct drm_exec *exec, unsigned long index)
+{
+	return index < exec->num_objects ? exec->objects[index] : NULL;
+}
+
 /**
  * drm_exec_for_each_locked_object - iterate over all the locked objects
  * @exec: drm_exec object
@@ -59,10 +73,23 @@ struct drm_exec {
  *
  * Iterate over all the locked GEM objects inside the drm_exec object.
  */
-#define drm_exec_for_each_locked_object(exec, index, obj)	\
-	for (index = 0, obj = (exec)->objects[0];		\
-	     index < (exec)->num_objects;			\
-	     ++index, obj = (exec)->objects[index])
+#define drm_exec_for_each_locked_object(exec, index, obj)		\
+	for ((index) = 0; ((obj) = drm_exec_obj(exec, index)); ++(index))
+
+/**
+ * drm_exec_for_each_locked_object_reverse - iterate over all the locked
+ * objects in reverse locking order
+ * @exec: drm_exec object
+ * @index: unsigned long index for the iteration
+ * @obj: the current GEM object
+ *
+ * Iterate over all the locked GEM objects inside the drm_exec object in
+ * reverse locking order. Note that @index may go below zero and wrap,
+ * but that will be caught by drm_exec_object(), returning a NULL object.
+ */
+#define drm_exec_for_each_locked_object_reverse(exec, index, obj)	\
+	for ((index) = (exec)->num_objects - 1;				\
+	     ((obj) = drm_exec_obj(exec, index)); --(index))
 
 /**
  * drm_exec_until_all_locked - loop until all GEM objects are locked
-- 
2.41.0


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* Re: [PATCH 3/3] drm/drm_exec: Work around a WW mutex lockdep oddity
  2023-09-05  8:58 ` [PATCH 3/3] drm/drm_exec: Work around a WW mutex lockdep oddity Thomas Hellström
@ 2023-09-05  9:22   ` Boris Brezillon
  2023-09-05 10:59   ` Danilo Krummrich
  2023-09-05 13:14   ` Christian König
  2 siblings, 0 replies; 19+ messages in thread
From: Boris Brezillon @ 2023-09-05  9:22 UTC (permalink / raw)
  To: Thomas Hellström
  Cc: Danilo Krummrich, intel-xe, dri-devel, Christian König

On Tue,  5 Sep 2023 10:58:32 +0200
Thomas Hellström <thomas.hellstrom@linux.intel.com> wrote:

> If *any* object of a certain WW mutex class is locked, lockdep will
> consider *all* mutexes of that class as locked. Also the lock allocation
> tracking code will apparently register only the address of the first
> mutex locked in a sequence.
> This has the odd consequence that if that first mutex is unlocked and
> its memory then freed, the lock alloc tracking code will assume that memory
> is freed with a held lock in there.
> 
> For now, work around that for drm_exec by releasing the first grabbed
> object lock last.

It's probably a good thing to unlock in reverse order anyway, just like
we do for regular locks.

> 
> Related lock alloc tracking warning:
> [  322.660067] =========================
> [  322.660070] WARNING: held lock freed!
> [  322.660074] 6.5.0-rc7+ #155 Tainted: G     U           N
> [  322.660078] -------------------------
> [  322.660081] kunit_try_catch/4981 is freeing memory ffff888112adc000-ffff888112adc3ff, with a lock still held there!
> [  322.660089] ffff888112adc1a0 (reservation_ww_class_mutex){+.+.}-{3:3}, at: drm_exec_lock_obj+0x11a/0x600 [drm_exec]
> [  322.660104] 2 locks held by kunit_try_catch/4981:
> [  322.660108]  #0: ffffc9000343fe18 (reservation_ww_class_acquire){+.+.}-{0:0}, at: test_early_put+0x22f/0x490 [drm_exec_test]
> [  322.660123]  #1: ffff888112adc1a0 (reservation_ww_class_mutex){+.+.}-{3:3}, at: drm_exec_lock_obj+0x11a/0x600 [drm_exec]
> [  322.660135]
>                stack backtrace:
> [  322.660139] CPU: 7 PID: 4981 Comm: kunit_try_catch Tainted: G     U           N 6.5.0-rc7+ #155
> [  322.660146] Hardware name: ASUS System Product Name/PRIME B560M-A AC, BIOS 0403 01/26/2021
> [  322.660152] Call Trace:
> [  322.660155]  <TASK>
> [  322.660158]  dump_stack_lvl+0x57/0x90
> [  322.660164]  debug_check_no_locks_freed+0x20b/0x2b0
> [  322.660172]  slab_free_freelist_hook+0xa1/0x160
> [  322.660179]  ? drm_exec_unlock_all+0x168/0x2a0 [drm_exec]
> [  322.660186]  __kmem_cache_free+0xb2/0x290
> [  322.660192]  drm_exec_unlock_all+0x168/0x2a0 [drm_exec]
> [  322.660200]  drm_exec_fini+0xf/0x1c0 [drm_exec]
> [  322.660206]  test_early_put+0x289/0x490 [drm_exec_test]
> [  322.660215]  ? __pfx_test_early_put+0x10/0x10 [drm_exec_test]
> [  322.660222]  ? __kasan_check_byte+0xf/0x40
> [  322.660227]  ? __ksize+0x63/0x140
> [  322.660233]  ? drmm_add_final_kfree+0x3e/0xa0 [drm]
> [  322.660289]  ? _raw_spin_unlock_irqrestore+0x30/0x60
> [  322.660294]  ? lockdep_hardirqs_on+0x7d/0x100
> [  322.660301]  ? __pfx_kunit_try_run_case+0x10/0x10 [kunit]
> [  322.660310]  ? __pfx_kunit_generic_run_threadfn_adapter+0x10/0x10 [kunit]
> [  322.660319]  kunit_generic_run_threadfn_adapter+0x4a/0x90 [kunit]
> [  322.660328]  kthread+0x2e7/0x3c0
> [  322.660334]  ? __pfx_kthread+0x10/0x10
> [  322.660339]  ret_from_fork+0x2d/0x70
> [  322.660345]  ? __pfx_kthread+0x10/0x10
> [  322.660349]  ret_from_fork_asm+0x1b/0x30
> [  322.660358]  </TASK>
> [  322.660818]     ok 8 test_early_put
> 
> Cc: Christian König <christian.koenig@amd.com>
> Cc: Boris Brezillon <boris.brezillon@collabora.com>
> Cc: Danilo Krummrich <dakr@redhat.com>
> Cc: dri-devel@lists.freedesktop.org
> Signed-off-by: Thomas Hellström <thomas.hellstrom@linux.intel.com>

Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>

> ---
>  drivers/gpu/drm/drm_exec.c |  2 +-
>  include/drm/drm_exec.h     | 35 +++++++++++++++++++++++++++++++----
>  2 files changed, 32 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/gpu/drm/drm_exec.c b/drivers/gpu/drm/drm_exec.c
> index ff69cf0fb42a..5d2809de4517 100644
> --- a/drivers/gpu/drm/drm_exec.c
> +++ b/drivers/gpu/drm/drm_exec.c
> @@ -56,7 +56,7 @@ static void drm_exec_unlock_all(struct drm_exec *exec)
>  	struct drm_gem_object *obj;
>  	unsigned long index;
>  
> -	drm_exec_for_each_locked_object(exec, index, obj) {
> +	drm_exec_for_each_locked_object_reverse(exec, index, obj) {
>  		dma_resv_unlock(obj->resv);
>  		drm_gem_object_put(obj);
>  	}
> diff --git a/include/drm/drm_exec.h b/include/drm/drm_exec.h
> index e0462361adf9..55764cf7c374 100644
> --- a/include/drm/drm_exec.h
> +++ b/include/drm/drm_exec.h
> @@ -51,6 +51,20 @@ struct drm_exec {
>  	struct drm_gem_object *prelocked;
>  };
>  
> +/**
> + * drm_exec_obj() - Return the object for a give drm_exec index
> + * @exec: Pointer to the drm_exec context
> + * @index: The index.
> + *
> + * Return: Pointer to the locked object corresponding to @index if
> + * index is within the number of locked objects. NULL otherwise.
> + */
> +static inline struct drm_gem_object *
> +drm_exec_obj(struct drm_exec *exec, unsigned long index)
> +{
> +	return index < exec->num_objects ? exec->objects[index] : NULL;
> +}
> +
>  /**
>   * drm_exec_for_each_locked_object - iterate over all the locked objects
>   * @exec: drm_exec object
> @@ -59,10 +73,23 @@ struct drm_exec {
>   *
>   * Iterate over all the locked GEM objects inside the drm_exec object.
>   */
> -#define drm_exec_for_each_locked_object(exec, index, obj)	\
> -	for (index = 0, obj = (exec)->objects[0];		\
> -	     index < (exec)->num_objects;			\
> -	     ++index, obj = (exec)->objects[index])
> +#define drm_exec_for_each_locked_object(exec, index, obj)		\
> +	for ((index) = 0; ((obj) = drm_exec_obj(exec, index)); ++(index))
> +
> +/**
> + * drm_exec_for_each_locked_object_reverse - iterate over all the locked
> + * objects in reverse locking order
> + * @exec: drm_exec object
> + * @index: unsigned long index for the iteration
> + * @obj: the current GEM object
> + *
> + * Iterate over all the locked GEM objects inside the drm_exec object in
> + * reverse locking order. Note that @index may go below zero and wrap,
> + * but that will be caught by drm_exec_object(), returning a NULL object.
> + */
> +#define drm_exec_for_each_locked_object_reverse(exec, index, obj)	\
> +	for ((index) = (exec)->num_objects - 1;				\
> +	     ((obj) = drm_exec_obj(exec, index)); --(index))
>  
>  /**
>   * drm_exec_until_all_locked - loop until all GEM objects are locked


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 3/3] drm/drm_exec: Work around a WW mutex lockdep oddity
  2023-09-05  8:58 ` [PATCH 3/3] drm/drm_exec: Work around a WW mutex lockdep oddity Thomas Hellström
  2023-09-05  9:22   ` Boris Brezillon
@ 2023-09-05 10:59   ` Danilo Krummrich
  2023-09-05 13:14   ` Christian König
  2 siblings, 0 replies; 19+ messages in thread
From: Danilo Krummrich @ 2023-09-05 10:59 UTC (permalink / raw)
  To: Thomas Hellström, intel-xe, dri-devel
  Cc: Boris Brezillon, Christian König

On 9/5/23 10:58, Thomas Hellström wrote:
> If *any* object of a certain WW mutex class is locked, lockdep will
> consider *all* mutexes of that class as locked. Also the lock allocation
> tracking code will apparently register only the address of the first
> mutex locked in a sequence.
> This has the odd consequence that if that first mutex is unlocked and
> its memory then freed, the lock alloc tracking code will assume that memory
> is freed with a held lock in there.
> 
> For now, work around that for drm_exec by releasing the first grabbed
> object lock last.
> 
> Related lock alloc tracking warning:
> [  322.660067] =========================
> [  322.660070] WARNING: held lock freed!
> [  322.660074] 6.5.0-rc7+ #155 Tainted: G     U           N
> [  322.660078] -------------------------
> [  322.660081] kunit_try_catch/4981 is freeing memory ffff888112adc000-ffff888112adc3ff, with a lock still held there!
> [  322.660089] ffff888112adc1a0 (reservation_ww_class_mutex){+.+.}-{3:3}, at: drm_exec_lock_obj+0x11a/0x600 [drm_exec]
> [  322.660104] 2 locks held by kunit_try_catch/4981:
> [  322.660108]  #0: ffffc9000343fe18 (reservation_ww_class_acquire){+.+.}-{0:0}, at: test_early_put+0x22f/0x490 [drm_exec_test]
> [  322.660123]  #1: ffff888112adc1a0 (reservation_ww_class_mutex){+.+.}-{3:3}, at: drm_exec_lock_obj+0x11a/0x600 [drm_exec]
> [  322.660135]
>                 stack backtrace:
> [  322.660139] CPU: 7 PID: 4981 Comm: kunit_try_catch Tainted: G     U           N 6.5.0-rc7+ #155
> [  322.660146] Hardware name: ASUS System Product Name/PRIME B560M-A AC, BIOS 0403 01/26/2021
> [  322.660152] Call Trace:
> [  322.660155]  <TASK>
> [  322.660158]  dump_stack_lvl+0x57/0x90
> [  322.660164]  debug_check_no_locks_freed+0x20b/0x2b0
> [  322.660172]  slab_free_freelist_hook+0xa1/0x160
> [  322.660179]  ? drm_exec_unlock_all+0x168/0x2a0 [drm_exec]
> [  322.660186]  __kmem_cache_free+0xb2/0x290
> [  322.660192]  drm_exec_unlock_all+0x168/0x2a0 [drm_exec]
> [  322.660200]  drm_exec_fini+0xf/0x1c0 [drm_exec]
> [  322.660206]  test_early_put+0x289/0x490 [drm_exec_test]
> [  322.660215]  ? __pfx_test_early_put+0x10/0x10 [drm_exec_test]
> [  322.660222]  ? __kasan_check_byte+0xf/0x40
> [  322.660227]  ? __ksize+0x63/0x140
> [  322.660233]  ? drmm_add_final_kfree+0x3e/0xa0 [drm]
> [  322.660289]  ? _raw_spin_unlock_irqrestore+0x30/0x60
> [  322.660294]  ? lockdep_hardirqs_on+0x7d/0x100
> [  322.660301]  ? __pfx_kunit_try_run_case+0x10/0x10 [kunit]
> [  322.660310]  ? __pfx_kunit_generic_run_threadfn_adapter+0x10/0x10 [kunit]
> [  322.660319]  kunit_generic_run_threadfn_adapter+0x4a/0x90 [kunit]
> [  322.660328]  kthread+0x2e7/0x3c0
> [  322.660334]  ? __pfx_kthread+0x10/0x10
> [  322.660339]  ret_from_fork+0x2d/0x70
> [  322.660345]  ? __pfx_kthread+0x10/0x10
> [  322.660349]  ret_from_fork_asm+0x1b/0x30
> [  322.660358]  </TASK>
> [  322.660818]     ok 8 test_early_put
> 
> Cc: Christian König <christian.koenig@amd.com>
> Cc: Boris Brezillon <boris.brezillon@collabora.com>
> Cc: Danilo Krummrich <dakr@redhat.com>
> Cc: dri-devel@lists.freedesktop.org
> Signed-off-by: Thomas Hellström <thomas.hellstrom@linux.intel.com>

Reviewed-by: Danilo Krummrich <dakr@redhat.com>

One typo below.

> ---
>   drivers/gpu/drm/drm_exec.c |  2 +-
>   include/drm/drm_exec.h     | 35 +++++++++++++++++++++++++++++++----
>   2 files changed, 32 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/gpu/drm/drm_exec.c b/drivers/gpu/drm/drm_exec.c
> index ff69cf0fb42a..5d2809de4517 100644
> --- a/drivers/gpu/drm/drm_exec.c
> +++ b/drivers/gpu/drm/drm_exec.c
> @@ -56,7 +56,7 @@ static void drm_exec_unlock_all(struct drm_exec *exec)
>   	struct drm_gem_object *obj;
>   	unsigned long index;
>   
> -	drm_exec_for_each_locked_object(exec, index, obj) {
> +	drm_exec_for_each_locked_object_reverse(exec, index, obj) {
>   		dma_resv_unlock(obj->resv);
>   		drm_gem_object_put(obj);
>   	}
> diff --git a/include/drm/drm_exec.h b/include/drm/drm_exec.h
> index e0462361adf9..55764cf7c374 100644
> --- a/include/drm/drm_exec.h
> +++ b/include/drm/drm_exec.h
> @@ -51,6 +51,20 @@ struct drm_exec {
>   	struct drm_gem_object *prelocked;
>   };
>   
> +/**
> + * drm_exec_obj() - Return the object for a give drm_exec index
> + * @exec: Pointer to the drm_exec context
> + * @index: The index.
> + *
> + * Return: Pointer to the locked object corresponding to @index if
> + * index is within the number of locked objects. NULL otherwise.
> + */
> +static inline struct drm_gem_object *
> +drm_exec_obj(struct drm_exec *exec, unsigned long index)
> +{
> +	return index < exec->num_objects ? exec->objects[index] : NULL;
> +}
> +
>   /**
>    * drm_exec_for_each_locked_object - iterate over all the locked objects
>    * @exec: drm_exec object
> @@ -59,10 +73,23 @@ struct drm_exec {
>    *
>    * Iterate over all the locked GEM objects inside the drm_exec object.
>    */
> -#define drm_exec_for_each_locked_object(exec, index, obj)	\
> -	for (index = 0, obj = (exec)->objects[0];		\
> -	     index < (exec)->num_objects;			\
> -	     ++index, obj = (exec)->objects[index])
> +#define drm_exec_for_each_locked_object(exec, index, obj)		\
> +	for ((index) = 0; ((obj) = drm_exec_obj(exec, index)); ++(index))
> +
> +/**
> + * drm_exec_for_each_locked_object_reverse - iterate over all the locked
> + * objects in reverse locking order
> + * @exec: drm_exec object
> + * @index: unsigned long index for the iteration
> + * @obj: the current GEM object
> + *
> + * Iterate over all the locked GEM objects inside the drm_exec object in
> + * reverse locking order. Note that @index may go below zero and wrap,
> + * but that will be caught by drm_exec_object(), returning a NULL object.

drm_exec_obj()

> + */
> +#define drm_exec_for_each_locked_object_reverse(exec, index, obj)	\
> +	for ((index) = (exec)->num_objects - 1;				\
> +	     ((obj) = drm_exec_obj(exec, index)); --(index))
>   
>   /**
>    * drm_exec_until_all_locked - loop until all GEM objects are locked


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [Intel-xe] [PATCH 2/3] drm/tests/drm_exec: Add a test for object freeing within drm_exec_fini()
  2023-09-05  8:58 ` [PATCH 2/3] drm/tests/drm_exec: Add a test for object freeing within drm_exec_fini() Thomas Hellström
@ 2023-09-05 12:05   ` Maxime Ripard
  2023-09-05 12:32     ` Thomas Hellström
  0 siblings, 1 reply; 19+ messages in thread
From: Maxime Ripard @ 2023-09-05 12:05 UTC (permalink / raw)
  To: Thomas Hellström; +Cc: intel-xe, dri-devel, Christian König

[-- Attachment #1: Type: text/plain, Size: 2318 bytes --]

Hi,

On Tue, Sep 05, 2023 at 10:58:31AM +0200, Thomas Hellström wrote:
> Check that object freeing from within drm_exec_fini() works as expected
> and doesn't generate any warnings.
> 
> Cc: Christian König <christian.koenig@amd.com>
> Cc: dri-devel@lists.freedesktop.org
> Signed-off-by: Thomas Hellström <thomas.hellstrom@linux.intel.com>
> ---
>  drivers/gpu/drm/tests/drm_exec_test.c | 47 +++++++++++++++++++++++++++
>  1 file changed, 47 insertions(+)
> 
> diff --git a/drivers/gpu/drm/tests/drm_exec_test.c b/drivers/gpu/drm/tests/drm_exec_test.c
> index 563949d777dd..294c25f49cc7 100644
> --- a/drivers/gpu/drm/tests/drm_exec_test.c
> +++ b/drivers/gpu/drm/tests/drm_exec_test.c
> @@ -170,6 +170,52 @@ static void test_prepare_array(struct kunit *test)
>  	drm_gem_private_object_fini(&gobj2);
>  }
>  
> +static const struct drm_gem_object_funcs put_funcs = {
> +	.free = (void *)kfree,
> +};
> +
> +/*
> + * Check that freeing objects from within drm_exec_fini()
> + * behaves as expected.
> + */
> +static void test_early_put(struct kunit *test)
> +{
> +	struct drm_exec_priv *priv = test->priv;
> +	struct drm_gem_object *gobj1;
> +	struct drm_gem_object *gobj2;
> +	struct drm_gem_object *array[2];
> +	struct drm_exec exec;
> +	int ret;
> +
> +	gobj1 = kzalloc(sizeof(*gobj1), GFP_KERNEL);
> +	KUNIT_EXPECT_NOT_ERR_OR_NULL(test, gobj1);
> +	if (!gobj1)
> +		return;
> +
> +	gobj2 = kzalloc(sizeof(*gobj2), GFP_KERNEL);
> +	KUNIT_EXPECT_NOT_ERR_OR_NULL(test, gobj2);
> +	if (!gobj2) {
> +		kfree(gobj1);
> +		return;
> +	}
> +
> +	gobj1->funcs = &put_funcs;
> +	gobj2->funcs = &put_funcs;
> +	array[0] = gobj1;
> +	array[1] = gobj2;
> +	drm_gem_private_object_init(priv->drm, gobj1, PAGE_SIZE);
> +	drm_gem_private_object_init(priv->drm, gobj2, PAGE_SIZE);
> +
> +	drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT);
> +	drm_exec_until_all_locked(&exec)
> +		ret = drm_exec_prepare_array(&exec, array, ARRAY_SIZE(array),
> +					     1);
> +	KUNIT_EXPECT_EQ(test, ret, 0);
> +	drm_gem_object_put(gobj1);
> +	drm_gem_object_put(gobj2);
> +	drm_exec_fini(&exec);

It doesn't look like you actually check that "freeing objects from
within drm_exec_fini() behaves as expected." What is the expectation
here, and how is it checked?

Maxime

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [Intel-xe] [PATCH 1/3] drm/kunit: Avoid a driver uaf
  2023-09-05  8:58 ` [PATCH 1/3] drm/kunit: Avoid a driver uaf Thomas Hellström
@ 2023-09-05 12:06   ` Maxime Ripard
  2023-09-05 12:43     ` Thomas Hellström
  0 siblings, 1 reply; 19+ messages in thread
From: Maxime Ripard @ 2023-09-05 12:06 UTC (permalink / raw)
  To: Thomas Hellström; +Cc: Thomas Zimmermann, intel-xe, dri-devel

[-- Attachment #1: Type: text/plain, Size: 385 bytes --]

On Tue, Sep 05, 2023 at 10:58:30AM +0200, Thomas Hellström wrote:
> when using __drm_kunit_helper_alloc_drm_device() the driver may be
> dereferenced by device-managed resources up until the device is
> freed, which is typically later than the kunit-managed resource code
> frees it.

I'd like to have a bit more context on how a driver can end up in that
situation?

Maxime

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [Intel-xe] [PATCH 2/3] drm/tests/drm_exec: Add a test for object freeing within drm_exec_fini()
  2023-09-05 12:05   ` [Intel-xe] " Maxime Ripard
@ 2023-09-05 12:32     ` Thomas Hellström
  2023-09-05 13:16       ` Maxime Ripard
  0 siblings, 1 reply; 19+ messages in thread
From: Thomas Hellström @ 2023-09-05 12:32 UTC (permalink / raw)
  To: Maxime Ripard; +Cc: intel-xe, dri-devel, Christian König

Hi,

On 9/5/23 14:05, Maxime Ripard wrote:
> Hi,
>
> On Tue, Sep 05, 2023 at 10:58:31AM +0200, Thomas Hellström wrote:
>> Check that object freeing from within drm_exec_fini() works as expected
>> and doesn't generate any warnings.
>>
>> Cc: Christian König <christian.koenig@amd.com>
>> Cc: dri-devel@lists.freedesktop.org
>> Signed-off-by: Thomas Hellström <thomas.hellstrom@linux.intel.com>
>> ---
>>   drivers/gpu/drm/tests/drm_exec_test.c | 47 +++++++++++++++++++++++++++
>>   1 file changed, 47 insertions(+)
>>
>> diff --git a/drivers/gpu/drm/tests/drm_exec_test.c b/drivers/gpu/drm/tests/drm_exec_test.c
>> index 563949d777dd..294c25f49cc7 100644
>> --- a/drivers/gpu/drm/tests/drm_exec_test.c
>> +++ b/drivers/gpu/drm/tests/drm_exec_test.c
>> @@ -170,6 +170,52 @@ static void test_prepare_array(struct kunit *test)
>>   	drm_gem_private_object_fini(&gobj2);
>>   }
>>   
>> +static const struct drm_gem_object_funcs put_funcs = {
>> +	.free = (void *)kfree,
>> +};
>> +
>> +/*
>> + * Check that freeing objects from within drm_exec_fini()
>> + * behaves as expected.
>> + */
>> +static void test_early_put(struct kunit *test)
>> +{
>> +	struct drm_exec_priv *priv = test->priv;
>> +	struct drm_gem_object *gobj1;
>> +	struct drm_gem_object *gobj2;
>> +	struct drm_gem_object *array[2];
>> +	struct drm_exec exec;
>> +	int ret;
>> +
>> +	gobj1 = kzalloc(sizeof(*gobj1), GFP_KERNEL);
>> +	KUNIT_EXPECT_NOT_ERR_OR_NULL(test, gobj1);
>> +	if (!gobj1)
>> +		return;
>> +
>> +	gobj2 = kzalloc(sizeof(*gobj2), GFP_KERNEL);
>> +	KUNIT_EXPECT_NOT_ERR_OR_NULL(test, gobj2);
>> +	if (!gobj2) {
>> +		kfree(gobj1);
>> +		return;
>> +	}
>> +
>> +	gobj1->funcs = &put_funcs;
>> +	gobj2->funcs = &put_funcs;
>> +	array[0] = gobj1;
>> +	array[1] = gobj2;
>> +	drm_gem_private_object_init(priv->drm, gobj1, PAGE_SIZE);
>> +	drm_gem_private_object_init(priv->drm, gobj2, PAGE_SIZE);
>> +
>> +	drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT);
>> +	drm_exec_until_all_locked(&exec)
>> +		ret = drm_exec_prepare_array(&exec, array, ARRAY_SIZE(array),
>> +					     1);
>> +	KUNIT_EXPECT_EQ(test, ret, 0);
>> +	drm_gem_object_put(gobj1);
>> +	drm_gem_object_put(gobj2);
>> +	drm_exec_fini(&exec);
> It doesn't look like you actually check that "freeing objects from
> within drm_exec_fini() behaves as expected." What is the expectation
> here, and how is it checked?

Hm. Good question, I've been manually checking dmesg for lockdep splats. 
Is there a way to automate that?

/Thomas



> Maxime

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [Intel-xe] [PATCH 1/3] drm/kunit: Avoid a driver uaf
  2023-09-05 12:06   ` [Intel-xe] " Maxime Ripard
@ 2023-09-05 12:43     ` Thomas Hellström
  2023-09-06 10:08       ` Maxime Ripard
  0 siblings, 1 reply; 19+ messages in thread
From: Thomas Hellström @ 2023-09-05 12:43 UTC (permalink / raw)
  To: Maxime Ripard; +Cc: Thomas Zimmermann, intel-xe, dri-devel

Hi maxime,

On 9/5/23 14:06, Maxime Ripard wrote:
> On Tue, Sep 05, 2023 at 10:58:30AM +0200, Thomas Hellström wrote:
>> when using __drm_kunit_helper_alloc_drm_device() the driver may be
>> dereferenced by device-managed resources up until the device is
>> freed, which is typically later than the kunit-managed resource code
>> frees it.
> I'd like to have a bit more context on how a driver can end up in that
> situation?

I interpret the attached traces as follows.

INIT:

Code allocates a struct device as a kunit-managed resource.
Code allocates a drm driver as a kunit-managed resource.
Code allocates a drm device as a device-managed resource.

EXIT:

Kunit resource cleanup frees the drm driver
Kunit resource cleanup frees the struct device, which starts a 
device-managed resource cleanup
device-managed cleanup calls drm_dev_put()
drm_dev_put() dereferences the (now freed) drm driver -> Boom.

It should be sufficient to enable KASAN and run the drm_exec_test kunit 
test to trigger this.

Thanks,
Thomas


>
> Maxime

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 3/3] drm/drm_exec: Work around a WW mutex lockdep oddity
  2023-09-05  8:58 ` [PATCH 3/3] drm/drm_exec: Work around a WW mutex lockdep oddity Thomas Hellström
  2023-09-05  9:22   ` Boris Brezillon
  2023-09-05 10:59   ` Danilo Krummrich
@ 2023-09-05 13:14   ` Christian König
  2023-09-05 14:29     ` Thomas Hellström
  2 siblings, 1 reply; 19+ messages in thread
From: Christian König @ 2023-09-05 13:14 UTC (permalink / raw)
  To: Thomas Hellström, intel-xe, dri-devel
  Cc: Boris Brezillon, Danilo Krummrich

Am 05.09.23 um 10:58 schrieb Thomas Hellström:
> If *any* object of a certain WW mutex class is locked, lockdep will
> consider *all* mutexes of that class as locked. Also the lock allocation
> tracking code will apparently register only the address of the first
> mutex locked in a sequence.
> This has the odd consequence that if that first mutex is unlocked and
> its memory then freed, the lock alloc tracking code will assume that memory
> is freed with a held lock in there.
>
> For now, work around that for drm_exec by releasing the first grabbed
> object lock last.
>
> Related lock alloc tracking warning:
> [  322.660067] =========================
> [  322.660070] WARNING: held lock freed!
> [  322.660074] 6.5.0-rc7+ #155 Tainted: G     U           N
> [  322.660078] -------------------------
> [  322.660081] kunit_try_catch/4981 is freeing memory ffff888112adc000-ffff888112adc3ff, with a lock still held there!
> [  322.660089] ffff888112adc1a0 (reservation_ww_class_mutex){+.+.}-{3:3}, at: drm_exec_lock_obj+0x11a/0x600 [drm_exec]
> [  322.660104] 2 locks held by kunit_try_catch/4981:
> [  322.660108]  #0: ffffc9000343fe18 (reservation_ww_class_acquire){+.+.}-{0:0}, at: test_early_put+0x22f/0x490 [drm_exec_test]
> [  322.660123]  #1: ffff888112adc1a0 (reservation_ww_class_mutex){+.+.}-{3:3}, at: drm_exec_lock_obj+0x11a/0x600 [drm_exec]
> [  322.660135]
>                 stack backtrace:
> [  322.660139] CPU: 7 PID: 4981 Comm: kunit_try_catch Tainted: G     U           N 6.5.0-rc7+ #155
> [  322.660146] Hardware name: ASUS System Product Name/PRIME B560M-A AC, BIOS 0403 01/26/2021
> [  322.660152] Call Trace:
> [  322.660155]  <TASK>
> [  322.660158]  dump_stack_lvl+0x57/0x90
> [  322.660164]  debug_check_no_locks_freed+0x20b/0x2b0
> [  322.660172]  slab_free_freelist_hook+0xa1/0x160
> [  322.660179]  ? drm_exec_unlock_all+0x168/0x2a0 [drm_exec]
> [  322.660186]  __kmem_cache_free+0xb2/0x290
> [  322.660192]  drm_exec_unlock_all+0x168/0x2a0 [drm_exec]
> [  322.660200]  drm_exec_fini+0xf/0x1c0 [drm_exec]
> [  322.660206]  test_early_put+0x289/0x490 [drm_exec_test]
> [  322.660215]  ? __pfx_test_early_put+0x10/0x10 [drm_exec_test]
> [  322.660222]  ? __kasan_check_byte+0xf/0x40
> [  322.660227]  ? __ksize+0x63/0x140
> [  322.660233]  ? drmm_add_final_kfree+0x3e/0xa0 [drm]
> [  322.660289]  ? _raw_spin_unlock_irqrestore+0x30/0x60
> [  322.660294]  ? lockdep_hardirqs_on+0x7d/0x100
> [  322.660301]  ? __pfx_kunit_try_run_case+0x10/0x10 [kunit]
> [  322.660310]  ? __pfx_kunit_generic_run_threadfn_adapter+0x10/0x10 [kunit]
> [  322.660319]  kunit_generic_run_threadfn_adapter+0x4a/0x90 [kunit]
> [  322.660328]  kthread+0x2e7/0x3c0
> [  322.660334]  ? __pfx_kthread+0x10/0x10
> [  322.660339]  ret_from_fork+0x2d/0x70
> [  322.660345]  ? __pfx_kthread+0x10/0x10
> [  322.660349]  ret_from_fork_asm+0x1b/0x30
> [  322.660358]  </TASK>
> [  322.660818]     ok 8 test_early_put
>
> Cc: Christian König <christian.koenig@amd.com>
> Cc: Boris Brezillon <boris.brezillon@collabora.com>
> Cc: Danilo Krummrich <dakr@redhat.com>
> Cc: dri-devel@lists.freedesktop.org
> Signed-off-by: Thomas Hellström <thomas.hellstrom@linux.intel.com>
> ---
>   drivers/gpu/drm/drm_exec.c |  2 +-
>   include/drm/drm_exec.h     | 35 +++++++++++++++++++++++++++++++----
>   2 files changed, 32 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/gpu/drm/drm_exec.c b/drivers/gpu/drm/drm_exec.c
> index ff69cf0fb42a..5d2809de4517 100644
> --- a/drivers/gpu/drm/drm_exec.c
> +++ b/drivers/gpu/drm/drm_exec.c
> @@ -56,7 +56,7 @@ static void drm_exec_unlock_all(struct drm_exec *exec)
>   	struct drm_gem_object *obj;
>   	unsigned long index;
>   
> -	drm_exec_for_each_locked_object(exec, index, obj) {
> +	drm_exec_for_each_locked_object_reverse(exec, index, obj) {

Well that's a really good catch, just one more additional thought below.

>   		dma_resv_unlock(obj->resv);
>   		drm_gem_object_put(obj);
>   	}
> diff --git a/include/drm/drm_exec.h b/include/drm/drm_exec.h
> index e0462361adf9..55764cf7c374 100644
> --- a/include/drm/drm_exec.h
> +++ b/include/drm/drm_exec.h
> @@ -51,6 +51,20 @@ struct drm_exec {
>   	struct drm_gem_object *prelocked;
>   };
>   
> +/**
> + * drm_exec_obj() - Return the object for a give drm_exec index
> + * @exec: Pointer to the drm_exec context
> + * @index: The index.
> + *
> + * Return: Pointer to the locked object corresponding to @index if
> + * index is within the number of locked objects. NULL otherwise.
> + */
> +static inline struct drm_gem_object *
> +drm_exec_obj(struct drm_exec *exec, unsigned long index)
> +{
> +	return index < exec->num_objects ? exec->objects[index] : NULL;
> +}
> +
>   /**
>    * drm_exec_for_each_locked_object - iterate over all the locked objects
>    * @exec: drm_exec object
> @@ -59,10 +73,23 @@ struct drm_exec {
>    *
>    * Iterate over all the locked GEM objects inside the drm_exec object.
>    */
> -#define drm_exec_for_each_locked_object(exec, index, obj)	\
> -	for (index = 0, obj = (exec)->objects[0];		\
> -	     index < (exec)->num_objects;			\
> -	     ++index, obj = (exec)->objects[index])
> +#define drm_exec_for_each_locked_object(exec, index, obj)		\
> +	for ((index) = 0; ((obj) = drm_exec_obj(exec, index)); ++(index))

Mhm, that makes it possible to modify the number of objects while inside 
the loop, doesn't it?

I'm not sure if that's a good idea or not.

Regards,
Christian.

> +
> +/**
> + * drm_exec_for_each_locked_object_reverse - iterate over all the locked
> + * objects in reverse locking order
> + * @exec: drm_exec object
> + * @index: unsigned long index for the iteration
> + * @obj: the current GEM object
> + *
> + * Iterate over all the locked GEM objects inside the drm_exec object in
> + * reverse locking order. Note that @index may go below zero and wrap,
> + * but that will be caught by drm_exec_object(), returning a NULL object.
> + */
> +#define drm_exec_for_each_locked_object_reverse(exec, index, obj)	\
> +	for ((index) = (exec)->num_objects - 1;				\
> +	     ((obj) = drm_exec_obj(exec, index)); --(index))
>   
>   /**
>    * drm_exec_until_all_locked - loop until all GEM objects are locked


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [Intel-xe] [PATCH 2/3] drm/tests/drm_exec: Add a test for object freeing within drm_exec_fini()
  2023-09-05 12:32     ` Thomas Hellström
@ 2023-09-05 13:16       ` Maxime Ripard
  2023-09-05 13:42         ` Thomas Hellström
  0 siblings, 1 reply; 19+ messages in thread
From: Maxime Ripard @ 2023-09-05 13:16 UTC (permalink / raw)
  To: Thomas Hellström; +Cc: intel-xe, dri-devel, Christian König

[-- Attachment #1: Type: text/plain, Size: 4176 bytes --]

On Tue, Sep 05, 2023 at 02:32:38PM +0200, Thomas Hellström wrote:
> Hi,
> 
> On 9/5/23 14:05, Maxime Ripard wrote:
> > Hi,
> > 
> > On Tue, Sep 05, 2023 at 10:58:31AM +0200, Thomas Hellström wrote:
> > > Check that object freeing from within drm_exec_fini() works as expected
> > > and doesn't generate any warnings.
> > > 
> > > Cc: Christian König <christian.koenig@amd.com>
> > > Cc: dri-devel@lists.freedesktop.org
> > > Signed-off-by: Thomas Hellström <thomas.hellstrom@linux.intel.com>
> > > ---
> > >   drivers/gpu/drm/tests/drm_exec_test.c | 47 +++++++++++++++++++++++++++
> > >   1 file changed, 47 insertions(+)
> > > 
> > > diff --git a/drivers/gpu/drm/tests/drm_exec_test.c b/drivers/gpu/drm/tests/drm_exec_test.c
> > > index 563949d777dd..294c25f49cc7 100644
> > > --- a/drivers/gpu/drm/tests/drm_exec_test.c
> > > +++ b/drivers/gpu/drm/tests/drm_exec_test.c
> > > @@ -170,6 +170,52 @@ static void test_prepare_array(struct kunit *test)
> > >   	drm_gem_private_object_fini(&gobj2);
> > >   }
> > > +static const struct drm_gem_object_funcs put_funcs = {
> > > +	.free = (void *)kfree,
> > > +};
> > > +
> > > +/*
> > > + * Check that freeing objects from within drm_exec_fini()
> > > + * behaves as expected.
> > > + */
> > > +static void test_early_put(struct kunit *test)
> > > +{
> > > +	struct drm_exec_priv *priv = test->priv;
> > > +	struct drm_gem_object *gobj1;
> > > +	struct drm_gem_object *gobj2;
> > > +	struct drm_gem_object *array[2];
> > > +	struct drm_exec exec;
> > > +	int ret;
> > > +
> > > +	gobj1 = kzalloc(sizeof(*gobj1), GFP_KERNEL);
> > > +	KUNIT_EXPECT_NOT_ERR_OR_NULL(test, gobj1);
> > > +	if (!gobj1)
> > > +		return;
> > > +
> > > +	gobj2 = kzalloc(sizeof(*gobj2), GFP_KERNEL);
> > > +	KUNIT_EXPECT_NOT_ERR_OR_NULL(test, gobj2);
> > > +	if (!gobj2) {
> > > +		kfree(gobj1);
> > > +		return;
> > > +	}
> > > +
> > > +	gobj1->funcs = &put_funcs;
> > > +	gobj2->funcs = &put_funcs;
> > > +	array[0] = gobj1;
> > > +	array[1] = gobj2;
> > > +	drm_gem_private_object_init(priv->drm, gobj1, PAGE_SIZE);
> > > +	drm_gem_private_object_init(priv->drm, gobj2, PAGE_SIZE);
> > > +
> > > +	drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT);
> > > +	drm_exec_until_all_locked(&exec)
> > > +		ret = drm_exec_prepare_array(&exec, array, ARRAY_SIZE(array),
> > > +					     1);
> > > +	KUNIT_EXPECT_EQ(test, ret, 0);
> > > +	drm_gem_object_put(gobj1);
> > > +	drm_gem_object_put(gobj2);
> > > +	drm_exec_fini(&exec);
> > It doesn't look like you actually check that "freeing objects from
> > within drm_exec_fini() behaves as expected." What is the expectation
> > here, and how is it checked?
> 
> Hm. Good question, I've been manually checking dmesg for lockdep splats. Is
> there a way to automate that?

I'm not familiar with the drm_exec API, but judging by the code I'd
assume you want to check that gobj1 and gobj2 are actually freed using
kfree?

If so, I've used tested for that by creating a waitqueue and completing
it from the free function. You won't be certain that you have gone
through kfree, but you'll know that drm_gem_object_funcs.free will have
been called which is what you actually care about I think?

So something along those lines would work I think:

struct test_gem_object {
	struct drm_gem_object base;
	wait_queue_head_t freed_wq;
	bool freed_done;
};

void free_test_gem_object(struct drm_gem_object *obj)
{
	struct test_gem_object *test_obj =
		container_of(obj, struct test_gem_object, base)

	test_obj->freed_done = true;
	wake_up(&test_obj->freed_wq);
};

static const struct drm_gem_object_funcs put_funcs = {
	.free = free_test_gem_object,
}

static void test_early_put(struct kunit *test)
{
	struct test_gem_object *gobj1;
	...
	gobj1 = kunit_kzalloc(test, sizeof(*gobj1), GFP_KERNEL);
	...
	gobj1->base.funcs = &put_funcs;
	...
	array[0] = &gobj1->base;

	drm_exec_fini(&exec);

	ret = wait_event_interruptible_timeout(gobj1->freed_wq, gobj1->freed_done,
                                               msecs_to_jiffies(TIMEOUT_MS));
	KUNIT_EXPECT_GT(test, ret, 0);
}

I guess?

Maxime

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [Intel-xe] [PATCH 2/3] drm/tests/drm_exec: Add a test for object freeing within drm_exec_fini()
  2023-09-05 13:16       ` Maxime Ripard
@ 2023-09-05 13:42         ` Thomas Hellström
  2023-09-06 10:07           ` Maxime Ripard
  0 siblings, 1 reply; 19+ messages in thread
From: Thomas Hellström @ 2023-09-05 13:42 UTC (permalink / raw)
  To: Maxime Ripard; +Cc: intel-xe, dri-devel, Christian König

Hi, Maxime

On 9/5/23 15:16, Maxime Ripard wrote:
> On Tue, Sep 05, 2023 at 02:32:38PM +0200, Thomas Hellström wrote:
>> Hi,
>>
>> On 9/5/23 14:05, Maxime Ripard wrote:
>>> Hi,
>>>
>>> On Tue, Sep 05, 2023 at 10:58:31AM +0200, Thomas Hellström wrote:
>>>> Check that object freeing from within drm_exec_fini() works as expected
>>>> and doesn't generate any warnings.
>>>>
>>>> Cc: Christian König <christian.koenig@amd.com>
>>>> Cc: dri-devel@lists.freedesktop.org
>>>> Signed-off-by: Thomas Hellström <thomas.hellstrom@linux.intel.com>
>>>> ---
>>>>    drivers/gpu/drm/tests/drm_exec_test.c | 47 +++++++++++++++++++++++++++
>>>>    1 file changed, 47 insertions(+)
>>>>
>>>> diff --git a/drivers/gpu/drm/tests/drm_exec_test.c b/drivers/gpu/drm/tests/drm_exec_test.c
>>>> index 563949d777dd..294c25f49cc7 100644
>>>> --- a/drivers/gpu/drm/tests/drm_exec_test.c
>>>> +++ b/drivers/gpu/drm/tests/drm_exec_test.c
>>>> @@ -170,6 +170,52 @@ static void test_prepare_array(struct kunit *test)
>>>>    	drm_gem_private_object_fini(&gobj2);
>>>>    }
>>>> +static const struct drm_gem_object_funcs put_funcs = {
>>>> +	.free = (void *)kfree,
>>>> +};
>>>> +
>>>> +/*
>>>> + * Check that freeing objects from within drm_exec_fini()
>>>> + * behaves as expected.
>>>> + */
>>>> +static void test_early_put(struct kunit *test)
>>>> +{
>>>> +	struct drm_exec_priv *priv = test->priv;
>>>> +	struct drm_gem_object *gobj1;
>>>> +	struct drm_gem_object *gobj2;
>>>> +	struct drm_gem_object *array[2];
>>>> +	struct drm_exec exec;
>>>> +	int ret;
>>>> +
>>>> +	gobj1 = kzalloc(sizeof(*gobj1), GFP_KERNEL);
>>>> +	KUNIT_EXPECT_NOT_ERR_OR_NULL(test, gobj1);
>>>> +	if (!gobj1)
>>>> +		return;
>>>> +
>>>> +	gobj2 = kzalloc(sizeof(*gobj2), GFP_KERNEL);
>>>> +	KUNIT_EXPECT_NOT_ERR_OR_NULL(test, gobj2);
>>>> +	if (!gobj2) {
>>>> +		kfree(gobj1);
>>>> +		return;
>>>> +	}
>>>> +
>>>> +	gobj1->funcs = &put_funcs;
>>>> +	gobj2->funcs = &put_funcs;
>>>> +	array[0] = gobj1;
>>>> +	array[1] = gobj2;
>>>> +	drm_gem_private_object_init(priv->drm, gobj1, PAGE_SIZE);
>>>> +	drm_gem_private_object_init(priv->drm, gobj2, PAGE_SIZE);
>>>> +
>>>> +	drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT);
>>>> +	drm_exec_until_all_locked(&exec)
>>>> +		ret = drm_exec_prepare_array(&exec, array, ARRAY_SIZE(array),
>>>> +					     1);
>>>> +	KUNIT_EXPECT_EQ(test, ret, 0);
>>>> +	drm_gem_object_put(gobj1);
>>>> +	drm_gem_object_put(gobj2);
>>>> +	drm_exec_fini(&exec);
>>> It doesn't look like you actually check that "freeing objects from
>>> within drm_exec_fini() behaves as expected." What is the expectation
>>> here, and how is it checked?
>> Hm. Good question, I've been manually checking dmesg for lockdep splats. Is
>> there a way to automate that?
> I'm not familiar with the drm_exec API, but judging by the code I'd
> assume you want to check that gobj1 and gobj2 are actually freed using
> kfree?

Actually not. What's important here is that the call to drm_exec_fini(), 
which puts the last references to gobj1 and gobj2 doesn't trigger any 
lockdep splats, like the one in the commit message of patch 3/3. So to 
make more sense, the test could perhaps be conditioned on 
CONFIG_DEBUG_LOCK_ALLOC. Still it would require manual checking of 
dmesg() after being run.

/Thomas




^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 3/3] drm/drm_exec: Work around a WW mutex lockdep oddity
  2023-09-05 13:14   ` Christian König
@ 2023-09-05 14:29     ` Thomas Hellström
  2023-09-06  8:34       ` Christian König
  0 siblings, 1 reply; 19+ messages in thread
From: Thomas Hellström @ 2023-09-05 14:29 UTC (permalink / raw)
  To: Christian König, intel-xe, dri-devel
  Cc: Boris Brezillon, Danilo Krummrich

Hi, Christian

On 9/5/23 15:14, Christian König wrote:
> Am 05.09.23 um 10:58 schrieb Thomas Hellström:
>> If *any* object of a certain WW mutex class is locked, lockdep will
>> consider *all* mutexes of that class as locked. Also the lock allocation
>> tracking code will apparently register only the address of the first
>> mutex locked in a sequence.
>> This has the odd consequence that if that first mutex is unlocked and
>> its memory then freed, the lock alloc tracking code will assume that 
>> memory
>> is freed with a held lock in there.
>>
>> For now, work around that for drm_exec by releasing the first grabbed
>> object lock last.
>>
>> Related lock alloc tracking warning:
>> [  322.660067] =========================
>> [  322.660070] WARNING: held lock freed!
>> [  322.660074] 6.5.0-rc7+ #155 Tainted: G     U           N
>> [  322.660078] -------------------------
>> [  322.660081] kunit_try_catch/4981 is freeing memory 
>> ffff888112adc000-ffff888112adc3ff, with a lock still held there!
>> [  322.660089] ffff888112adc1a0 
>> (reservation_ww_class_mutex){+.+.}-{3:3}, at: 
>> drm_exec_lock_obj+0x11a/0x600 [drm_exec]
>> [  322.660104] 2 locks held by kunit_try_catch/4981:
>> [  322.660108]  #0: ffffc9000343fe18 
>> (reservation_ww_class_acquire){+.+.}-{0:0}, at: 
>> test_early_put+0x22f/0x490 [drm_exec_test]
>> [  322.660123]  #1: ffff888112adc1a0 
>> (reservation_ww_class_mutex){+.+.}-{3:3}, at: 
>> drm_exec_lock_obj+0x11a/0x600 [drm_exec]
>> [  322.660135]
>>                 stack backtrace:
>> [  322.660139] CPU: 7 PID: 4981 Comm: kunit_try_catch Tainted: G     
>> U           N 6.5.0-rc7+ #155
>> [  322.660146] Hardware name: ASUS System Product Name/PRIME B560M-A 
>> AC, BIOS 0403 01/26/2021
>> [  322.660152] Call Trace:
>> [  322.660155]  <TASK>
>> [  322.660158]  dump_stack_lvl+0x57/0x90
>> [  322.660164]  debug_check_no_locks_freed+0x20b/0x2b0
>> [  322.660172]  slab_free_freelist_hook+0xa1/0x160
>> [  322.660179]  ? drm_exec_unlock_all+0x168/0x2a0 [drm_exec]
>> [  322.660186]  __kmem_cache_free+0xb2/0x290
>> [  322.660192]  drm_exec_unlock_all+0x168/0x2a0 [drm_exec]
>> [  322.660200]  drm_exec_fini+0xf/0x1c0 [drm_exec]
>> [  322.660206]  test_early_put+0x289/0x490 [drm_exec_test]
>> [  322.660215]  ? __pfx_test_early_put+0x10/0x10 [drm_exec_test]
>> [  322.660222]  ? __kasan_check_byte+0xf/0x40
>> [  322.660227]  ? __ksize+0x63/0x140
>> [  322.660233]  ? drmm_add_final_kfree+0x3e/0xa0 [drm]
>> [  322.660289]  ? _raw_spin_unlock_irqrestore+0x30/0x60
>> [  322.660294]  ? lockdep_hardirqs_on+0x7d/0x100
>> [  322.660301]  ? __pfx_kunit_try_run_case+0x10/0x10 [kunit]
>> [  322.660310]  ? __pfx_kunit_generic_run_threadfn_adapter+0x10/0x10 
>> [kunit]
>> [  322.660319]  kunit_generic_run_threadfn_adapter+0x4a/0x90 [kunit]
>> [  322.660328]  kthread+0x2e7/0x3c0
>> [  322.660334]  ? __pfx_kthread+0x10/0x10
>> [  322.660339]  ret_from_fork+0x2d/0x70
>> [  322.660345]  ? __pfx_kthread+0x10/0x10
>> [  322.660349]  ret_from_fork_asm+0x1b/0x30
>> [  322.660358]  </TASK>
>> [  322.660818]     ok 8 test_early_put
>>
>> Cc: Christian König <christian.koenig@amd.com>
>> Cc: Boris Brezillon <boris.brezillon@collabora.com>
>> Cc: Danilo Krummrich <dakr@redhat.com>
>> Cc: dri-devel@lists.freedesktop.org
>> Signed-off-by: Thomas Hellström <thomas.hellstrom@linux.intel.com>
>> ---
>>   drivers/gpu/drm/drm_exec.c |  2 +-
>>   include/drm/drm_exec.h     | 35 +++++++++++++++++++++++++++++++----
>>   2 files changed, 32 insertions(+), 5 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/drm_exec.c b/drivers/gpu/drm/drm_exec.c
>> index ff69cf0fb42a..5d2809de4517 100644
>> --- a/drivers/gpu/drm/drm_exec.c
>> +++ b/drivers/gpu/drm/drm_exec.c
>> @@ -56,7 +56,7 @@ static void drm_exec_unlock_all(struct drm_exec *exec)
>>       struct drm_gem_object *obj;
>>       unsigned long index;
>>   -    drm_exec_for_each_locked_object(exec, index, obj) {
>> +    drm_exec_for_each_locked_object_reverse(exec, index, obj) {
>
> Well that's a really good catch, just one more additional thought below.
>
>>           dma_resv_unlock(obj->resv);
>>           drm_gem_object_put(obj);
>>       }
>> diff --git a/include/drm/drm_exec.h b/include/drm/drm_exec.h
>> index e0462361adf9..55764cf7c374 100644
>> --- a/include/drm/drm_exec.h
>> +++ b/include/drm/drm_exec.h
>> @@ -51,6 +51,20 @@ struct drm_exec {
>>       struct drm_gem_object *prelocked;
>>   };
>>   +/**
>> + * drm_exec_obj() - Return the object for a give drm_exec index
>> + * @exec: Pointer to the drm_exec context
>> + * @index: The index.
>> + *
>> + * Return: Pointer to the locked object corresponding to @index if
>> + * index is within the number of locked objects. NULL otherwise.
>> + */
>> +static inline struct drm_gem_object *
>> +drm_exec_obj(struct drm_exec *exec, unsigned long index)
>> +{
>> +    return index < exec->num_objects ? exec->objects[index] : NULL;
>> +}
>> +
>>   /**
>>    * drm_exec_for_each_locked_object - iterate over all the locked 
>> objects
>>    * @exec: drm_exec object
>> @@ -59,10 +73,23 @@ struct drm_exec {
>>    *
>>    * Iterate over all the locked GEM objects inside the drm_exec object.
>>    */
>> -#define drm_exec_for_each_locked_object(exec, index, obj)    \
>> -    for (index = 0, obj = (exec)->objects[0];        \
>> -         index < (exec)->num_objects;            \
>> -         ++index, obj = (exec)->objects[index])
>> +#define drm_exec_for_each_locked_object(exec, index, obj)        \
>> +    for ((index) = 0; ((obj) = drm_exec_obj(exec, index)); ++(index))
>
> Mhm, that makes it possible to modify the number of objects while 
> inside the loop, doesn't it?

Sorry, you lost me a bit there. Isn't that possible with the previous 
code as well?

/Thanks,

Thomas



>
> I'm not sure if that's a good idea or not.
>
> Regards,
> Christian.
>
>> +
>> +/**
>> + * drm_exec_for_each_locked_object_reverse - iterate over all the 
>> locked
>> + * objects in reverse locking order
>> + * @exec: drm_exec object
>> + * @index: unsigned long index for the iteration
>> + * @obj: the current GEM object
>> + *
>> + * Iterate over all the locked GEM objects inside the drm_exec 
>> object in
>> + * reverse locking order. Note that @index may go below zero and wrap,
>> + * but that will be caught by drm_exec_object(), returning a NULL 
>> object.
>> + */
>> +#define drm_exec_for_each_locked_object_reverse(exec, index, obj)    \
>> +    for ((index) = (exec)->num_objects - 1;                \
>> +         ((obj) = drm_exec_obj(exec, index)); --(index))
>>     /**
>>    * drm_exec_until_all_locked - loop until all GEM objects are locked
>

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 3/3] drm/drm_exec: Work around a WW mutex lockdep oddity
  2023-09-05 14:29     ` Thomas Hellström
@ 2023-09-06  8:34       ` Christian König
  2023-09-07  8:59         ` Thomas Hellström
  0 siblings, 1 reply; 19+ messages in thread
From: Christian König @ 2023-09-06  8:34 UTC (permalink / raw)
  To: Thomas Hellström, intel-xe, dri-devel
  Cc: Boris Brezillon, Danilo Krummrich

Am 05.09.23 um 16:29 schrieb Thomas Hellström:
> Hi, Christian
>
> On 9/5/23 15:14, Christian König wrote:
>> Am 05.09.23 um 10:58 schrieb Thomas Hellström:
>>> If *any* object of a certain WW mutex class is locked, lockdep will
>>> consider *all* mutexes of that class as locked. Also the lock 
>>> allocation
>>> tracking code will apparently register only the address of the first
>>> mutex locked in a sequence.
>>> This has the odd consequence that if that first mutex is unlocked and
>>> its memory then freed, the lock alloc tracking code will assume that 
>>> memory
>>> is freed with a held lock in there.
>>>
>>> For now, work around that for drm_exec by releasing the first grabbed
>>> object lock last.
>>>
>>> Related lock alloc tracking warning:
>>> [  322.660067] =========================
>>> [  322.660070] WARNING: held lock freed!
>>> [  322.660074] 6.5.0-rc7+ #155 Tainted: G     U           N
>>> [  322.660078] -------------------------
>>> [  322.660081] kunit_try_catch/4981 is freeing memory 
>>> ffff888112adc000-ffff888112adc3ff, with a lock still held there!
>>> [  322.660089] ffff888112adc1a0 
>>> (reservation_ww_class_mutex){+.+.}-{3:3}, at: 
>>> drm_exec_lock_obj+0x11a/0x600 [drm_exec]
>>> [  322.660104] 2 locks held by kunit_try_catch/4981:
>>> [  322.660108]  #0: ffffc9000343fe18 
>>> (reservation_ww_class_acquire){+.+.}-{0:0}, at: 
>>> test_early_put+0x22f/0x490 [drm_exec_test]
>>> [  322.660123]  #1: ffff888112adc1a0 
>>> (reservation_ww_class_mutex){+.+.}-{3:3}, at: 
>>> drm_exec_lock_obj+0x11a/0x600 [drm_exec]
>>> [  322.660135]
>>>                 stack backtrace:
>>> [  322.660139] CPU: 7 PID: 4981 Comm: kunit_try_catch Tainted: G     
>>> U           N 6.5.0-rc7+ #155
>>> [  322.660146] Hardware name: ASUS System Product Name/PRIME B560M-A 
>>> AC, BIOS 0403 01/26/2021
>>> [  322.660152] Call Trace:
>>> [  322.660155]  <TASK>
>>> [  322.660158]  dump_stack_lvl+0x57/0x90
>>> [  322.660164]  debug_check_no_locks_freed+0x20b/0x2b0
>>> [  322.660172]  slab_free_freelist_hook+0xa1/0x160
>>> [  322.660179]  ? drm_exec_unlock_all+0x168/0x2a0 [drm_exec]
>>> [  322.660186]  __kmem_cache_free+0xb2/0x290
>>> [  322.660192]  drm_exec_unlock_all+0x168/0x2a0 [drm_exec]
>>> [  322.660200]  drm_exec_fini+0xf/0x1c0 [drm_exec]
>>> [  322.660206]  test_early_put+0x289/0x490 [drm_exec_test]
>>> [  322.660215]  ? __pfx_test_early_put+0x10/0x10 [drm_exec_test]
>>> [  322.660222]  ? __kasan_check_byte+0xf/0x40
>>> [  322.660227]  ? __ksize+0x63/0x140
>>> [  322.660233]  ? drmm_add_final_kfree+0x3e/0xa0 [drm]
>>> [  322.660289]  ? _raw_spin_unlock_irqrestore+0x30/0x60
>>> [  322.660294]  ? lockdep_hardirqs_on+0x7d/0x100
>>> [  322.660301]  ? __pfx_kunit_try_run_case+0x10/0x10 [kunit]
>>> [  322.660310]  ? __pfx_kunit_generic_run_threadfn_adapter+0x10/0x10 
>>> [kunit]
>>> [  322.660319]  kunit_generic_run_threadfn_adapter+0x4a/0x90 [kunit]
>>> [  322.660328]  kthread+0x2e7/0x3c0
>>> [  322.660334]  ? __pfx_kthread+0x10/0x10
>>> [  322.660339]  ret_from_fork+0x2d/0x70
>>> [  322.660345]  ? __pfx_kthread+0x10/0x10
>>> [  322.660349]  ret_from_fork_asm+0x1b/0x30
>>> [  322.660358]  </TASK>
>>> [  322.660818]     ok 8 test_early_put
>>>
>>> Cc: Christian König <christian.koenig@amd.com>
>>> Cc: Boris Brezillon <boris.brezillon@collabora.com>
>>> Cc: Danilo Krummrich <dakr@redhat.com>
>>> Cc: dri-devel@lists.freedesktop.org
>>> Signed-off-by: Thomas Hellström <thomas.hellstrom@linux.intel.com>
>>> ---
>>>   drivers/gpu/drm/drm_exec.c |  2 +-
>>>   include/drm/drm_exec.h     | 35 +++++++++++++++++++++++++++++++----
>>>   2 files changed, 32 insertions(+), 5 deletions(-)
>>>
>>> diff --git a/drivers/gpu/drm/drm_exec.c b/drivers/gpu/drm/drm_exec.c
>>> index ff69cf0fb42a..5d2809de4517 100644
>>> --- a/drivers/gpu/drm/drm_exec.c
>>> +++ b/drivers/gpu/drm/drm_exec.c
>>> @@ -56,7 +56,7 @@ static void drm_exec_unlock_all(struct drm_exec 
>>> *exec)
>>>       struct drm_gem_object *obj;
>>>       unsigned long index;
>>>   -    drm_exec_for_each_locked_object(exec, index, obj) {
>>> +    drm_exec_for_each_locked_object_reverse(exec, index, obj) {
>>
>> Well that's a really good catch, just one more additional thought below.
>>
>>>           dma_resv_unlock(obj->resv);
>>>           drm_gem_object_put(obj);
>>>       }
>>> diff --git a/include/drm/drm_exec.h b/include/drm/drm_exec.h
>>> index e0462361adf9..55764cf7c374 100644
>>> --- a/include/drm/drm_exec.h
>>> +++ b/include/drm/drm_exec.h
>>> @@ -51,6 +51,20 @@ struct drm_exec {
>>>       struct drm_gem_object *prelocked;
>>>   };
>>>   +/**
>>> + * drm_exec_obj() - Return the object for a give drm_exec index
>>> + * @exec: Pointer to the drm_exec context
>>> + * @index: The index.
>>> + *
>>> + * Return: Pointer to the locked object corresponding to @index if
>>> + * index is within the number of locked objects. NULL otherwise.
>>> + */
>>> +static inline struct drm_gem_object *
>>> +drm_exec_obj(struct drm_exec *exec, unsigned long index)
>>> +{
>>> +    return index < exec->num_objects ? exec->objects[index] : NULL;
>>> +}
>>> +
>>>   /**
>>>    * drm_exec_for_each_locked_object - iterate over all the locked 
>>> objects
>>>    * @exec: drm_exec object
>>> @@ -59,10 +73,23 @@ struct drm_exec {
>>>    *
>>>    * Iterate over all the locked GEM objects inside the drm_exec 
>>> object.
>>>    */
>>> -#define drm_exec_for_each_locked_object(exec, index, obj) \
>>> -    for (index = 0, obj = (exec)->objects[0];        \
>>> -         index < (exec)->num_objects;            \
>>> -         ++index, obj = (exec)->objects[index])
>>> +#define drm_exec_for_each_locked_object(exec, index, obj)        \
>>> +    for ((index) = 0; ((obj) = drm_exec_obj(exec, index)); ++(index))
>>
>> Mhm, that makes it possible to modify the number of objects while 
>> inside the loop, doesn't it?
>
> Sorry, you lost me a bit there. Isn't that possible with the previous 
> code as well?

Yeah, indeed. Reviewed-by: Christian König <christian.koenig@amd.com>

Regards,
Christian.

>
> /Thanks,
>
> Thomas
>
>
>
>>
>> I'm not sure if that's a good idea or not.
>>
>> Regards,
>> Christian.
>>
>>> +
>>> +/**
>>> + * drm_exec_for_each_locked_object_reverse - iterate over all the 
>>> locked
>>> + * objects in reverse locking order
>>> + * @exec: drm_exec object
>>> + * @index: unsigned long index for the iteration
>>> + * @obj: the current GEM object
>>> + *
>>> + * Iterate over all the locked GEM objects inside the drm_exec 
>>> object in
>>> + * reverse locking order. Note that @index may go below zero and wrap,
>>> + * but that will be caught by drm_exec_object(), returning a NULL 
>>> object.
>>> + */
>>> +#define drm_exec_for_each_locked_object_reverse(exec, index, obj)    \
>>> +    for ((index) = (exec)->num_objects - 1; \
>>> +         ((obj) = drm_exec_obj(exec, index)); --(index))
>>>     /**
>>>    * drm_exec_until_all_locked - loop until all GEM objects are locked
>>


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [Intel-xe] [PATCH 2/3] drm/tests/drm_exec: Add a test for object freeing within drm_exec_fini()
  2023-09-05 13:42         ` Thomas Hellström
@ 2023-09-06 10:07           ` Maxime Ripard
  0 siblings, 0 replies; 19+ messages in thread
From: Maxime Ripard @ 2023-09-06 10:07 UTC (permalink / raw)
  To: Thomas Hellström, Brendan Higgins, David Gow
  Cc: linux-kselftest, kunit-dev, intel-xe, dri-devel,
	Christian König

[-- Attachment #1: Type: text/plain, Size: 4601 bytes --]

On Tue, Sep 05, 2023 at 03:42:58PM +0200, Thomas Hellström wrote:
> Hi, Maxime
> 
> On 9/5/23 15:16, Maxime Ripard wrote:
> > On Tue, Sep 05, 2023 at 02:32:38PM +0200, Thomas Hellström wrote:
> > > Hi,
> > > 
> > > On 9/5/23 14:05, Maxime Ripard wrote:
> > > > Hi,
> > > > 
> > > > On Tue, Sep 05, 2023 at 10:58:31AM +0200, Thomas Hellström wrote:
> > > > > Check that object freeing from within drm_exec_fini() works as expected
> > > > > and doesn't generate any warnings.
> > > > > 
> > > > > Cc: Christian König <christian.koenig@amd.com>
> > > > > Cc: dri-devel@lists.freedesktop.org
> > > > > Signed-off-by: Thomas Hellström <thomas.hellstrom@linux.intel.com>
> > > > > ---
> > > > >    drivers/gpu/drm/tests/drm_exec_test.c | 47 +++++++++++++++++++++++++++
> > > > >    1 file changed, 47 insertions(+)
> > > > > 
> > > > > diff --git a/drivers/gpu/drm/tests/drm_exec_test.c b/drivers/gpu/drm/tests/drm_exec_test.c
> > > > > index 563949d777dd..294c25f49cc7 100644
> > > > > --- a/drivers/gpu/drm/tests/drm_exec_test.c
> > > > > +++ b/drivers/gpu/drm/tests/drm_exec_test.c
> > > > > @@ -170,6 +170,52 @@ static void test_prepare_array(struct kunit *test)
> > > > >    	drm_gem_private_object_fini(&gobj2);
> > > > >    }
> > > > > +static const struct drm_gem_object_funcs put_funcs = {
> > > > > +	.free = (void *)kfree,
> > > > > +};
> > > > > +
> > > > > +/*
> > > > > + * Check that freeing objects from within drm_exec_fini()
> > > > > + * behaves as expected.
> > > > > + */
> > > > > +static void test_early_put(struct kunit *test)
> > > > > +{
> > > > > +	struct drm_exec_priv *priv = test->priv;
> > > > > +	struct drm_gem_object *gobj1;
> > > > > +	struct drm_gem_object *gobj2;
> > > > > +	struct drm_gem_object *array[2];
> > > > > +	struct drm_exec exec;
> > > > > +	int ret;
> > > > > +
> > > > > +	gobj1 = kzalloc(sizeof(*gobj1), GFP_KERNEL);
> > > > > +	KUNIT_EXPECT_NOT_ERR_OR_NULL(test, gobj1);
> > > > > +	if (!gobj1)
> > > > > +		return;
> > > > > +
> > > > > +	gobj2 = kzalloc(sizeof(*gobj2), GFP_KERNEL);
> > > > > +	KUNIT_EXPECT_NOT_ERR_OR_NULL(test, gobj2);
> > > > > +	if (!gobj2) {
> > > > > +		kfree(gobj1);
> > > > > +		return;
> > > > > +	}
> > > > > +
> > > > > +	gobj1->funcs = &put_funcs;
> > > > > +	gobj2->funcs = &put_funcs;
> > > > > +	array[0] = gobj1;
> > > > > +	array[1] = gobj2;
> > > > > +	drm_gem_private_object_init(priv->drm, gobj1, PAGE_SIZE);
> > > > > +	drm_gem_private_object_init(priv->drm, gobj2, PAGE_SIZE);
> > > > > +
> > > > > +	drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT);
> > > > > +	drm_exec_until_all_locked(&exec)
> > > > > +		ret = drm_exec_prepare_array(&exec, array, ARRAY_SIZE(array),
> > > > > +					     1);
> > > > > +	KUNIT_EXPECT_EQ(test, ret, 0);
> > > > > +	drm_gem_object_put(gobj1);
> > > > > +	drm_gem_object_put(gobj2);
> > > > > +	drm_exec_fini(&exec);
> > > > It doesn't look like you actually check that "freeing objects from
> > > > within drm_exec_fini() behaves as expected." What is the expectation
> > > > here, and how is it checked?
> > > Hm. Good question, I've been manually checking dmesg for lockdep splats. Is
> > > there a way to automate that?
> > I'm not familiar with the drm_exec API, but judging by the code I'd
> > assume you want to check that gobj1 and gobj2 are actually freed using
> > kfree?
> 
> Actually not. What's important here is that the call to drm_exec_fini(),
> which puts the last references to gobj1 and gobj2 doesn't trigger any
> lockdep splats, like the one in the commit message of patch 3/3. So to make
> more sense, the test could perhaps be conditioned on
> CONFIG_DEBUG_LOCK_ALLOC. Still it would require manual checking of dmesg()
> after being run.

I'm not aware of something to check on lockdep's status when running a
kunit test, but I'm not sure anyone is expected to look at the dmesg
trace when running kunit to find out whether the test succeeded or not.

It looks like there was an attempt at some point to fail the test if
there was a lockdep error:
https://lore.kernel.org/all/20200814205527.1833459-1-urielguajardojr@gmail.com/

It doesn't look like it's been merged though. David, Brendan, do you
know why it wasn't merged or if there is a good option for us there?

At the very least, I think a comment after the call to drm_exec_fini to
make it clear that the error would be in the kernel logs, and a better
one on the test definition to explicitly say what you want to make sure
of, and how one can check it's been done would be great.

Maxime

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [Intel-xe] [PATCH 1/3] drm/kunit: Avoid a driver uaf
  2023-09-05 12:43     ` Thomas Hellström
@ 2023-09-06 10:08       ` Maxime Ripard
  2023-09-07 10:32         ` Thomas Hellström
  0 siblings, 1 reply; 19+ messages in thread
From: Maxime Ripard @ 2023-09-06 10:08 UTC (permalink / raw)
  To: Thomas Hellström; +Cc: Thomas Zimmermann, intel-xe, dri-devel

[-- Attachment #1: Type: text/plain, Size: 1226 bytes --]

On Tue, Sep 05, 2023 at 02:43:00PM +0200, Thomas Hellström wrote:
> Hi maxime,
> 
> On 9/5/23 14:06, Maxime Ripard wrote:
> > On Tue, Sep 05, 2023 at 10:58:30AM +0200, Thomas Hellström wrote:
> > > when using __drm_kunit_helper_alloc_drm_device() the driver may be
> > > dereferenced by device-managed resources up until the device is
> > > freed, which is typically later than the kunit-managed resource code
> > > frees it.
> > I'd like to have a bit more context on how a driver can end up in that
> > situation?
> 
> I interpret the attached traces as follows.
> 
> INIT:
> 
> Code allocates a struct device as a kunit-managed resource.
> Code allocates a drm driver as a kunit-managed resource.
> Code allocates a drm device as a device-managed resource.
> 
> EXIT:
> 
> Kunit resource cleanup frees the drm driver
> Kunit resource cleanup frees the struct device, which starts a
> device-managed resource cleanup
> device-managed cleanup calls drm_dev_put()
> drm_dev_put() dereferences the (now freed) drm driver -> Boom.
> 
> It should be sufficient to enable KASAN and run the drm_exec_test kunit test
> to trigger this.

Ack. Can you put this into your commit log?

Thanks!
Maxime

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 3/3] drm/drm_exec: Work around a WW mutex lockdep oddity
  2023-09-06  8:34       ` Christian König
@ 2023-09-07  8:59         ` Thomas Hellström
  0 siblings, 0 replies; 19+ messages in thread
From: Thomas Hellström @ 2023-09-07  8:59 UTC (permalink / raw)
  To: Christian König, intel-xe, dri-devel
  Cc: Boris Brezillon, Danilo Krummrich

Hi,

On 9/6/23 10:34, Christian König wrote:
> Am 05.09.23 um 16:29 schrieb Thomas Hellström:
>> Hi, Christian
>>
>> On 9/5/23 15:14, Christian König wrote:
>>> Am 05.09.23 um 10:58 schrieb Thomas Hellström:
>>>> If *any* object of a certain WW mutex class is locked, lockdep will
>>>> consider *all* mutexes of that class as locked. Also the lock 
>>>> allocation
>>>> tracking code will apparently register only the address of the first
>>>> mutex locked in a sequence.
>>>> This has the odd consequence that if that first mutex is unlocked and
>>>> its memory then freed, the lock alloc tracking code will assume 
>>>> that memory
>>>> is freed with a held lock in there.
>>>>
>>>> For now, work around that for drm_exec by releasing the first grabbed
>>>> object lock last.
>>>>
>>>> Related lock alloc tracking warning:
>>>> [  322.660067] =========================
>>>> [  322.660070] WARNING: held lock freed!
>>>> [  322.660074] 6.5.0-rc7+ #155 Tainted: G     U           N
>>>> [  322.660078] -------------------------
>>>> [  322.660081] kunit_try_catch/4981 is freeing memory 
>>>> ffff888112adc000-ffff888112adc3ff, with a lock still held there!
>>>> [  322.660089] ffff888112adc1a0 
>>>> (reservation_ww_class_mutex){+.+.}-{3:3}, at: 
>>>> drm_exec_lock_obj+0x11a/0x600 [drm_exec]
>>>> [  322.660104] 2 locks held by kunit_try_catch/4981:
>>>> [  322.660108]  #0: ffffc9000343fe18 
>>>> (reservation_ww_class_acquire){+.+.}-{0:0}, at: 
>>>> test_early_put+0x22f/0x490 [drm_exec_test]
>>>> [  322.660123]  #1: ffff888112adc1a0 
>>>> (reservation_ww_class_mutex){+.+.}-{3:3}, at: 
>>>> drm_exec_lock_obj+0x11a/0x600 [drm_exec]
>>>> [  322.660135]
>>>>                 stack backtrace:
>>>> [  322.660139] CPU: 7 PID: 4981 Comm: kunit_try_catch Tainted: 
>>>> G     U           N 6.5.0-rc7+ #155
>>>> [  322.660146] Hardware name: ASUS System Product Name/PRIME 
>>>> B560M-A AC, BIOS 0403 01/26/2021
>>>> [  322.660152] Call Trace:
>>>> [  322.660155]  <TASK>
>>>> [  322.660158]  dump_stack_lvl+0x57/0x90
>>>> [  322.660164]  debug_check_no_locks_freed+0x20b/0x2b0
>>>> [  322.660172]  slab_free_freelist_hook+0xa1/0x160
>>>> [  322.660179]  ? drm_exec_unlock_all+0x168/0x2a0 [drm_exec]
>>>> [  322.660186]  __kmem_cache_free+0xb2/0x290
>>>> [  322.660192]  drm_exec_unlock_all+0x168/0x2a0 [drm_exec]
>>>> [  322.660200]  drm_exec_fini+0xf/0x1c0 [drm_exec]
>>>> [  322.660206]  test_early_put+0x289/0x490 [drm_exec_test]
>>>> [  322.660215]  ? __pfx_test_early_put+0x10/0x10 [drm_exec_test]
>>>> [  322.660222]  ? __kasan_check_byte+0xf/0x40
>>>> [  322.660227]  ? __ksize+0x63/0x140
>>>> [  322.660233]  ? drmm_add_final_kfree+0x3e/0xa0 [drm]
>>>> [  322.660289]  ? _raw_spin_unlock_irqrestore+0x30/0x60
>>>> [  322.660294]  ? lockdep_hardirqs_on+0x7d/0x100
>>>> [  322.660301]  ? __pfx_kunit_try_run_case+0x10/0x10 [kunit]
>>>> [  322.660310]  ? 
>>>> __pfx_kunit_generic_run_threadfn_adapter+0x10/0x10 [kunit]
>>>> [  322.660319]  kunit_generic_run_threadfn_adapter+0x4a/0x90 [kunit]
>>>> [  322.660328]  kthread+0x2e7/0x3c0
>>>> [  322.660334]  ? __pfx_kthread+0x10/0x10
>>>> [  322.660339]  ret_from_fork+0x2d/0x70
>>>> [  322.660345]  ? __pfx_kthread+0x10/0x10
>>>> [  322.660349]  ret_from_fork_asm+0x1b/0x30
>>>> [  322.660358]  </TASK>
>>>> [  322.660818]     ok 8 test_early_put
>>>>
>>>> Cc: Christian König <christian.koenig@amd.com>
>>>> Cc: Boris Brezillon <boris.brezillon@collabora.com>
>>>> Cc: Danilo Krummrich <dakr@redhat.com>
>>>> Cc: dri-devel@lists.freedesktop.org
>>>> Signed-off-by: Thomas Hellström <thomas.hellstrom@linux.intel.com>
>>>> ---
>>>>   drivers/gpu/drm/drm_exec.c |  2 +-
>>>>   include/drm/drm_exec.h     | 35 +++++++++++++++++++++++++++++++----
>>>>   2 files changed, 32 insertions(+), 5 deletions(-)
>>>>
>>>> diff --git a/drivers/gpu/drm/drm_exec.c b/drivers/gpu/drm/drm_exec.c
>>>> index ff69cf0fb42a..5d2809de4517 100644
>>>> --- a/drivers/gpu/drm/drm_exec.c
>>>> +++ b/drivers/gpu/drm/drm_exec.c
>>>> @@ -56,7 +56,7 @@ static void drm_exec_unlock_all(struct drm_exec 
>>>> *exec)
>>>>       struct drm_gem_object *obj;
>>>>       unsigned long index;
>>>>   -    drm_exec_for_each_locked_object(exec, index, obj) {
>>>> +    drm_exec_for_each_locked_object_reverse(exec, index, obj) {
>>>
>>> Well that's a really good catch, just one more additional thought 
>>> below.
>>>
>>>> dma_resv_unlock(obj->resv);
>>>>           drm_gem_object_put(obj);
>>>>       }
>>>> diff --git a/include/drm/drm_exec.h b/include/drm/drm_exec.h
>>>> index e0462361adf9..55764cf7c374 100644
>>>> --- a/include/drm/drm_exec.h
>>>> +++ b/include/drm/drm_exec.h
>>>> @@ -51,6 +51,20 @@ struct drm_exec {
>>>>       struct drm_gem_object *prelocked;
>>>>   };
>>>>   +/**
>>>> + * drm_exec_obj() - Return the object for a give drm_exec index
>>>> + * @exec: Pointer to the drm_exec context
>>>> + * @index: The index.
>>>> + *
>>>> + * Return: Pointer to the locked object corresponding to @index if
>>>> + * index is within the number of locked objects. NULL otherwise.
>>>> + */
>>>> +static inline struct drm_gem_object *
>>>> +drm_exec_obj(struct drm_exec *exec, unsigned long index)
>>>> +{
>>>> +    return index < exec->num_objects ? exec->objects[index] : NULL;
>>>> +}
>>>> +
>>>>   /**
>>>>    * drm_exec_for_each_locked_object - iterate over all the locked 
>>>> objects
>>>>    * @exec: drm_exec object
>>>> @@ -59,10 +73,23 @@ struct drm_exec {
>>>>    *
>>>>    * Iterate over all the locked GEM objects inside the drm_exec 
>>>> object.
>>>>    */
>>>> -#define drm_exec_for_each_locked_object(exec, index, obj) \
>>>> -    for (index = 0, obj = (exec)->objects[0];        \
>>>> -         index < (exec)->num_objects;            \
>>>> -         ++index, obj = (exec)->objects[index])
>>>> +#define drm_exec_for_each_locked_object(exec, index, obj)        \
>>>> +    for ((index) = 0; ((obj) = drm_exec_obj(exec, index)); ++(index))
>>>
>>> Mhm, that makes it possible to modify the number of objects while 
>>> inside the loop, doesn't it?
>>
>> Sorry, you lost me a bit there. Isn't that possible with the previous 
>> code as well?
>
> Yeah, indeed. Reviewed-by: Christian König <christian.koenig@amd.com>
>
> Regards,
> Christian.

Thanks Boris, Danilo and Christian for review. I pushed this one to 
drm-misc-next-fixes.

/Thomas


>
>>
>> /Thanks,
>>
>> Thomas
>>
>>
>>
>>>
>>> I'm not sure if that's a good idea or not.
>>>
>>> Regards,
>>> Christian.
>>>
>>>> +
>>>> +/**
>>>> + * drm_exec_for_each_locked_object_reverse - iterate over all the 
>>>> locked
>>>> + * objects in reverse locking order
>>>> + * @exec: drm_exec object
>>>> + * @index: unsigned long index for the iteration
>>>> + * @obj: the current GEM object
>>>> + *
>>>> + * Iterate over all the locked GEM objects inside the drm_exec 
>>>> object in
>>>> + * reverse locking order. Note that @index may go below zero and 
>>>> wrap,
>>>> + * but that will be caught by drm_exec_object(), returning a NULL 
>>>> object.
>>>> + */
>>>> +#define drm_exec_for_each_locked_object_reverse(exec, index, 
>>>> obj)    \
>>>> +    for ((index) = (exec)->num_objects - 1; \
>>>> +         ((obj) = drm_exec_obj(exec, index)); --(index))
>>>>     /**
>>>>    * drm_exec_until_all_locked - loop until all GEM objects are locked
>>>
>

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [Intel-xe] [PATCH 1/3] drm/kunit: Avoid a driver uaf
  2023-09-06 10:08       ` Maxime Ripard
@ 2023-09-07 10:32         ` Thomas Hellström
  0 siblings, 0 replies; 19+ messages in thread
From: Thomas Hellström @ 2023-09-07 10:32 UTC (permalink / raw)
  To: Maxime Ripard; +Cc: Thomas Zimmermann, intel-xe, dri-devel

Hi, Maxime,

On 9/6/23 12:08, Maxime Ripard wrote:
> On Tue, Sep 05, 2023 at 02:43:00PM +0200, Thomas Hellström wrote:
>> Hi maxime,
>>
>> On 9/5/23 14:06, Maxime Ripard wrote:
>>> On Tue, Sep 05, 2023 at 10:58:30AM +0200, Thomas Hellström wrote:
>>>> when using __drm_kunit_helper_alloc_drm_device() the driver may be
>>>> dereferenced by device-managed resources up until the device is
>>>> freed, which is typically later than the kunit-managed resource code
>>>> frees it.
>>> I'd like to have a bit more context on how a driver can end up in that
>>> situation?
>> I interpret the attached traces as follows.
>>
>> INIT:
>>
>> Code allocates a struct device as a kunit-managed resource.
>> Code allocates a drm driver as a kunit-managed resource.
>> Code allocates a drm device as a device-managed resource.
>>
>> EXIT:
>>
>> Kunit resource cleanup frees the drm driver
>> Kunit resource cleanup frees the struct device, which starts a
>> device-managed resource cleanup
>> device-managed cleanup calls drm_dev_put()
>> drm_dev_put() dereferences the (now freed) drm driver -> Boom.
>>
>> It should be sufficient to enable KASAN and run the drm_exec_test kunit test
>> to trigger this.
> Ack. Can you put this into your commit log?
>
> Thanks!
> Maxime

Thanks for reviewing. I'll update this and the other patch with your 
comments.

Thanks,

Thomas



^ permalink raw reply	[flat|nested] 19+ messages in thread

end of thread, other threads:[~2023-09-07 10:32 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-09-05  8:58 [PATCH 0/3] drm/drm_exec, drm/drm_kunit: Fix / WA for uaf and lock alloc tracking Thomas Hellström
2023-09-05  8:58 ` [PATCH 1/3] drm/kunit: Avoid a driver uaf Thomas Hellström
2023-09-05 12:06   ` [Intel-xe] " Maxime Ripard
2023-09-05 12:43     ` Thomas Hellström
2023-09-06 10:08       ` Maxime Ripard
2023-09-07 10:32         ` Thomas Hellström
2023-09-05  8:58 ` [PATCH 2/3] drm/tests/drm_exec: Add a test for object freeing within drm_exec_fini() Thomas Hellström
2023-09-05 12:05   ` [Intel-xe] " Maxime Ripard
2023-09-05 12:32     ` Thomas Hellström
2023-09-05 13:16       ` Maxime Ripard
2023-09-05 13:42         ` Thomas Hellström
2023-09-06 10:07           ` Maxime Ripard
2023-09-05  8:58 ` [PATCH 3/3] drm/drm_exec: Work around a WW mutex lockdep oddity Thomas Hellström
2023-09-05  9:22   ` Boris Brezillon
2023-09-05 10:59   ` Danilo Krummrich
2023-09-05 13:14   ` Christian König
2023-09-05 14:29     ` Thomas Hellström
2023-09-06  8:34       ` Christian König
2023-09-07  8:59         ` Thomas Hellström

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox