Commit ab9517fa authored by Linus Torvalds's avatar Linus Torvalds
Browse files

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
Loading
Loading
Loading
Loading
+78 −122
Original line number Diff line number Diff line
@@ -620,9 +620,8 @@ static void debug_objects_fill_pool(void)
static void
__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_obj *obj;
	unsigned long flags;

	debug_objects_fill_pool();
@@ -643,24 +642,18 @@ __debug_object_init(void *addr, const struct debug_obj_descr *descr, int onstack
	case ODEBUG_STATE_INIT:
	case ODEBUG_STATE_INACTIVE:
		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);
		debug_print_object(obj, "init");
		return;
	default:
		break;
	}

	o = *obj;
	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);
int debug_object_activate(void *addr, const struct debug_obj_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_obj *obj;
	unsigned long flags;
	int ret;

	if (!debug_objects_enabled)
		return 0;
@@ -717,49 +708,38 @@ int debug_object_activate(void *addr, const struct debug_obj_descr *descr)
	raw_spin_lock_irqsave(&db->lock, flags);

	obj = lookup_object_or_alloc(addr, db, descr, false, true);
	if (likely(!IS_ERR_OR_NULL(obj))) {
		bool print_object = false;

	if (unlikely(!obj)) {
		raw_spin_unlock_irqrestore(&db->lock, flags);
		debug_objects_oom();
		return 0;
	} else if (likely(!IS_ERR(obj))) {
		switch (obj->state) {
		case ODEBUG_STATE_INIT:
		case ODEBUG_STATE_INACTIVE:
			obj->state = ODEBUG_STATE_ACTIVE;
			ret = 0;
			break;

		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:
			print_object = true;
			ret = -EINVAL;
			o = *obj;
			break;
		case ODEBUG_STATE_INIT:
		case ODEBUG_STATE_INACTIVE:
			obj->state = ODEBUG_STATE_ACTIVE;
			fallthrough;
		default:
			ret = 0;
			break;
		}
			raw_spin_unlock_irqrestore(&db->lock, flags);
		if (print_object)
			debug_print_object(obj, "activate");
		return ret;
			return 0;
		}
	}

	raw_spin_unlock_irqrestore(&db->lock, flags);
	debug_print_object(&o, "activate");

	/* If NULL the allocation has hit OOM */
	if (!obj) {
		debug_objects_oom();
	switch (o.state) {
	case ODEBUG_STATE_ACTIVE:
	case ODEBUG_STATE_NOTAVAILABLE:
		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);

@@ -770,10 +750,10 @@ EXPORT_SYMBOL_GPL(debug_object_activate);
 */
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_obj *obj;
	unsigned long flags;
	bool print_object = false;

	if (!debug_objects_enabled)
		return;
@@ -785,33 +765,24 @@ void debug_object_deactivate(void *addr, const struct debug_obj_descr *descr)
	obj = lookup_object(addr, db);
	if (obj) {
		switch (obj->state) {
		case ODEBUG_STATE_DESTROYED:
			break;
		case ODEBUG_STATE_INIT:
		case ODEBUG_STATE_INACTIVE:
		case ODEBUG_STATE_ACTIVE:
			if (!obj->astate)
				obj->state = ODEBUG_STATE_INACTIVE;
			else
				print_object = true;
			break;

		case ODEBUG_STATE_DESTROYED:
			print_object = true;
			if (obj->astate)
				break;
			obj->state = ODEBUG_STATE_INACTIVE;
			fallthrough;
		default:
			break;
			raw_spin_unlock_irqrestore(&db->lock, flags);
			return;
		}
		o = *obj;
	}

	raw_spin_unlock_irqrestore(&db->lock, flags);
	if (!obj) {
		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);

@@ -822,11 +793,9 @@ EXPORT_SYMBOL_GPL(debug_object_deactivate);
 */
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_obj *obj;
	unsigned long flags;
	bool print_object = false;

	if (!debug_objects_enabled)
		return;
@@ -836,32 +805,31 @@ void debug_object_destroy(void *addr, const struct debug_obj_descr *descr)
	raw_spin_lock_irqsave(&db->lock, flags);

	obj = lookup_object(addr, db);
	if (!obj)
		goto out_unlock;
	if (!obj) {
		raw_spin_unlock_irqrestore(&db->lock, flags);
		return;
	}

	switch (obj->state) {
	case ODEBUG_STATE_ACTIVE:
	case ODEBUG_STATE_DESTROYED:
		break;
	case ODEBUG_STATE_NONE:
	case ODEBUG_STATE_INIT:
	case ODEBUG_STATE_INACTIVE:
		obj->state = ODEBUG_STATE_DESTROYED;
		break;
	case ODEBUG_STATE_ACTIVE:
		state = obj->state;
		fallthrough;
	default:
		raw_spin_unlock_irqrestore(&db->lock, flags);
		debug_print_object(obj, "destroy");
		debug_object_fixup(descr->fixup_destroy, addr, state);
		return;

	case ODEBUG_STATE_DESTROYED:
		print_object = true;
		break;
	default:
		break;
	}
out_unlock:

	o = *obj;
	raw_spin_unlock_irqrestore(&db->lock, flags);
	if (print_object)
		debug_print_object(obj, "destroy");
	debug_print_object(&o, "destroy");

	if (o.state == ODEBUG_STATE_ACTIVE)
		debug_object_fixup(descr->fixup_destroy, addr, o.state);
}
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)
{
	enum debug_obj_state state;
	struct debug_obj *obj, o;
	struct debug_bucket *db;
	struct debug_obj *obj;
	unsigned long flags;

	if (!debug_objects_enabled)
@@ -885,24 +852,26 @@ void debug_object_free(void *addr, const struct debug_obj_descr *descr)
	raw_spin_lock_irqsave(&db->lock, flags);

	obj = lookup_object(addr, db);
	if (!obj)
		goto out_unlock;
	if (!obj) {
		raw_spin_unlock_irqrestore(&db->lock, flags);
		return;
	}

	switch (obj->state) {
	case ODEBUG_STATE_ACTIVE:
		state = obj->state;
		raw_spin_unlock_irqrestore(&db->lock, flags);
		debug_print_object(obj, "free");
		debug_object_fixup(descr->fixup_free, addr, state);
		return;
		break;
	default:
		hlist_del(&obj->node);
		raw_spin_unlock_irqrestore(&db->lock, flags);
		free_object(obj);
		return;
	}
out_unlock:

	o = *obj;
	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);

