All of lore.kernel.org
 help / color / mirror / Atom feed
From: Yafang Shao <laoar.shao@gmail.com>
To: jpoimboe@kernel.org, jikos@kernel.org, mbenes@suse.cz,
	pmladek@suse.com, joe.lawrence@redhat.com, song@kernel.org
Cc: live-patching@vger.kernel.org, Yafang Shao <laoar.shao@gmail.com>,
	sashiko-bot <sashiko-bot@kernel.org>
Subject: [PATCH v3 1/2] livepatch: Fix UAF of unregistered patch kobjects
Date: Fri, 21 Aug 2026 11:16:47 +0800	[thread overview]
Message-ID: <20260821031648.48195-2-laoar.shao@gmail.com> (raw)
In-Reply-To: <20260821031648.48195-1-laoar.shao@gmail.com>

The kobjects of a livepatch are released via kobject_put().  When
CONFIG_DEBUG_KOBJECT_RELEASE is enabled, kobject_put() does not release
the kobject synchronously but schedules a delayed release with a random
delay of up to 4 seconds (see kobject_release() in lib/kobject.c).

klp_free_patch_finish() only waits for the release of the patch kobject:

  klp_free_patch_finish():
    kobject_put(&patch->kobj);
    wait_for_completion(&patch->finish);

patch->finish is completed by the patch kobject's release callback.  If
the patch kobject was never added to sysfs, or if some child kobjects
were initialized but never added to sysfs (e.g. when klp_enable_patch()
fails after klp_init_patch_early()), those un-added children do not hold
a reference on the patch kobject.  kobject_add() is what takes the parent
reference, so the patch kobject can be released first, completing
patch->finish while the child releases are still pending.

The caller then unloads the livepatch module, which destroys the static
klp_object and klp_func structures.  The delayed child release callbacks
later access this freed memory, causing a use-after-free.

Fix it by making every child kobject hold an explicit reference on its
parent from the moment the object is initialized: klp_init_object_early()
takes a reference on the patch kobject and klp_init_func_early() takes a
reference on the object kobject.  Unlike the reference taken by
kobject_add(), these references also exist for objects that are never
added to sysfs, and the release callbacks drop them unconditionally.
This guarantees the patch kobject is released only after all child
kobjects have been released, so patch->finish cannot be completed before
the static structures are safe to free.

Because kobj->parent is set only by kobject_add(), add explicit
back-pointers, obj->patch and func->obj, so the release callbacks can
find the parent.  Dynamic objects and nop functions are freed by their
release callbacks; save the parent pointer before freeing and drop the
parent reference afterwards.

Reported-by: sashiko-bot <sashiko-bot@kernel.org>
Closes: https://lore.kernel.org/all/20260809094046.50ED31F000E9@smtp.kernel.org/
Suggested-by: Petr Mladek <pmladek@suse.com>
Signed-off-by: Yafang Shao <laoar.shao@gmail.com>
---
 include/linux/livepatch.h |  4 ++++
 kernel/livepatch/core.c   | 10 ++++++++++
 2 files changed, 14 insertions(+)

diff --git a/include/linux/livepatch.h b/include/linux/livepatch.h
index ba9e3988c07c..5f74f79c22b4 100644
--- a/include/linux/livepatch.h
+++ b/include/linux/livepatch.h
@@ -33,6 +33,7 @@
  * @kobj:	kobject for sysfs resources
  * @node:	list node for klp_object func_list
  * @stack_node:	list node for klp_ops func_stack list
+ * @obj:	back pointer to the owning object
  * @old_size:	size of the old function
  * @new_size:	size of the new function
  * @nop:        temporary patch to use the original code again; dyn. allocated
@@ -72,6 +73,7 @@ struct klp_func {
 	struct kobject kobj;
 	struct list_head node;
 	struct list_head stack_node;
+	struct klp_object *obj;
 	unsigned long old_size, new_size;
 	bool nop;
 	bool patched;
@@ -86,6 +88,7 @@ struct klp_func {
  * @kobj:	kobject for sysfs resources
  * @func_list:	dynamic list of the function entries
  * @node:	list node for klp_patch obj_list
+ * @patch:	back pointer to the owning patch
  * @mod:	kernel module associated with the patched object
  *		(NULL for vmlinux)
  * @dynamic:    temporary object for nop functions; dynamically allocated
@@ -101,6 +104,7 @@ struct klp_object {
 	struct kobject kobj;
 	struct list_head func_list;
 	struct list_head node;
+	struct klp_patch *patch;
 	struct module *mod;
 	bool dynamic;
 	bool patched;
diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c
index a240d1144e89..517fe427ff92 100644
--- a/kernel/livepatch/core.c
+++ b/kernel/livepatch/core.c
@@ -646,12 +646,15 @@ static const struct kobj_type klp_ktype_patch = {
 
 static void klp_kobj_release_object(struct kobject *kobj)
 {
+	struct klp_patch *patch;
 	struct klp_object *obj;
 
 	obj = container_of(kobj, struct klp_object, kobj);
+	patch = obj->patch;
 
 	if (obj->dynamic)
 		klp_free_object_dynamic(obj);
+	kobject_put(&patch->kobj);
 }
 
 static const struct kobj_type klp_ktype_object = {
@@ -662,12 +665,15 @@ static const struct kobj_type klp_ktype_object = {
 
 static void klp_kobj_release_func(struct kobject *kobj)
 {
+	struct klp_object *obj;
 	struct klp_func *func;
 
 	func = container_of(kobj, struct klp_func, kobj);
+	obj = func->obj;
 
 	if (func->nop)
 		klp_free_func_nop(func);
+	kobject_put(&obj->kobj);
 }
 
 static const struct kobj_type klp_ktype_func = {
@@ -943,7 +949,9 @@ static void klp_init_func_early(struct klp_object *obj,
 				struct klp_func *func)
 {
 	kobject_init(&func->kobj, &klp_ktype_func);
+	kobject_get(&obj->kobj);
 	list_add_tail(&func->node, &obj->func_list);
+	func->obj = obj;
 }
 
 static void klp_init_object_early(struct klp_patch *patch,
@@ -951,7 +959,9 @@ static void klp_init_object_early(struct klp_patch *patch,
 {
 	INIT_LIST_HEAD(&obj->func_list);
 	kobject_init(&obj->kobj, &klp_ktype_object);
+	kobject_get(&patch->kobj);
 	list_add_tail(&obj->node, &patch->obj_list);
+	obj->patch = patch;
 }
 
 static void klp_init_patch_early(struct klp_patch *patch)
-- 
2.52.0


  reply	other threads:[~2026-08-21  3:17 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-21  3:16 [PATCH v3 0/2] livepatch: Fix issues around CONFIG_DEBUG_KOBJECT_RELEASE Yafang Shao
2026-08-21  3:16 ` Yafang Shao [this message]
2026-08-21 20:45   ` [PATCH v3 1/2] livepatch: Fix UAF of unregistered patch kobjects Song Liu
2026-08-21  3:16 ` [PATCH v3 2/2] selftests/livepatch: filter debug messages in check_result() Yafang Shao
2026-08-21 20:45   ` Song Liu

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260821031648.48195-2-laoar.shao@gmail.com \
    --to=laoar.shao@gmail.com \
    --cc=jikos@kernel.org \
    --cc=joe.lawrence@redhat.com \
    --cc=jpoimboe@kernel.org \
    --cc=live-patching@vger.kernel.org \
    --cc=mbenes@suse.cz \
    --cc=pmladek@suse.com \
    --cc=sashiko-bot@kernel.org \
    --cc=song@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.