linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 1/2] debugobject: fix concurrency issues with is_static_object
@ 2023-03-03 16:19 Schspa Shi
  2023-03-03 16:19 ` [PATCH 2/2] debugobject: add unit test for static debug object Schspa Shi
                   ` (3 more replies)
  0 siblings, 4 replies; 25+ messages in thread
From: Schspa Shi @ 2023-03-03 16:19 UTC (permalink / raw)
  To: tglx, longman, swboyd, linux, wuchi.zero
  Cc: linux-kernel, Schspa Shi, syzbot+5093ba19745994288b53

The is_static_object implementation relay on the initial state of the
object. If multiple places are accessed concurrently, there is a
probability that the debug object has been registered in the system, which
will invalidate the judgment of is_static_object.

The following is the scenario where the problem occurs:

T0                                                   T1
=========================================================================
mod_timer();
  debug_object_assert_init
	db = get_bucket((unsigned long) addr);
	raw_spin_lock_irqsave(&db->lock, flags);
	obj = lookup_object(addr, db);
    if (!obj) {
		raw_spin_unlock_irqrestore(&db->lock, flags);
        << Context switch >>
                                             mod_timer();
                                               debug_object_assert_init
                                               ...
                                               enqueue_timer();
        /*
         * The initial state changed a static timer object, and
         * is_static_object will return false
         */

        if (descr->is_static_object &&
			descr->is_static_object(addr)) {
                debug_object_init();
            } else {
               << Hit here for a static object >>
			   debug_print_object(&o, "assert_init");
			   debug_object_fixup(descr->fixup_assert_init, addr,
					   ODEBUG_STATE_NOTAVAILABLE);
            }
    }

To fix it, we got the is_static_object called within db->lock, and save
it's state to struct debug_obj. This will ensure we won't hit the code
branch not belong to the static object.

For the same static object, debug_object_init may enter multiple times, but
there is a lock in debug_object_init to ensure that there is no problem.

Reported-by: syzbot+5093ba19745994288b53@syzkaller.appspotmail.com
Link: https://syzkaller.appspot.com/bug?id=22c8a5938eab640d1c6bcc0e3dc7be519d878462
Signed-off-by: Schspa Shi <schspa@gmail.com>
---
 include/linux/debugobjects.h |  1 +
 lib/debugobjects.c           | 71 ++++++++++++++++++++++++++++--------
 2 files changed, 56 insertions(+), 16 deletions(-)

diff --git a/include/linux/debugobjects.h b/include/linux/debugobjects.h
index 32444686b6ff4..544a6111b97f6 100644
--- a/include/linux/debugobjects.h
+++ b/include/linux/debugobjects.h
@@ -30,6 +30,7 @@ struct debug_obj {
 	enum debug_obj_state	state;
 	unsigned int		astate;
 	void			*object;
+	bool			is_static;
 	const struct debug_obj_descr *descr;
 };
 
diff --git a/lib/debugobjects.c b/lib/debugobjects.c
index df86e649d8be0..d1be18158a1f7 100644
--- a/lib/debugobjects.c
+++ b/lib/debugobjects.c
@@ -275,6 +275,8 @@ alloc_object(void *addr, struct debug_bucket *b, const struct debug_obj_descr *d
 		obj->descr  = descr;
 		obj->state  = ODEBUG_STATE_NONE;
 		obj->astate = 0;
+		obj->is_static = descr->is_static_object &&
+			descr->is_static_object(addr);
 		hlist_add_head(&obj->node, &b->list);
 	}
 	return obj;
@@ -581,7 +583,16 @@ __debug_object_init(void *addr, const struct debug_obj_descr *descr, int onstack
 			debug_objects_oom();
 			return;
 		}
+
 		check_stack = true;
