Commit ab9517fa authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'core-debugobjects-2024-01-08' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull debugobject update from Ingo Molnar:

 - Make tracking object use more robust: it's not safe to access a
   tracking object after releasing the hashbucket lock. Create a
   persistent copy for debug printouts instead.

* tag 'core-debugobjects-2024-01-08' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  debugobjects: Stop accessing objects after releasing hash bucket lock
parents 669d089a 9bb63626
...@@ -620,9 +620,8 @@ static void debug_objects_fill_pool(void) ...@@ -620,9 +620,8 @@ static void debug_objects_fill_pool(void)
static void static void
__debug_object_init(void *addr, const struct debug_obj_descr *descr, int onstack) __debug_object_init(void *addr, const struct debug_obj_descr *descr, int onstack)
{ {
enum debug_obj_state state; struct debug_obj *obj, o;
struct debug_bucket *db; struct debug_bucket *db;
struct debug_obj *obj;
unsigned long flags; unsigned long flags;
debug_objects_fill_pool(); debug_objects_fill_pool();
...@@ -643,24 +642,18 @@ __debug_object_init(void *addr, const struct debug_obj_descr *descr, int onstack ...@@ -643,24 +642,18 @@ __debug_object_init(void *addr, const struct debug_obj_descr *descr, int onstack
case ODEBUG_STATE_INIT: case ODEBUG_STATE_INIT:
case ODEBUG_STATE_INACTIVE: case ODEBUG_STATE_INACTIVE:
obj->state = ODEBUG_STATE_INIT; obj->state = ODEBUG_STATE_INIT;
break;
case ODEBUG_STATE_ACTIVE:
state = obj->state;
raw_spin_unlock_irqrestore(&db->lock, flags);
debug_print_object(obj, "init");
debug_object_fixup(descr->fixup_init, addr, state);
return;
case ODEBUG_STATE_DESTROYED:
raw_spin_unlock_irqrestore(&db->lock, flags); raw_spin_unlock_irqrestore(&db->lock, flags);
debug_print_object(obj, "init");
return; return;
default: default:
break; break;
} }
o = *obj;
raw_spin_unlock_irqrestore(&db->lock, flags); raw_spin_unlock_irqrestore(&db->lock, flags);
debug_print_object(&o, "init");
if (o.state == ODEBUG_STATE_ACTIVE)
debug_object_fixup(descr->fixup_init, addr, o.state);
} }
/** /**
...@@ -701,11 +694,9 @@ EXPORT_SYMBOL_GPL(debug_object_init_on_stack); ...@@ -701,11 +694,9 @@ EXPORT_SYMBOL_GPL(debug_object_init_on_stack);
int debug_object_activate(void *addr, const struct debug_obj_descr *descr) int debug_object_activate(void *addr, const struct debug_obj_descr *descr)
{ {
struct debug_obj o = { .object = addr, .state = ODEBUG_STATE_NOTAVAILABLE, .descr = descr }; struct debug_obj o = { .object = addr, .state = ODEBUG_STATE_NOTAVAILABLE, .descr = descr };
enum debug_obj_state state;
struct debug_bucket *db; struct debug_bucket *db;
struct debug_obj *obj; struct debug_obj *obj;
unsigned long flags; unsigned long flags;
int ret;
if (!debug_objects_enabled) if (!debug_objects_enabled)
return 0; return 0;
...@@ -717,49 +708,38 @@ int debug_object_activate(void *addr, const struct debug_obj_descr *descr) ...@@ -717,49 +708,38 @@ int debug_object_activate(void *addr, const struct debug_obj_descr *descr)
raw_spin_lock_irqsave(&db->lock, flags); raw_spin_lock_irqsave(&db->lock, flags);
obj = lookup_object_or_alloc(addr, db, descr, false, true); obj = lookup_object_or_alloc(addr, db, descr, false, true);
if (likely(!IS_ERR_OR_NULL(obj))) { if (unlikely(!obj)) {
bool print_object = false; raw_spin_unlock_irqrestore(&db->lock, flags);
debug_objects_oom();
return 0;
} else if (likely(!IS_ERR(obj))) {
switch (obj->state) { switch (obj->state) {
case ODEBUG_STATE_INIT:
case ODEBUG_STATE_INACTIVE:
obj->state = ODEBUG_STATE_ACTIVE;
ret = 0;
break;
case ODEBUG_STATE_ACTIVE: case ODEBUG_STATE_ACTIVE:
state = obj->state;
raw_spin_unlock_irqrestore(&db->lock, flags);
debug_print_object(obj, "activate");
ret = debug_object_fixup(descr->fixup_activate, addr, state);
return ret ? 0 : -EINVAL;
case ODEBUG_STATE_DESTROYED: case ODEBUG_STATE_DESTROYED:
print_object = true; o = *obj;
ret = -EINVAL;
break; break;
case ODEBUG_STATE_INIT:
case ODEBUG_STATE_INACTIVE:
obj->state = ODEBUG_STATE_ACTIVE;
fallthrough;
default: default:
ret = 0; raw_spin_unlock_irqrestore(&db->lock, flags);
break; return 0;
} }
raw_spin_unlock_irqrestore(&db->lock, flags);
if (print_object)
debug_print_object(obj, "activate");
return ret;
} }
raw_spin_unlock_irqrestore(&db->lock, flags); raw_spin_unlock_irqrestore(&db->lock, flags);
debug_print_object(&o, "activate");
/* If NULL the allocation has hit OOM */ switch (o.state) {
if (!obj) { case ODEBUG_STATE_ACTIVE:
debug_objects_oom(); case ODEBUG_STATE_NOTAVAILABLE:
return 0; if (debug_object_fixup(descr->fixup_activate, addr, o.state))
return 0;
fallthrough;
default:
return -EINVAL;
} }
/* Object is neither static nor tracked. It's not initialized */
debug_print_object(&o, "activate");
ret = debug_object_fixup(descr->fixup_activate, addr, ODEBUG_STATE_NOTAVAILABLE);
return ret ? 0 : -EINVAL;
} }
EXPORT_SYMBOL_GPL(debug_object_activate); EXPORT_SYMBOL_GPL(debug_object_activate);
...@@ -770,10 +750,10 @@ EXPORT_SYMBOL_GPL(debug_object_activate); ...@@ -770,10 +750,10 @@ EXPORT_SYMBOL_GPL(debug_object_activate);
*/ */
void debug_object_deactivate(void *addr, const struct debug_obj_descr *descr) void debug_object_deactivate(void *addr, const struct debug_obj_descr *descr)
{ {
struct debug_obj o = { .object = addr, .state = ODEBUG_STATE_NOTAVAILABLE, .descr = descr };
struct debug_bucket *db; struct debug_bucket *db;
struct debug_obj *obj; struct debug_obj *obj;
unsigned long flags; unsigned long flags;
bool print_object = false;
if (!debug_objects_enabled) if (!debug_objects_enabled)
return; return;
...@@ -785,33 +765,24 @@ void debug_object_deactivate(void *addr, const struct debug_obj_descr *descr) ...@@ -785,33 +765,24 @@ void debug_object_deactivate(void *addr, const struct debug_obj_descr *descr)
obj = lookup_object(addr, db); obj = lookup_object(addr, db);
if (obj) { if (obj) {
switch (obj->state) { switch (obj->state) {
case ODEBUG_STATE_DESTROYED:
break;
case ODEBUG_STATE_INIT: case ODEBUG_STATE_INIT:
case ODEBUG_STATE_INACTIVE: case ODEBUG_STATE_INACTIVE:
case ODEBUG_STATE_ACTIVE: case ODEBUG_STATE_ACTIVE:
if (!obj->astate) if (obj->astate)
obj->state = ODEBUG_STATE_INACTIVE; break;
else obj->state = ODEBUG_STATE_INACTIVE;
print_object = true; fallthrough;
break;
case ODEBUG_STATE_DESTROYED:
print_object = true;
break;
default: default:
break; raw_spin_unlock_irqrestore(&db->lock, flags);
return;
} }
o = *obj;
} }
raw_spin_unlock_irqrestore(&db->lock, flags); raw_spin_unlock_irqrestore(&db->lock, flags);
if (!obj) { debug_print_object(&o, "deactivate");
struct debug_obj o = { .object = addr,
.state = ODEBUG_STATE_NOTAVAILABLE,
.descr = descr };
debug_print_object(&o, "deactivate");
} else if (print_object) {
debug_print_object(obj, "deactivate");
}
} }
EXPORT_SYMBOL_GPL(debug_object_deactivate); EXPORT_SYMBOL_GPL(debug_object_deactivate);
...@@ -822,11 +793,9 @@ EXPORT_SYMBOL_GPL(debug_object_deactivate); ...@@ -822,11 +793,9 @@ EXPORT_SYMBOL_GPL(debug_object_deactivate);
*/ */
void debug_object_destroy(void *addr, const struct debug_obj_descr *descr) void debug_object_destroy(void *addr, const struct debug_obj_descr *descr)
{ {
enum debug_obj_state state; struct debug_obj *obj, o;
struct debug_bucket *db; struct debug_bucket *db;
struct debug_obj *obj;
unsigned long flags; unsigned long flags;
bool print_object = false;
if (!debug_objects_enabled) if (!debug_objects_enabled)
return; return;
...@@ -836,32 +805,31 @@ void debug_object_destroy(void *addr, const struct debug_obj_descr *descr) ...@@ -836,32 +805,31 @@ void debug_object_destroy(void *addr, const struct debug_obj_descr *descr)
raw_spin_lock_irqsave(&db->lock, flags); raw_spin_lock_irqsave(&db->lock, flags);
obj = lookup_object(addr, db); obj = lookup_object(addr, db);
if (!obj) if (!obj) {
goto out_unlock; raw_spin_unlock_irqrestore(&db->lock, flags);
return;
}
switch (obj->state) { switch (obj->state) {
case ODEBUG_STATE_ACTIVE:
case ODEBUG_STATE_DESTROYED:
break;
case ODEBUG_STATE_NONE: case ODEBUG_STATE_NONE:
case ODEBUG_STATE_INIT: case ODEBUG_STATE_INIT:
case ODEBUG_STATE_INACTIVE: case ODEBUG_STATE_INACTIVE:
obj->state = ODEBUG_STATE_DESTROYED; obj->state = ODEBUG_STATE_DESTROYED;
break; fallthrough;
case ODEBUG_STATE_ACTIVE: default:
state = obj->state;
raw_spin_unlock_irqrestore(&db->lock, flags); raw_spin_unlock_irqrestore(&db->lock, flags);
debug_print_object(obj, "destroy");
debug_object_fixup(descr->fixup_destroy, addr, state);
return; return;
case ODEBUG_STATE_DESTROYED:
print_object = true;
break;
default:
break;
} }
out_unlock:
o = *obj;
raw_spin_unlock_irqrestore(&db->lock, flags); raw_spin_unlock_irqrestore(&db->lock, flags);
if (print_object) debug_print_object(&o, "destroy");
debug_print_object(obj, "destroy");
if (o.state == ODEBUG_STATE_ACTIVE)
debug_object_fixup(descr->fixup_destroy, addr, o.state);
} }
EXPORT_SYMBOL_GPL(debug_object_destroy); EXPORT_SYMBOL_GPL(debug_object_destroy);
...@@ -872,9 +840,8 @@ EXPORT_SYMBOL_GPL(debug_object_destroy); ...@@ -872,9 +840,8 @@ EXPORT_SYMBOL_GPL(debug_object_destroy);
*/ */
void debug_object_free(void *addr, const struct debug_obj_descr *descr) void debug_object_free(void *addr, const struct debug_obj_descr *descr)
{ {
enum debug_obj_state state; struct debug_obj *obj, o;
struct debug_bucket *db; struct debug_bucket *db;
struct debug_obj *obj;
unsigned long flags; unsigned long flags;
if (!debug_objects_enabled) if (!debug_objects_enabled)
...@@ -885,24 +852,26 @@ void debug_object_free(void *addr, const struct debug_obj_descr *descr) ...@@ -885,24 +852,26 @@ void debug_object_free(void *addr, const struct debug_obj_descr *descr)
raw_spin_lock_irqsave(&db->lock, flags); raw_spin_lock_irqsave(&db->lock, flags);
obj = lookup_object(addr, db); obj = lookup_object(addr, db);
if (!obj) if (!obj) {
goto out_unlock; raw_spin_unlock_irqrestore(&db->lock, flags);
return;
}
switch (obj->state) { switch (obj->state) {
case ODEBUG_STATE_ACTIVE: case ODEBUG_STATE_ACTIVE:
state = obj->state; break;
raw_spin_unlock_irqrestore(&db->lock, flags);
debug_print_object(obj, "free");
debug_object_fixup(descr->fixup_free, addr, state);
return;
default: default:
hlist_del(&obj->node); hlist_del(&obj->node);
raw_spin_unlock_irqrestore(&db->lock, flags); raw_spin_unlock_irqrestore(&db->lock, flags);
free_object(obj); free_object(obj);
return; return;
} }
out_unlock:
o = *obj;
raw_spin_unlock_irqrestore(&db->lock, flags); raw_spin_unlock_irqrestore(&db->lock, flags);
debug_print_object(&o, "free");
debug_object_fixup(descr->fixup_free, addr, o.state);
} }
EXPORT_SYMBOL_GPL(debug_object_free); EXPORT_SYMBOL_GPL(debug_object_free);
...@@ -954,10 +923,10 @@ void ...@@ -954,10 +923,10 @@ void
debug_object_active_state(void *addr, const struct debug_obj_descr *descr, debug_object_active_state(void *addr, const struct debug_obj_descr *descr,
unsigned int expect, unsigned int next) unsigned int expect, unsigned int next)
{ {
struct debug_obj o = { .object = addr, .state = ODEBUG_STATE_NOTAVAILABLE, .descr = descr };
struct debug_bucket *db; struct debug_bucket *db;
struct debug_obj *obj; struct debug_obj *obj;
unsigned long flags; unsigned long flags;
bool print_object = false;
if (!debug_objects_enabled) if (!debug_objects_enabled)
return; return;
...@@ -970,28 +939,19 @@ debug_object_active_state(void *addr, const struct debug_obj_descr *descr, ...@@ -970,28 +939,19 @@ debug_object_active_state(void *addr, const struct debug_obj_descr *descr,
if (obj) { if (obj) {
switch (obj->state) { switch (obj->state) {
case ODEBUG_STATE_ACTIVE: case ODEBUG_STATE_ACTIVE:
if (obj->astate == expect) if (obj->astate != expect)
obj->astate = next; break;
else obj->astate = next;
print_object = true; raw_spin_unlock_irqrestore(&db->lock, flags);
break; return;
default: default:
print_object = true;
break; break;
} }
o = *obj;
} }
raw_spin_unlock_irqrestore(&db->lock, flags); raw_spin_unlock_irqrestore(&db->lock, flags);
if (!obj) { debug_print_object(&o, "active_state");
struct debug_obj o = { .object = addr,
.state = ODEBUG_STATE_NOTAVAILABLE,
.descr = descr };
debug_print_object(&o, "active_state");
} else if (print_object) {
debug_print_object(obj, "active_state");
}
} }
EXPORT_SYMBOL_GPL(debug_object_active_state); EXPORT_SYMBOL_GPL(debug_object_active_state);
...@@ -999,12 +959,10 @@ EXPORT_SYMBOL_GPL(debug_object_active_state); ...@@ -999,12 +959,10 @@ EXPORT_SYMBOL_GPL(debug_object_active_state);
static void __debug_check_no_obj_freed(const void *address, unsigned long size) static void __debug_check_no_obj_freed(const void *address, unsigned long size)
{ {
unsigned long flags, oaddr, saddr, eaddr, paddr, chunks; unsigned long flags, oaddr, saddr, eaddr, paddr, chunks;
const struct debug_obj_descr *descr; int cnt, objs_checked = 0;
enum debug_obj_state state; struct debug_obj *obj, o;
struct debug_bucket *db; struct debug_bucket *db;
struct hlist_node *tmp; struct hlist_node *tmp;
struct debug_obj *obj;
int cnt, objs_checked = 0;
saddr = (unsigned long) address; saddr = (unsigned long) address;
eaddr = saddr + size; eaddr = saddr + size;
...@@ -1026,12 +984,10 @@ static void __debug_check_no_obj_freed(const void *address, unsigned long size) ...@@ -1026,12 +984,10 @@ static void __debug_check_no_obj_freed(const void *address, unsigned long size)
switch (obj->state) { switch (obj->state) {
case ODEBUG_STATE_ACTIVE: case ODEBUG_STATE_ACTIVE:
descr = obj->descr; o = *obj;
state = obj->state;
raw_spin_unlock_irqrestore(&db->lock, flags); raw_spin_unlock_irqrestore(&db->lock, flags);
debug_print_object(obj, "free"); debug_print_object(&o, "free");
debug_object_fixup(descr->fixup_free, debug_object_fixup(o.descr->fixup_free, (void *)oaddr, o.state);
(void *) oaddr, state);
goto repeat; goto repeat;
default: default:
hlist_del(&obj->node); hlist_del(&obj->node);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment