Re: [RFC PATCH 3/4] livepatch: Add "replaceable" attribute to klp_patch

Petr Mladek
2026-04-07T15:08:06Z

On Tue 2026-04-07 17:45:31, Yafang Shao wrote:
> On Tue, Apr 7, 2026 at 11:16 AM Yafang Shao <laoar.shao@gmail.com> wrote:
> >
> > On Tue, Apr 7, 2026 at 10:54 AM Song Liu <song@kernel.org> wrote:
> > >
> > > On Mon, Apr 6, 2026 at 2:12 PM Joe Lawrence <joe.lawrence@redhat.com> wrote:
> > > [...]
> > > > > > > - The regular livepatches are cumulative, have the replace flag; and
> > > > > > > are replaceable.
> > > > > > > - The occasional "off-band" livepatches do not have the replace flag,
> > > > > > > and are not replaceable.
> > > > > > >
> > > > > > > With this setup, for systems with off-band livepatches loaded, we can
> > > > > > > still release a cumulative livepatch to replace the previous cumulative
> > > > > > > livepatch. Is this the expected use case?
> > > > > >
> > > > > > That matches our expected use case.
> > > > >
> > > > > If we really want to serve use cases like this, I think we can introduce
> > > > > some replace tag concept: Each livepatch will have a tag, u32 number.
> > > > > Newly loaded livepatch will only replace existing livepatch with the
> > > > > same tag. We can even reuse the existing "bool replace" in klp_patch,
> > > > > and make it u32: replace=0 means no replace; replace > 0 are the
> > > > > replace tag.
> > > > >
> > > > > For current users of cumulative patches, all the livepatch will have the
> > > > > same tag, say 1. For your use case, you can assign each user a
> > > > > unique tag. Then all these users can do atomic upgrades of their
> > > > > own livepatches.
> > > > >
> > > > > We may also need to check whether two livepatches of different tags
> > > > > touch the same kernel function. When that happens, the later
> > > > > livepatch should fail to load.

I still think how to make the hybrid mode more secure:

+ The isolated sets of livepatched functions look like a good rule.
+ What about isolating the shadow variables/states as well?

> > That sounds like a viable solution. I'll look into it and see how we
> > can implement it.
>
> Does the following change look good to you ?
>
> Subject: [PATCH] livepatch: Support scoped atomic replace using replace tags
>
> Extend the replace attribute from a boolean to a u32 to act as a replace
> tag. This introduces the following semantics:
>
> replace = 0: Atomic replace is disabled. However, this patch remains
> eligible to be superseded by others.
> replace > 0: Enables tagged replace (default is 1). A newly loaded
> livepatch will only replace existing patches that share the
> same tag.
>
> To maintain backward compatibility, a patch with replace == 0 does not
> trigger an outgoing atomic replace, but remains eligible to be superseded
> by any incoming patch with a valid replace tag.
>
> diff --git a/include/linux/livepatch.h b/include/linux/livepatch.h
> index ba9e3988c07c..417c67a17b99 100644
> --- a/include/linux/livepatch.h
> +++ b/include/linux/livepatch.h
> @@ -123,7 +123,11 @@ struct klp_state {
> * @mod: reference to the live patch module
> * @objs: object entries for kernel objects to be patched
> * @states: system states that can get modified
> - * @replace: replace all actively used patches
> + * @replace: replace tag:
> + * = 0: Atomic replace is disabled; however, this patch remains
> + * eligible to be superseded by others.

This is weird semantic. Which livepatch tag would be allowed to
supersede it, please?

Do we still need this category?

> + * > 0: Atomic replace is enabled. Only existing patches with a
> + * matching replace tag will be superseded.
> * @list: list node for global list of actively used patches
> * @kobj: kobject for sysfs resources
> * @obj_list: dynamic list of the object entries
> @@ -137,7 +141,7 @@ struct klp_patch {
> struct module *mod;
> struct klp_object *objs;
> struct klp_state *states;
> - bool replace;
> + unsigned int replace;

This already breaks the backward compatibility by changing the type
and semantic of this field. I would also change the name to better
match the new semantic. What about using:

* @replace_set: Livepatch using the same @replace_set will get
atomically replaced, see also conflicts[*].

unsigned int replace_set;

[*] A livepatch module, livepatching an already livepatches function,
can be loaded only when it has the same @replace_set number.

By other words, two livepatches conflict when they have a different
@replace_set number and have at least one livepatched version
in common.

>
> /* internal */
> struct list_head list;
> diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c
> index 28d15ba58a26..e4e5c03b0724 100644
> --- a/kernel/livepatch/core.c
> +++ b/kernel/livepatch/core.c
> @@ -793,6 +793,8 @@ void klp_free_replaced_patches_async(struct
> klp_patch *new_patch)
> klp_for_each_patch_safe(old_patch, tmp_patch) {
> if (old_patch == new_patch)
> return;
> + if (old_patch->replace && old_patch->replace !=
> new_patch->replace)
> + continue;
> klp_free_patch_async(old_patch);
> }
> }
> @@ -1194,6 +1196,8 @@ void klp_unpatch_replaced_patches(struct
> klp_patch *new_patch)
> klp_for_each_patch(old_patch) {
> if (old_patch == new_patch)
> return;
> + if (old_patch->replace && old_patch->replace !=
> new_patch->replace)
> + continue;
>
> old_patch->enabled = false;
> klp_unpatch_objects(old_patch);

This handles only the freeing part. More changes will be
necessary:

+ klp_is_patch_compatible() must check also conflicts
between livepatches with different @replace_set.
The conflicts might be in the lists of:

+ livepatched functions
+ state IDs (aka callbacks and shadow variables IDs)

+ klp_add_nops() must skip livepatches with another @replace_set

+ klp_unpatch_replaced_patches() should unpatch only
patches with the same @replace_set

Finally, we would need to update existing selftests
plus add new selftests.

It is possible that I have missed something.

Anyway, you should wait for more feedback before you do too much
coding, especially the selftests are not needed at RFC stage.

Best Regards,
Petr