Skip to content

Commit 63a7596

Browse files
committed
debugobject: Prevent init race with static objects
Statically initialized objects are usually not initialized via the init() function of the subsystem. They are special cased and the subsystem provides a function to validate whether an object which is not yet tracked by debugobjects is statically initialized. This means the object is started to be tracked on first use, e.g. activation. This works perfectly fine, unless there are two concurrent operations on that object. Schspa decoded the problem: T0 T1 debug_object_assert_init(addr) lock_hash_bucket() obj = lookup_object(addr); if (!obj) { unlock_hash_bucket(); - > preemption lock_subsytem_object(addr); activate_object(addr) lock_hash_bucket(); obj = lookup_object(addr); if (!obj) { unlock_hash_bucket(); if (is_static_object(addr)) init_and_track(addr); lock_hash_bucket(); obj = lookup_object(addr); obj->state = ACTIVATED; unlock_hash_bucket(); subsys function modifies content of addr, so static object detection does not longer work. unlock_subsytem_object(addr); if (is_static_object(addr)) <- Fails debugobject emits a warning and invokes the fixup function which reinitializes the already active object in the worst case. This race exists forever, but was never observed until mod_timer() got a debug_object_assert_init() added which is outside of the timer base lock held section right at the beginning of the function to cover the lockless early exit points too. Rework the code so that the lookup, the static object check and the tracking object association happens atomically under the hash bucket lock. This prevents the issue completely as all callers are serialized on the hash bucket lock and therefore cannot observe inconsistent state. Fixes: 3ac7fe5 ("infrastructure to debug (dynamic) objects") Reported-by: [email protected] Debugged-by: Schspa Shi <[email protected]> Signed-off-by: Thomas Gleixner <[email protected]> Reviewed-by: Stephen Boyd <[email protected]> Link: https://syzkaller.appspot.com/bug?id=22c8a5938eab640d1c6bcc0e3dc7be519d878462 Link: https://lore.kernel.org/lkml/[email protected] Link: https://lore.kernel.org/r/87zg7dzgao.ffs@tglx
1 parent 09a9639 commit 63a7596

File tree

1 file changed

+66
-59
lines changed

1 file changed

+66
-59
lines changed

lib/debugobjects.c

Lines changed: 66 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -216,10 +216,6 @@ static struct debug_obj *__alloc_object(struct hlist_head *list)
216216
return obj;
217217
}
218218

219-
/*
220-
* Allocate a new object. If the pool is empty, switch off the debugger.
221-
* Must be called with interrupts disabled.
222-
*/
223219
static struct debug_obj *
224220
alloc_object(void *addr, struct debug_bucket *b, const struct debug_obj_descr *descr)
225221
{
@@ -552,11 +548,49 @@ static void debug_object_is_on_stack(void *addr, int onstack)
552548
WARN_ON(1);
553549
}
554550

551+
static struct debug_obj *lookup_object_or_alloc(void *addr, struct debug_bucket *b,
552+
const struct debug_obj_descr *descr,
553+
bool onstack, bool alloc_ifstatic)
554+
{
555+
struct debug_obj *obj = lookup_object(addr, b);
556+
enum debug_obj_state state = ODEBUG_STATE_NONE;
557+
558+
if (likely(obj))
559+
return obj;
560+
561+
/*
562+
* debug_object_init() unconditionally allocates untracked
563+
* objects. It does not matter whether it is a static object or
564+
* not.
565+
*
566+
* debug_object_assert_init() and debug_object_activate() allow
567+
* allocation only if the descriptor callback confirms that the
568+
* object is static and considered initialized. For non-static
569+
* objects the allocation needs to be done from the fixup callback.
570+
*/
571+
if (unlikely(alloc_ifstatic)) {
572+
if (!descr->is_static_object || !descr->is_static_object(addr))
573+
return ERR_PTR(-ENOENT);
574+
/* Statically allocated objects are considered initialized */
575+
state = ODEBUG_STATE_INIT;
576+
}
577+
578+
obj = alloc_object(addr, b, descr);
579+
if (likely(obj)) {
580+
obj->state = state;
581+
debug_object_is_on_stack(addr, onstack);
582+
return obj;
583+
}
584+
585+
/* Out of memory. Do the cleanup outside of the locked region */
586+
debug_objects_enabled = 0;
587+
return NULL;
588+
}
589+
555590
static void
556591
__debug_object_init(void *addr, const struct debug_obj_descr *descr, int onstack)
557592
{
558593
enum debug_obj_state state;
559-
bool check_stack = false;
560594
struct debug_bucket *db;
561595
struct debug_obj *obj;
562596
unsigned long flags;
@@ -572,16 +606,11 @@ __debug_object_init(void *addr, const struct debug_obj_descr *descr, int onstack
572606

573607
raw_spin_lock_irqsave(&db->lock, flags);
574608

575-
obj = lookup_object(addr, db);
576-
if (!obj) {
577-
obj = alloc_object(addr, db, descr);
578-
if (!obj) {
579-
debug_objects_enabled = 0;
580-
raw_spin_unlock_irqrestore(&db->lock, flags);
581-
debug_objects_oom();
582-
return;
583-
}
584-
check_stack = true;
609+
obj = lookup_object_or_alloc(addr, db, descr, onstack, false);
610+
if (unlikely(!obj)) {
611+
raw_spin_unlock_irqrestore(&db->lock, flags);
612+
debug_objects_oom();
613+
return;
585614
}
586615

587616
switch (obj->state) {
@@ -607,8 +636,6 @@ __debug_object_init(void *addr, const struct debug_obj_descr *descr, int onstack
607636
}
608637

609638
raw_spin_unlock_irqrestore(&db->lock, flags);
610-
if (check_stack)
611-
debug_object_is_on_stack(addr, onstack);
612639
}
613640

614641
/**
@@ -648,14 +675,12 @@ EXPORT_SYMBOL_GPL(debug_object_init_on_stack);
648675
*/
649676
int debug_object_activate(void *addr, const struct debug_obj_descr *descr)
650677
{
678+
struct debug_obj o = { .object = addr, .state = ODEBUG_STATE_NOTAVAILABLE, .descr = descr };
651679
enum debug_obj_state state;
652680
struct debug_bucket *db;
653681
struct debug_obj *obj;
654682
unsigned long flags;
655683
int ret;
656-
struct debug_obj o = { .object = addr,
657-
.state = ODEBUG_STATE_NOTAVAILABLE,
658-
.descr = descr };
659684

660685
if (!debug_objects_enabled)
661686
return 0;
@@ -664,8 +689,8 @@ int debug_object_activate(void *addr, const struct debug_obj_descr *descr)
664689

665690
raw_spin_lock_irqsave(&db->lock, flags);
666691

667-
obj = lookup_object(addr, db);
668-
if (obj) {
692+
obj = lookup_object_or_alloc(addr, db, descr, false, true);
693+
if (likely(!IS_ERR_OR_NULL(obj))) {
669694
bool print_object = false;
670695

671696
switch (obj->state) {
@@ -698,24 +723,16 @@ int debug_object_activate(void *addr, const struct debug_obj_descr *descr)
698723

699724
raw_spin_unlock_irqrestore(&db->lock, flags);
700725

701-
/*
702-
* We are here when a static object is activated. We
703-
* let the type specific code confirm whether this is
704-
* true or not. if true, we just make sure that the
705-
* static object is tracked in the object tracker. If
706-
* not, this must be a bug, so we try to fix it up.
707-
*/
708-
if (descr->is_static_object && descr->is_static_object(addr)) {
709-
/* track this static object */
710-
debug_object_init(addr, descr);
711-
debug_object_activate(addr, descr);
712-
} else {
713-
debug_print_object(&o, "activate");
714-
ret = debug_object_fixup(descr->fixup_activate, addr,
715-
ODEBUG_STATE_NOTAVAILABLE);
716-
return ret ? 0 : -EINVAL;
726+
/* If NULL the allocation has hit OOM */
727+
if (!obj) {
728+
debug_objects_oom();
729+
return 0;
717730
}
718-
return 0;
731+
732+
/* Object is neither static nor tracked. It's not initialized */
733+
debug_print_object(&o, "activate");
734+
ret = debug_object_fixup(descr->fixup_activate, addr, ODEBUG_STATE_NOTAVAILABLE);
735+
return ret ? 0 : -EINVAL;
719736
}
720737
EXPORT_SYMBOL_GPL(debug_object_activate);
721738

@@ -869,6 +886,7 @@ EXPORT_SYMBOL_GPL(debug_object_free);
869886
*/
870887
void debug_object_assert_init(void *addr, const struct debug_obj_descr *descr)
871888
{
889+
struct debug_obj o = { .object = addr, .state = ODEBUG_STATE_NOTAVAILABLE, .descr = descr };
872890
struct debug_bucket *db;
873891
struct debug_obj *obj;
874892
unsigned long flags;
@@ -879,31 +897,20 @@ void debug_object_assert_init(void *addr, const struct debug_obj_descr *descr)
879897
db = get_bucket((unsigned long) addr);
880898

881899
raw_spin_lock_irqsave(&db->lock, flags);
900+
obj = lookup_object_or_alloc(addr, db, descr, false, true);
901+
raw_spin_unlock_irqrestore(&db->lock, flags);
902+
if (likely(!IS_ERR_OR_NULL(obj)))
903+
return;
882904

883-
obj = lookup_object(addr, db);
905+
/* If NULL the allocation has hit OOM */
884906
if (!obj) {
885-
struct debug_obj o = { .object = addr,
886-
.state = ODEBUG_STATE_NOTAVAILABLE,
887-
.descr = descr };
888-
889-
raw_spin_unlock_irqrestore(&db->lock, flags);
890-
/*
891-
* Maybe the object is static, and we let the type specific
892-
* code confirm. Track this static object if true, else invoke
893-
* fixup.
894-
*/
895-
if (descr->is_static_object && descr->is_static_object(addr)) {
896-
/* Track this static object */
897-
debug_object_init(addr, descr);
898-
} else {
899-
debug_print_object(&o, "assert_init");
900-
debug_object_fixup(descr->fixup_assert_init, addr,
901-
ODEBUG_STATE_NOTAVAILABLE);
902-
}
907+
debug_objects_oom();
903908
return;
904909
}
905910

906-
raw_spin_unlock_irqrestore(&db->lock, flags);
911+
/* Object is neither tracked nor static. It's not initialized. */
912+
debug_print_object(&o, "assert_init");
913+
debug_object_fixup(descr->fixup_assert_init, addr, ODEBUG_STATE_NOTAVAILABLE);
907914
}
908915
EXPORT_SYMBOL_GPL(debug_object_assert_init);
909916

0 commit comments

Comments
 (0)