@@ -954,10 +923,10 @@ void
debug_object_active_state(void *addr, const struct debug_obj_descr *descr,
			  unsigned int expect, unsigned int next)
{
	struct debug_obj o = { .object = addr, .state = ODEBUG_STATE_NOTAVAILABLE, .descr = descr };
	struct debug_bucket *db;
	struct debug_obj *obj;
	unsigned long flags;
	bool print_object = false;

	if (!debug_objects_enabled)
		return;
@@ -970,28 +939,19 @@ debug_object_active_state(void *addr, const struct debug_obj_descr *descr,
	if (obj) {
		switch (obj->state) {
		case ODEBUG_STATE_ACTIVE:
			if (obj->astate == expect)
				obj->astate = next;
			else
				print_object = true;
			if (obj->astate != expect)
				break;

			obj->astate = next;
			raw_spin_unlock_irqrestore(&db->lock, flags);
			return;
		default:
			print_object = true;
			break;
		}
		o = *obj;
	}

	raw_spin_unlock_irqrestore(&db->lock, flags);
	if (!obj) {
		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);

@@ -999,12 +959,10 @@ EXPORT_SYMBOL_GPL(debug_object_active_state);
static void __debug_check_no_obj_freed(const void *address, unsigned long size)
{
	unsigned long flags, oaddr, saddr, eaddr, paddr, chunks;
	const struct debug_obj_descr *descr;
	enum debug_obj_state state;
	int cnt, objs_checked = 0;
	struct debug_obj *obj, o;
	struct debug_bucket *db;
	struct hlist_node *tmp;
	struct debug_obj *obj;
	int cnt, objs_checked = 0;

	saddr = (unsigned long) address;
	eaddr = saddr + size;
@@ -1026,12 +984,10 @@ static void __debug_check_no_obj_freed(const void *address, unsigned long size)

			switch (obj->state) {
			case ODEBUG_STATE_ACTIVE:
				descr = obj->descr;
				state = obj->state;
				o = *obj;
				raw_spin_unlock_irqrestore(&db->lock, flags);
				debug_print_object(obj, "free");
				debug_object_fixup(descr->fixup_free,
						   (void *) oaddr, state);
				debug_print_object(&o, "free");
				debug_object_fixup(o.descr->fixup_free, (void *)oaddr, o.state);
				goto repeat;
			default:
				hlist_del(&obj->node);