+	} else {
+		/*
+		 * The debug object is inited, and we should check this again
+		 */
+		if (obj->is_static) {
+			raw_spin_unlock_irqrestore(&db->lock, flags);
+			return;
+		}
 	}
 
 	switch (obj->state) {
@@ -640,6 +651,29 @@ void debug_object_init_on_stack(void *addr, const struct debug_obj_descr *descr)
 }
 EXPORT_SYMBOL_GPL(debug_object_init_on_stack);
 
+/*
+ * Check static object.
+ */
+static bool debug_object_check_static(struct debug_bucket *db, void *addr,
+			const struct debug_obj_descr *descr)
+{
+	struct debug_obj *obj;
+
+	/*
+	 * The is_static_object implementation relay on the initial state of the
+	 * object. If multiple places are accessed concurrently, there is a
+	 * probability that the debug object has been registered in the system,
+	 * which will invalidate the judgment of is_static_object.
+	 */
+	lockdep_assert_held(&db->lock);
+
+	obj = lookup_object(addr, db);
+	if (likely(obj))
+		return obj->is_static;
+
+	return descr->is_static_object && descr->is_static_object(addr);
+}
+
 /**
  * debug_object_activate - debug checks when an object is activated
  * @addr:	address of the object
@@ -656,6 +690,7 @@ int debug_object_activate(void *addr, const struct debug_obj_descr *descr)
 	struct debug_obj o = { .object = addr,
 			       .state = ODEBUG_STATE_NOTAVAILABLE,
 			       .descr = descr };
+	bool is_static;
 
 	if (!debug_objects_enabled)
 		return 0;
@@ -696,6 +731,7 @@ int debug_object_activate(void *addr, const struct debug_obj_descr *descr)
 		return ret;
 	}
 
+	is_static = debug_object_check_static(db, addr, descr);
 	raw_spin_unlock_irqrestore(&db->lock, flags);
 
 	/*
@@ -705,7 +741,7 @@ int debug_object_activate(void *addr, const struct debug_obj_descr *descr)
 	 * static object is tracked in the object tracker. If
 	 * not, this must be a bug, so we try to fix it up.
 	 */
