| From 5023aa331f659b748c819eaafd50e79d19669957 Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Thu, 4 Jun 2020 19:46:46 +0200 |
| Subject: kobject: Avoid premature parent object freeing in kobject_cleanup() |
| |
| From: Heikki Krogerus <heikki.krogerus@linux.intel.com> |
| |
| [ Upstream commit 079ad2fb4bf9eba8a0aaab014b49705cd7f07c66 ] |
| |
| If kobject_del() is invoked by kobject_cleanup() to delete the |
| target kobject, it may cause its parent kobject to be freed |
| before invoking the target kobject's ->release() method, which |
| effectively means freeing the parent before dealing with the |
| child entirely. |
| |
| That is confusing at best and it may also lead to functional |
| issues if the callers of kobject_cleanup() are not careful enough |
| about the order in which these calls are made, so avoid the |
| problem by making kobject_cleanup() drop the last reference to |
| the target kobject's parent at the end, after invoking the target |
| kobject's ->release() method. |
| |
| [ rjw: Rewrite the subject and changelog, make kobject_cleanup() |
| drop the parent reference only when __kobject_del() has been |
| called. ] |
| |
| Reported-by: Naresh Kamboju <naresh.kamboju@linaro.org> |
| Reported-by: kernel test robot <rong.a.chen@intel.com> |
| Fixes: 7589238a8cf3 ("Revert "software node: Simplify software_node_release() function"") |
| Suggested-by: Rafael J. Wysocki <rafael@kernel.org> |
| Signed-off-by: Heikki Krogerus <heikki.krogerus@linux.intel.com> |
| Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> |
| Link: https://lore.kernel.org/r/1908555.IiAGLGrh1Z@kreacher |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| lib/kobject.c | 33 +++++++++++++++++++++++---------- |
| 1 file changed, 23 insertions(+), 10 deletions(-) |
| |
| diff --git a/lib/kobject.c b/lib/kobject.c |
| index 83198cb37d8d9..386873bdd51c9 100644 |
| --- a/lib/kobject.c |
| +++ b/lib/kobject.c |
| @@ -599,14 +599,7 @@ int kobject_move(struct kobject *kobj, struct kobject *new_parent) |
| } |
| EXPORT_SYMBOL_GPL(kobject_move); |
| |
| -/** |
| - * kobject_del() - Unlink kobject from hierarchy. |
| - * @kobj: object. |
| - * |
| - * This is the function that should be called to delete an object |
| - * successfully added via kobject_add(). |
| - */ |
| -void kobject_del(struct kobject *kobj) |
| +static void __kobject_del(struct kobject *kobj) |
| { |
| struct kernfs_node *sd; |
| const struct kobj_type *ktype; |
| @@ -625,9 +618,23 @@ void kobject_del(struct kobject *kobj) |
| |
| kobj->state_in_sysfs = 0; |
| kobj_kset_leave(kobj); |
| - kobject_put(kobj->parent); |
| kobj->parent = NULL; |
| } |
| + |
| +/** |
| + * kobject_del() - Unlink kobject from hierarchy. |
| + * @kobj: object. |
| + * |
| + * This is the function that should be called to delete an object |
| + * successfully added via kobject_add(). |
| + */ |
| +void kobject_del(struct kobject *kobj) |
| +{ |
| + struct kobject *parent = kobj->parent; |
| + |
| + __kobject_del(kobj); |
| + kobject_put(parent); |
| +} |
| EXPORT_SYMBOL(kobject_del); |
| |
| /** |
| @@ -663,6 +670,7 @@ EXPORT_SYMBOL(kobject_get_unless_zero); |
| */ |
| static void kobject_cleanup(struct kobject *kobj) |
| { |
| + struct kobject *parent = kobj->parent; |
| struct kobj_type *t = get_ktype(kobj); |
| const char *name = kobj->name; |
| |
| @@ -684,7 +692,10 @@ static void kobject_cleanup(struct kobject *kobj) |
| if (kobj->state_in_sysfs) { |
| pr_debug("kobject: '%s' (%p): auto cleanup kobject_del\n", |
| kobject_name(kobj), kobj); |
| - kobject_del(kobj); |
| + __kobject_del(kobj); |
| + } else { |
| + /* avoid dropping the parent reference unnecessarily */ |
| + parent = NULL; |
| } |
| |
| if (t && t->release) { |
| @@ -698,6 +709,8 @@ static void kobject_cleanup(struct kobject *kobj) |
| pr_debug("kobject: '%s': free name\n", name); |
| kfree_const(name); |
| } |
| + |
| + kobject_put(parent); |
| } |
| |
| #ifdef CONFIG_DEBUG_KOBJECT_RELEASE |
| -- |
| 2.25.1 |
| |