Re: [PATCH 2/2] livepatch: Fix UAF of unregistered patch kobjects

Yafang Shao <[email protected]>
Newsgroups org.kernel.vger.live-patching,dev.linux.lists.sashiko-reviews
Message-ID <CALOAHbAEc_hKR6Fz4JkVyZkhjHepBe_s_mpTe3Q8sa0V9oH2Lw@mail.gmail.com>
On Thu, Aug 13, 2026 at 11:16 AM <[email protected]> wrote:
>
> Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
>
> New issues:
> - [Critical] The newly added release signaling logic introduces a direct Use-After-Free when processing dynamically allocated objects and NOP functions in the kobject release paths.
>
> Pre-existing issues:
> - [High] The patch fails to fully resolve the original delayed kobject release Use-After-Free if partial initialization fails after the patch kobject has been added, leaving child kobjects un-added.
> --
>
> --- Patch [2]: [PATCH 2/2] livepatch: Fix UAF of unregistered patch kobjects ---
> commit ac38837d810b471eed544191021bf254f8b9e585
> Author: Yafang Shao <[email protected]>
>
> livepatch: Fix UAF of unregistered patch kobjects
>
> When klp_enable_patch() fails after klp_init_patch_early() has run,
> delayed kobject releases can cause use-after-free bugs when the module
> is unloaded. This patch tracks pending kobject releases and waits for
> them before completion.
>
> Link: https://lore.kernel.org/all/[email protected]/
>
> > diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c
> > index 28d15ba58a26d..e07b026feeb99 100644
> > --- a/kernel/livepatch/core.c
> > +++ b/kernel/livepatch/core.c
>
> [ ... ]
>
> > @@ -652,6 +653,14 @@ static void klp_kobj_release_object(struct kobject *kobj)
> >
> >       if (obj->dynamic)
> >               klp_free_object_dynamic(obj);
> > +
> > +     /*
> > +      * The object kobject was initialized but never added to sysfs.
> > +      * Signal the release to the owning patch.
> > +      */
> > +     if (!obj->patch->kobj_added &&
> > +         atomic_dec_and_test(&obj->patch->kobj_pending))
> > +             complete(&obj->patch->finish);
> >  }
>
> [Severity: Critical]
> Does this dereference the freed obj pointer?
>
> If obj->dynamic is true, klp_free_object_dynamic(obj) is called, which
> frees the object structure. Immediately after this intentional free, the
> newly added logic dereferences the freed pointer by evaluating
> !obj->patch->kobj_added.

makes sense.
It seems we only fix it for the !obj->dynamic case.

>
> > @@ -668,6 +677,13 @@ static void klp_kobj_release_func(struct kobject *kobj)
> >
> >       if (func->nop)
> >               klp_free_func_nop(func);
> > +     /*
> > +      * The function kobject was initialized but never added to sysfs.
> > +      * Signal the release to the owning patch.
> > +      */
> > +     if (!func->obj->patch->kobj_added &&
> > +         atomic_dec_and_test(&func->obj->patch->kobj_pending))
> > +             complete(&func->obj->patch->finish);
> >  }
>
> [Severity: Critical]
> Similarly here, does this access func after it has been freed?
>
> If func->nop is true, klp_free_func_nop(func) frees the function,
> but the new check immediately dereferences func->obj->patch->kobj_added.
>

makes sense.
We only need to fix it for the !func->nop case.

> [ ... ]
>
> > @@ -737,9 +753,29 @@ static void klp_free_objects_dynamic(struct klp_patch *patch)
> >   */
> >  static void klp_free_patch_start(struct klp_patch *patch)
> >  {
> > +     struct klp_object *obj;
> > +     struct klp_func *func;
> > +
> >       if (!list_empty(&patch->list))
> >               list_del(&patch->list);
> >
> > +     if (!patch->kobj_added) {
>
> [Severity: High]
> This is a pre-existing issue, but does this check leave child kobjects
> unprotected if partial initialization fails after the patch kobject has
> been added?
>
> If klp_init_patch() successfully adds the patch kobject, it sets
> patch->kobj_added = true. If a subsequent operation like klp_add_nops()
> or klp_init_object() fails, klp_free_patch_start() is called on the
> error path.
>
> Because patch->kobj_added is true, this check is bypassed and
> patch->kobj_pending tracking is not initialized. However, child kobjects
> that were initialized early but not yet added to sysfs do not hold a
> reference to patch->kobj. Thus, patch->kobj will be released
> independently, unblocking patch->finish, and allowing the livepatch
> module to be unloaded while un-added child kobjects may still trigger
> their delayed releases and access freed module memory.

Right.
The kobj_added flag is insufficient because it doesn't account
for partial initialization failures where the patch kobject was added
to sysfs but some child kobjects were not. Removing kobj_added and
always counting all static kobjects in kobj_pending handles both the
fully-unadded and partially-added cases uniformly.

-- 
Regards
Yafang
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.