-	if (descr->is_static_object && descr->is_static_object(addr)) {
+	if (is_static) {
 		/* track this static object */
 		debug_object_init(addr, descr);
 		debug_object_activate(addr, descr);
@@ -872,6 +908,7 @@ void debug_object_assert_init(void *addr, const struct debug_obj_descr *descr)
 	struct debug_bucket *db;
 	struct debug_obj *obj;
 	unsigned long flags;
+	bool is_static;
 
 	if (!debug_objects_enabled)
 		return;
@@ -886,13 +923,14 @@ void debug_object_assert_init(void *addr, const struct debug_obj_descr *descr)
 				       .state = ODEBUG_STATE_NOTAVAILABLE,
 				       .descr = descr };
 
+		is_static = debug_object_check_static(db, addr, descr);
 		raw_spin_unlock_irqrestore(&db->lock, flags);
 		/*
 		 * Maybe the object is static, and we let the type specific
 		 * code confirm. Track this static object if true, else invoke
 		 * fixup.
 		 */
-		if (descr->is_static_object && descr->is_static_object(addr)) {
+		if (is_static) {
 			/* Track this static object */
 			debug_object_init(addr, descr);
 		} else {
@@ -1215,7 +1253,8 @@ static __initconst const struct debug_obj_descr descr_type_test = {
 	.fixup_free		= fixup_free,
 };
 
-static __initdata struct self_test obj = { .static_init = 0 };
+static struct self_test obj __initdata = { .static_init = 0 };
+static struct self_test sobj __initdata = { .static_init = 1 };
 
 static void __init debug_objects_selftest(void)
 {
@@ -1256,26 +1295,26 @@ static void __init debug_objects_selftest(void)
 	if (check_results(&obj, ODEBUG_STATE_NONE, fixups, warnings))
 		goto out;
 
-	obj.static_init = 1;
-	debug_object_activate(&obj, &descr_type_test);
-	if (check_results(&obj, ODEBUG_STATE_ACTIVE, fixups, warnings))
+	debug_object_init(&sobj, &descr_type_test);
+	debug_object_activate(&sobj, &descr_type_test);
+	if (check_results(&sobj, ODEBUG_STATE_ACTIVE, fixups, warnings))
 		goto out;
-	debug_object_init(&obj, &descr_type_test);
-	if (check_results(&obj, ODEBUG_STATE_INIT, ++fixups, ++warnings))
+	debug_object_init(&sobj, &descr_type_test);
+	if (check_results(&sobj, ODEBUG_STATE_INIT, ++fixups, ++warnings))
 		goto out;
-	debug_object_free(&obj, &descr_type_test);
-	if (check_results(&obj, ODEBUG_STATE_NONE, fixups, warnings))
+	debug_object_free(&sobj, &descr_type_test);
+	if (check_results(&sobj, ODEBUG_STATE_NONE, fixups, warnings))
 		goto out;
 
 #ifdef CONFIG_DEBUG_OBJECTS_FREE
-	debug_object_init(&obj, &descr_type_test);
-	if (check_results(&obj, ODEBUG_STATE_INIT, fixups, warnings))
+	debug_object_init(&sobj, &descr_type_test);
+	if (check_results(&sobj, ODEBUG_STATE_INIT, fixups, warnings))
 		goto out;
-	debug_object_activate(&obj, &descr_type_test);
-	if (check_results(&obj, ODEBUG_STATE_ACTIVE, fixups, warnings))
+	debug_object_activate(&sobj, &descr_type_test);
+	if (check_results(&sobj, ODEBUG_STATE_ACTIVE, fixups, warnings))
 		goto out;
-	__debug_check_no_obj_freed(&obj, sizeof(obj));
-	if (check_results(&obj, ODEBUG_STATE_NONE, ++fixups, ++warnings))
+	__debug_check_no_obj_freed(&sobj, sizeof(sobj));
+	if (check_results(&sobj, ODEBUG_STATE_NONE, ++fixups, ++warnings))
 		goto out;
 #endif
 	pr_info("selftest passed\n");
-- 
2.39.2


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

end of thread, other threads:[~2023-05-02  8:12 UTC | newest]

Thread overview: 25+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-03-03 16:19 [PATCH 1/2] debugobject: fix concurrency issues with is_static_object Schspa Shi
2023-03-03 16:19 ` [PATCH 2/2] debugobject: add unit test for static debug object Schspa Shi
2023-03-23  3:16   ` Schspa Shi
2023-03-23  7:53     ` Thomas Gleixner
2023-03-23  8:44       ` Schspa Shi
2023-04-13 22:39         ` Thomas Gleixner
2023-03-03 17:00 ` [PATCH 1/2] debugobject: fix concurrency issues with is_static_object Waiman Long
2023-03-03 17:53   ` Schspa Shi
2023-03-04  0:14     ` Thomas Gleixner
2023-03-03 23:47 ` Thomas Gleixner
2023-03-22 15:40   ` Schspa Shi
2023-03-22 17:46     ` Thomas Gleixner
2023-03-22 17:55       ` Schspa Shi
2023-03-22 21:17         ` Thomas Gleixner
2023-03-23  3:10           ` Schspa Shi
2023-03-22 18:05       ` Thomas Gleixner
2023-03-22 18:18         ` Schspa Shi
2023-04-12  7:54         ` [PATCH] debugobject: Prevent init race with static objects Thomas Gleixner
2023-04-13  0:17           ` Stephen Boyd
2023-04-13 12:14             ` Thomas Gleixner
2023-05-01 13:40           ` Ido Schimmel
2023-05-01 15:42             ` Thomas Gleixner
2023-05-02  5:53               ` Ido Schimmel
2023-05-02  8:12               ` [tip: core/debugobjects] debugobject: Ensure pool refill (again) tip-bot2 for Thomas Gleixner
2023-04-15 21:20 ` [tip: core/debugobjects] debugobject: Prevent init race with static objects tip-bot2 for Thomas Gleixner

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).