diff mbox series

[2/3] livepatch: Allow user to specify functions to search for on a stack

Message ID 20211119090327.12811-3-mbenes@suse.cz (mailing list archive)
State New
Headers show
Series livepatch: Allow user to specify functions to search for on a stack | expand

Commit Message

Miroslav Benes Nov. 19, 2021, 9:03 a.m. UTC
livepatch's consistency model requires that no live patched function
must be found on any task's stack during a transition process after a
live patch is applied. It is achieved by walking through stacks of all
blocked tasks.

The user might also want to define more functions to search for without
them being patched at all. It may either help with preparing a live
patch, which would otherwise require additional touches to achieve the
consistency, or it can be used to overcome deficiencies the stack
checking inherently has. For example, GCC may optimize a function so
that a part of it is moved to a different section and the function would
jump to it. This child function would not be found on a stack in this
case, but it may be important to search for it so that, again, the
consistency is achieved.

Allow the user to specify such functions on klp_object level.

Signed-off-by: Miroslav Benes <mbenes@suse.cz>
---
 include/linux/livepatch.h     | 11 +++++++++++
 kernel/livepatch/core.c       | 16 ++++++++++++++++
 kernel/livepatch/transition.c | 21 ++++++++++++++++-----
 3 files changed, 43 insertions(+), 5 deletions(-)

Comments

Peter Zijlstra Nov. 19, 2021, 10:17 a.m. UTC | #1
On Fri, Nov 19, 2021 at 10:03:26AM +0100, Miroslav Benes wrote:
> livepatch's consistency model requires that no live patched function
> must be found on any task's stack during a transition process after a
> live patch is applied. It is achieved by walking through stacks of all
> blocked tasks.
> 
> The user might also want to define more functions to search for without
> them being patched at all. It may either help with preparing a live
> patch, which would otherwise require additional touches to achieve the
> consistency, or it can be used to overcome deficiencies the stack
> checking inherently has. For example, GCC may optimize a function so
> that a part of it is moved to a different section and the function would
> jump to it. This child function would not be found on a stack in this
> case, but it may be important to search for it so that, again, the
> consistency is achieved.
> 
> Allow the user to specify such functions on klp_object level.

Ok, so this relies on the patch generator to DTRT but then it should
work.

Thanks!
Josh Poimboeuf Nov. 19, 2021, 6:20 p.m. UTC | #2
Thanks for doing this!  And at peterz-esque speed no less :-)

On Fri, Nov 19, 2021 at 10:03:26AM +0100, Miroslav Benes wrote:
> livepatch's consistency model requires that no live patched function
> must be found on any task's stack during a transition process after a
> live patch is applied. It is achieved by walking through stacks of all
> blocked tasks.
> 
> The user might also want to define more functions to search for without
> them being patched at all. It may either help with preparing a live
> patch, which would otherwise require additional touches to achieve the
> consistency

Do we have any examples of this situation we can add to the commit log?

> or it can be used to overcome deficiencies the stack
> checking inherently has. For example, GCC may optimize a function so
> that a part of it is moved to a different section and the function would
> jump to it. This child function would not be found on a stack in this
> case, but it may be important to search for it so that, again, the
> consistency is achieved.
> 
> Allow the user to specify such functions on klp_object level.
> 
> Signed-off-by: Miroslav Benes <mbenes@suse.cz>
> ---
>  include/linux/livepatch.h     | 11 +++++++++++
>  kernel/livepatch/core.c       | 16 ++++++++++++++++
>  kernel/livepatch/transition.c | 21 ++++++++++++++++-----
>  3 files changed, 43 insertions(+), 5 deletions(-)
> 
> diff --git a/include/linux/livepatch.h b/include/linux/livepatch.h
> index 2614247a9781..89df578af8c3 100644
> --- a/include/linux/livepatch.h
> +++ b/include/linux/livepatch.h
> @@ -106,9 +106,11 @@ struct klp_callbacks {
>   * struct klp_object - kernel object structure for live patching
>   * @name:	module name (or NULL for vmlinux)
>   * @funcs:	function entries for functions to be patched in the object
> + * @funcs_stack:	function entries for functions to be stack checked

So there are two arrays/lists of 'klp_func', and two implied meanings of
what a 'klp_func' is and how it's initialized.

Might it be simpler and more explicit to just add a new external field
to 'klp_func' and continue to have a single 'funcs' array?  Similar to
what we already do with the special-casing of 'nop', except it would be
an external field, e.g. 'no_patch' or 'stack_only'.

Then instead of all the extra klp_for_each_func_stack_static()
incantations, and the special cases in higher-level callers like
klp_init_object() and klp_init_patch_early(), the lower-level functions
like klp_init_func() and klp_init_func_early() can check the field to
determine which initializations need to be made.  Which is kind of nice
IMO as it pushes that detail down more where it belongs.  And makes the
different types of 'klp_func' more explicit.
Miroslav Benes Nov. 22, 2021, 7:57 a.m. UTC | #3
On Fri, 19 Nov 2021, Josh Poimboeuf wrote:

> Thanks for doing this!  And at peterz-esque speed no less :-)
> 
> On Fri, Nov 19, 2021 at 10:03:26AM +0100, Miroslav Benes wrote:
> > livepatch's consistency model requires that no live patched function
> > must be found on any task's stack during a transition process after a
> > live patch is applied. It is achieved by walking through stacks of all
> > blocked tasks.
> > 
> > The user might also want to define more functions to search for without
> > them being patched at all. It may either help with preparing a live
> > patch, which would otherwise require additional touches to achieve the
> > consistency
> 
> Do we have any examples of this situation we can add to the commit log?

I do not have anything at hand. Joe, do you remember the case you 
mentioned previously about adding a nop to a function?
 
> > or it can be used to overcome deficiencies the stack
> > checking inherently has. For example, GCC may optimize a function so
> > that a part of it is moved to a different section and the function would
> > jump to it. This child function would not be found on a stack in this
> > case, but it may be important to search for it so that, again, the
> > consistency is achieved.
> > 
> > Allow the user to specify such functions on klp_object level.
> > 
> > Signed-off-by: Miroslav Benes <mbenes@suse.cz>
> > ---
> >  include/linux/livepatch.h     | 11 +++++++++++
> >  kernel/livepatch/core.c       | 16 ++++++++++++++++
> >  kernel/livepatch/transition.c | 21 ++++++++++++++++-----
> >  3 files changed, 43 insertions(+), 5 deletions(-)
> > 
> > diff --git a/include/linux/livepatch.h b/include/linux/livepatch.h
> > index 2614247a9781..89df578af8c3 100644
> > --- a/include/linux/livepatch.h
> > +++ b/include/linux/livepatch.h
> > @@ -106,9 +106,11 @@ struct klp_callbacks {
> >   * struct klp_object - kernel object structure for live patching
> >   * @name:	module name (or NULL for vmlinux)
> >   * @funcs:	function entries for functions to be patched in the object
> > + * @funcs_stack:	function entries for functions to be stack checked
> 
> So there are two arrays/lists of 'klp_func', and two implied meanings of
> what a 'klp_func' is and how it's initialized.
> 
> Might it be simpler and more explicit to just add a new external field
> to 'klp_func' and continue to have a single 'funcs' array?  Similar to
> what we already do with the special-casing of 'nop', except it would be
> an external field, e.g. 'no_patch' or 'stack_only'.
> 
> Then instead of all the extra klp_for_each_func_stack_static()
> incantations, and the special cases in higher-level callers like
> klp_init_object() and klp_init_patch_early(), the lower-level functions
> like klp_init_func() and klp_init_func_early() can check the field to
> determine which initializations need to be made.  Which is kind of nice
> IMO as it pushes that detail down more where it belongs.  And makes the
> different types of 'klp_func' more explicit.

I thought about doing this for a moment but then I was worried there would 
be many places which would require special-casing, so I tried to keep it 
separate. But yes, it would be cleaner, so definitely worth trying for v2.

Thanks

Miroslav
Joe Lawrence Nov. 22, 2021, 3:53 p.m. UTC | #4
On 11/22/21 2:57 AM, Miroslav Benes wrote:
> On Fri, 19 Nov 2021, Josh Poimboeuf wrote:
> 
>> Thanks for doing this!  And at peterz-esque speed no less :-)
>>
>> On Fri, Nov 19, 2021 at 10:03:26AM +0100, Miroslav Benes wrote:
>>> livepatch's consistency model requires that no live patched function
>>> must be found on any task's stack during a transition process after a
>>> live patch is applied. It is achieved by walking through stacks of all
>>> blocked tasks.
>>>
>>> The user might also want to define more functions to search for without
>>> them being patched at all. It may either help with preparing a live
>>> patch, which would otherwise require additional touches to achieve the
>>> consistency
>>
>> Do we have any examples of this situation we can add to the commit log?
> 
> I do not have anything at hand. Joe, do you remember the case you 
> mentioned previously about adding a nop to a function?
>  

I went looking in my inbox to see... Unfortunately the closest thing I
found was a kpatchset in which we added nops to coax kpatch-build into
reverting previous patch version changes.  Not applicable here, but I
was certain we entertained the same idea to increase the task stack
check for some other problem.

Maybe adding a hypothetical scenario to the commit log would suffice?

>>> or it can be used to overcome deficiencies the stack
>>> checking inherently has. For example, GCC may optimize a function so
>>> that a part of it is moved to a different section and the function would
>>> jump to it. This child function would not be found on a stack in this
>>> case, but it may be important to search for it so that, again, the
>>> consistency is achieved.
>>>
>>> Allow the user to specify such functions on klp_object level.
>>>
>>> Signed-off-by: Miroslav Benes <mbenes@suse.cz>
>>> ---
>>>  include/linux/livepatch.h     | 11 +++++++++++
>>>  kernel/livepatch/core.c       | 16 ++++++++++++++++
>>>  kernel/livepatch/transition.c | 21 ++++++++++++++++-----
>>>  3 files changed, 43 insertions(+), 5 deletions(-)
>>>
>>> diff --git a/include/linux/livepatch.h b/include/linux/livepatch.h
>>> index 2614247a9781..89df578af8c3 100644
>>> --- a/include/linux/livepatch.h
>>> +++ b/include/linux/livepatch.h
>>> @@ -106,9 +106,11 @@ struct klp_callbacks {
>>>   * struct klp_object - kernel object structure for live patching
>>>   * @name:	module name (or NULL for vmlinux)
>>>   * @funcs:	function entries for functions to be patched in the object
>>> + * @funcs_stack:	function entries for functions to be stack checked
>>
>> So there are two arrays/lists of 'klp_func', and two implied meanings of
>> what a 'klp_func' is and how it's initialized.
>>
>> Might it be simpler and more explicit to just add a new external field
>> to 'klp_func' and continue to have a single 'funcs' array?  Similar to
>> what we already do with the special-casing of 'nop', except it would be
>> an external field, e.g. 'no_patch' or 'stack_only'.
>>
>> Then instead of all the extra klp_for_each_func_stack_static()
>> incantations, and the special cases in higher-level callers like
>> klp_init_object() and klp_init_patch_early(), the lower-level functions
>> like klp_init_func() and klp_init_func_early() can check the field to
>> determine which initializations need to be made.  Which is kind of nice
>> IMO as it pushes that detail down more where it belongs.  And makes the
>> different types of 'klp_func' more explicit.
> 
> I thought about doing this for a moment but then I was worried there would 
> be many places which would require special-casing, so I tried to keep it 
> separate. But yes, it would be cleaner, so definitely worth trying for v2.
> 

I'll add that the first thing that came to mind when you raised this
feature idea in the other thread was to support existing klp_funcs array
with NULL new_func's.  I didn't go look to see how invasive it would be,
but it will be interesting to see if a single list approach turns out
any simpler for v2.
Petr Mladek Nov. 25, 2021, 10:07 a.m. UTC | #5
On Mon 2021-11-22 10:53:21, Joe Lawrence wrote:
> On 11/22/21 2:57 AM, Miroslav Benes wrote:
> > On Fri, 19 Nov 2021, Josh Poimboeuf wrote:
> > 
> >> Thanks for doing this!  And at peterz-esque speed no less :-)
> >>
> >> On Fri, Nov 19, 2021 at 10:03:26AM +0100, Miroslav Benes wrote:
> >>> livepatch's consistency model requires that no live patched function
> >>> must be found on any task's stack during a transition process after a
> >>> live patch is applied. It is achieved by walking through stacks of all
> >>> blocked tasks.
> >>>
> >>> The user might also want to define more functions to search for without
> >>> them being patched at all. It may either help with preparing a live
> >>> patch, which would otherwise require additional touches to achieve the
> >>> consistency
> >>
> >> Do we have any examples of this situation we can add to the commit log?
> > 
> > I do not have anything at hand. Joe, do you remember the case you 
> > mentioned previously about adding a nop to a function?
> 
> Maybe adding a hypothetical scenario to the commit log would suffice?

I wonder if we could describe a scenario based on the thread about
.cold code variants, see
https://lore.kernel.org/all/20211112015003.pefl656m3zmir6ov@treble/

This feature would allow to safely livepatch already released
kernels where the unwinder is not able to reliably detect
a newly discovered problems.

> >>> or it can be used to overcome deficiencies the stack
> >>> checking inherently has. For example, GCC may optimize a function so
> >>> that a part of it is moved to a different section and the function would
> >>> jump to it. This child function would not be found on a stack in this
> >>> case, but it may be important to search for it so that, again, the
> >>> consistency is achieved.
> >>>
> >>> Allow the user to specify such functions on klp_object level.
> >>>
> >>> Signed-off-by: Miroslav Benes <mbenes@suse.cz>
> >>> ---
> >>>  include/linux/livepatch.h     | 11 +++++++++++
> >>>  kernel/livepatch/core.c       | 16 ++++++++++++++++
> >>>  kernel/livepatch/transition.c | 21 ++++++++++++++++-----
> >>>  3 files changed, 43 insertions(+), 5 deletions(-)
> >>>
> >>> diff --git a/include/linux/livepatch.h b/include/linux/livepatch.h
> >>> index 2614247a9781..89df578af8c3 100644
> >>> --- a/include/linux/livepatch.h
> >>> +++ b/include/linux/livepatch.h
> >>> @@ -106,9 +106,11 @@ struct klp_callbacks {
> >>>   * struct klp_object - kernel object structure for live patching
> >>>   * @name:	module name (or NULL for vmlinux)
> >>>   * @funcs:	function entries for functions to be patched in the object
> >>> + * @funcs_stack:	function entries for functions to be stack checked
> >>
> >> So there are two arrays/lists of 'klp_func', and two implied meanings of
> >> what a 'klp_func' is and how it's initialized.
> >>
> >> Might it be simpler and more explicit to just add a new external field
> >> to 'klp_func' and continue to have a single 'funcs' array?  Similar to
> >> what we already do with the special-casing of 'nop', except it would be
> >> an external field, e.g. 'no_patch' or 'stack_only'.
> 
> I'll add that the first thing that came to mind when you raised this
> feature idea in the other thread was to support existing klp_funcs array
> with NULL new_func's.

Please, solve this with the extra flag, e.g. .stack_only, as
already suggested. It will help to distinguish mistakes and
intentions. Also it will allow to find these symbols by grep.

> I didn't go look to see how invasive it would be,
> but it will be interesting to see if a single list approach turns out
> any simpler for v2.

I am not sure either. But I expect that it will be easier than
the extra array.

Best Regards,
Petr
Miroslav Benes Nov. 25, 2021, 10:20 a.m. UTC | #6
On Thu, 25 Nov 2021, Petr Mladek wrote:

> On Mon 2021-11-22 10:53:21, Joe Lawrence wrote:
> > On 11/22/21 2:57 AM, Miroslav Benes wrote:
> > > On Fri, 19 Nov 2021, Josh Poimboeuf wrote:
> > > 
> > >> Thanks for doing this!  And at peterz-esque speed no less :-)
> > >>
> > >> On Fri, Nov 19, 2021 at 10:03:26AM +0100, Miroslav Benes wrote:
> > >>> livepatch's consistency model requires that no live patched function
> > >>> must be found on any task's stack during a transition process after a
> > >>> live patch is applied. It is achieved by walking through stacks of all
> > >>> blocked tasks.
> > >>>
> > >>> The user might also want to define more functions to search for without
> > >>> them being patched at all. It may either help with preparing a live
> > >>> patch, which would otherwise require additional touches to achieve the
> > >>> consistency
> > >>
> > >> Do we have any examples of this situation we can add to the commit log?
> > > 
> > > I do not have anything at hand. Joe, do you remember the case you 
> > > mentioned previously about adding a nop to a function?
> > 
> > Maybe adding a hypothetical scenario to the commit log would suffice?
> 
> I wonder if we could describe a scenario based on the thread about
> .cold code variants, see
> https://lore.kernel.org/all/20211112015003.pefl656m3zmir6ov@treble/
> 
> This feature would allow to safely livepatch already released
> kernels where the unwinder is not able to reliably detect
> a newly discovered problems.

and is described (well, without actually using .cold suffix) a few lines 
below. I'll try to improve the changelog.
 
> > >>> or it can be used to overcome deficiencies the stack
> > >>> checking inherently has. For example, GCC may optimize a function so
> > >>> that a part of it is moved to a different section and the function would
> > >>> jump to it. This child function would not be found on a stack in this
> > >>> case, but it may be important to search for it so that, again, the
> > >>> consistency is achieved.
> > >>>
> > >>> Allow the user to specify such functions on klp_object level.
> > >>>
> > >>> Signed-off-by: Miroslav Benes <mbenes@suse.cz>
> > >>> ---
> > >>>  include/linux/livepatch.h     | 11 +++++++++++
> > >>>  kernel/livepatch/core.c       | 16 ++++++++++++++++
> > >>>  kernel/livepatch/transition.c | 21 ++++++++++++++++-----
> > >>>  3 files changed, 43 insertions(+), 5 deletions(-)
> > >>>
> > >>> diff --git a/include/linux/livepatch.h b/include/linux/livepatch.h
> > >>> index 2614247a9781..89df578af8c3 100644
> > >>> --- a/include/linux/livepatch.h
> > >>> +++ b/include/linux/livepatch.h
> > >>> @@ -106,9 +106,11 @@ struct klp_callbacks {
> > >>>   * struct klp_object - kernel object structure for live patching
> > >>>   * @name:	module name (or NULL for vmlinux)
> > >>>   * @funcs:	function entries for functions to be patched in the object
> > >>> + * @funcs_stack:	function entries for functions to be stack checked
> > >>
> > >> So there are two arrays/lists of 'klp_func', and two implied meanings of
> > >> what a 'klp_func' is and how it's initialized.
> > >>
> > >> Might it be simpler and more explicit to just add a new external field
> > >> to 'klp_func' and continue to have a single 'funcs' array?  Similar to
> > >> what we already do with the special-casing of 'nop', except it would be
> > >> an external field, e.g. 'no_patch' or 'stack_only'.
> > 
> > I'll add that the first thing that came to mind when you raised this
> > feature idea in the other thread was to support existing klp_funcs array
> > with NULL new_func's.
> 
> Please, solve this with the extra flag, e.g. .stack_only, as
> already suggested. It will help to distinguish mistakes and
> intentions. Also it will allow to find these symbols by grep.

Indeed, that is what I did for v2. I used new_func being NULL fact even in 
v1, but I do not like it much. Extra flag is definitely more robust.
 
> > I didn't go look to see how invasive it would be,
> > but it will be interesting to see if a single list approach turns out
> > any simpler for v2.
> 
> I am not sure either. But I expect that it will be easier than
> the extra array.

So, extra flag and one array makes certain things (initialization) 
definitely easier. On the other hand, there are suddenly more problems to 
think about (and I haven't solved them yet):

  - I need to make it work with nops functions. Especially if we allow a 
    scenario where there could be klp_object instance with just stack_only 
    functions. Would that be useful? For example, to patch something in a 
    module and add a stack_only for a function in vmlinux.
 
    If yes, then the interaction with nops is not completely 
    straightforward and also some parts of the code would have to be 
    changed (for example how obj->patched flag is handled).

  - klp_func instances are directly mirrored in sysfs. Do we want to keep 
    stack_only functions there too? If not, it makes the whole thing 
    slighly more difficult given how we manage kobjects.

Nothing really difficult to implement if we come up with answers.

Miroslav
Petr Mladek Dec. 3, 2021, 4:01 p.m. UTC | #7
On Thu 2021-11-25 11:20:04, Miroslav Benes wrote:
> On Thu, 25 Nov 2021, Petr Mladek wrote:
> > On Mon 2021-11-22 10:53:21, Joe Lawrence wrote:
> > > On 11/22/21 2:57 AM, Miroslav Benes wrote:
> > > > On Fri, 19 Nov 2021, Josh Poimboeuf wrote:
> > > >>> diff --git a/include/linux/livepatch.h b/include/linux/livepatch.h
> > > >>> index 2614247a9781..89df578af8c3 100644
> > > >>> --- a/include/linux/livepatch.h
> > > >>> +++ b/include/linux/livepatch.h
> > > >>> @@ -106,9 +106,11 @@ struct klp_callbacks {
> > > >>>   * struct klp_object - kernel object structure for live patching
> > > >>>   * @name:	module name (or NULL for vmlinux)
> > > >>>   * @funcs:	function entries for functions to be patched in the object
> > > >>> + * @funcs_stack:	function entries for functions to be stack checked
> > > >>
> > > >> So there are two arrays/lists of 'klp_func', and two implied meanings of
> > > >> what a 'klp_func' is and how it's initialized.
> > > >>
> > > >> Might it be simpler and more explicit to just add a new external field
> > > >> to 'klp_func' and continue to have a single 'funcs' array?  Similar to
> > > >> what we already do with the special-casing of 'nop', except it would be
> > > >> an external field, e.g. 'no_patch' or 'stack_only'.
> > > 
> > > I'll add that the first thing that came to mind when you raised this
> > > feature idea in the other thread was to support existing klp_funcs array
> > > with NULL new_func's.
> > 
> > Please, solve this with the extra flag, e.g. .stack_only, as
> > already suggested. It will help to distinguish mistakes and
> > intentions. Also it will allow to find these symbols by grep.
> 
> Indeed, that is what I did for v2. I used new_func being NULL fact even in 
> v1, but I do not like it much. Extra flag is definitely more robust.
>  
> > > I didn't go look to see how invasive it would be,
> > > but it will be interesting to see if a single list approach turns out
> > > any simpler for v2.
> > 
> > I am not sure either. But I expect that it will be easier than
> > the extra array.
> 
> So, extra flag and one array makes certain things (initialization) 
> definitely easier. On the other hand, there are suddenly more problems to 
> think about (and I haven't solved them yet):
> 
>   - I need to make it work with nops functions. Especially if we allow a 
>     scenario where there could be klp_object instance with just stack_only 
>     functions. Would that be useful? For example, to patch something in a 
>     module and add a stack_only for a function in vmlinux.

My naive expectation is that the struct klp_func with @stack_only
flag might be handled the same way on most locations, except when:

   + func->new_func is handled
   + ftrace handler is added/removed

Something like:

--- a/kernel/livepatch/core.c
+++ b/kernel/livepatch/core.c
@@ -724,7 +724,7 @@ static int klp_init_func(struct klp_object *obj, struct klp_func *func)
 	 * NOPs get the address later. The patched module must be loaded,
 	 * see klp_init_object_loaded().
 	 */
-	if (!func->new_func && !func->nop)
+	if (!func->new_func && !func->nop && !func->stack_only)
 		return -EINVAL;
 
 	if (strlen(func->old_name) >= KSYM_NAME_LEN)
@@ -801,6 +801,9 @@ static int klp_init_object_loaded(struct klp_patch *patch,
 			return -ENOENT;
 		}
 
+		if (func->stack_only)
+			continue;
+
 		if (func->nop)
 			func->new_func = func->old_func;
 
--- a/kernel/livepatch/patch.c
+++ b/kernel/livepatch/patch.c
@@ -247,6 +247,9 @@ static void __klp_unpatch_object(struct klp_object *obj, bool nops_only)
 	struct klp_func *func;
 
 	klp_for_each_func(obj, func) {
+		if (func->stack_only)
+			continue;
+
 		if (nops_only && !func->nop)
 			continue;
 
@@ -273,6 +276,8 @@ int klp_patch_object(struct klp_object *obj)
 		return -EINVAL;
 
 	klp_for_each_func(obj, func) {
+		if (func->stack_only)
+			continue;
 		ret = klp_patch_func(func);
 		if (ret) {
 			klp_unpatch_object(obj);


>     If yes, then the interaction with nops is not completely 
>     straightforward and also some parts of the code would have to be 
>     changed (for example how obj->patched flag is handled).

I would keep func->patched disabled for @stack_only entries.

But they should not affect obj->patched state. I mean that
obj->patched should be set when the patch is enabled even when
there are only "stack_only" funcs.

It might look a bit unclear. A solution might be to rename the flags:

   + func->patched    ->   func->active   (and set even for @stack_only)
   + obj->patched     ->   obj->active    (same as func)

But I am not sure if it is worth it.


>   - klp_func instances are directly mirrored in sysfs. Do we want to keep 
>     stack_only functions there too? If not, it makes the whole thing 
>     slighly more difficult given how we manage kobjects.

I would keep them in sysfs. It will be easier and it does not harm.

> Nothing really difficult to implement if we come up with answers.

I am sorry for not answering this earlier. I have missed the questions :-(

Best Regards,
Petr
diff mbox series

Patch

diff --git a/include/linux/livepatch.h b/include/linux/livepatch.h
index 2614247a9781..89df578af8c3 100644
--- a/include/linux/livepatch.h
+++ b/include/linux/livepatch.h
@@ -106,9 +106,11 @@  struct klp_callbacks {
  * struct klp_object - kernel object structure for live patching
  * @name:	module name (or NULL for vmlinux)
  * @funcs:	function entries for functions to be patched in the object
+ * @funcs_stack:	function entries for functions to be stack checked
  * @callbacks:	functions to be executed pre/post (un)patching
  * @kobj:	kobject for sysfs resources
  * @func_list:	dynamic list of the function entries
+ * @func_stack_list:	dynamic list of the function entries for stack checking
  * @node:	list node for klp_patch obj_list
  * @mod:	kernel module associated with the patched object
  *		(NULL for vmlinux)
@@ -119,11 +121,13 @@  struct klp_object {
 	/* external */
 	const char *name;
 	struct klp_func *funcs;
+	struct klp_func *funcs_stack;
 	struct klp_callbacks callbacks;
 
 	/* internal */
 	struct kobject kobj;
 	struct list_head func_list;
+	struct list_head func_stack_list;
 	struct list_head node;
 	struct module *mod;
 	bool dynamic;
@@ -187,12 +191,19 @@  struct klp_patch {
 	     func->old_name || func->new_func || func->old_sympos; \
 	     func++)
 
+#define klp_for_each_func_stack_static(obj, func) \
+	for (func = obj->funcs_stack; \
+	     func && (func->old_name || func->old_sympos); func++)
+
 #define klp_for_each_func_safe(obj, func, tmp_func)			\
 	list_for_each_entry_safe(func, tmp_func, &obj->func_list, node)
 
 #define klp_for_each_func(obj, func)	\
 	list_for_each_entry(func, &obj->func_list, node)
 
+#define klp_for_each_func_stack(obj, func)	\
+	list_for_each_entry(func, &obj->func_stack_list, node)
+
 int klp_enable_patch(struct klp_patch *);
 
 /* Called from the module loader during module coming/going states */
diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c
index 3d8e3caf9f92..86fc73a06844 100644
--- a/kernel/livepatch/core.c
+++ b/kernel/livepatch/core.c
@@ -825,6 +825,12 @@  static int klp_init_object_loaded(struct klp_patch *patch,
 		}
 	}
 
+	klp_for_each_func_stack(obj, func) {
+		ret = klp_init_old_func(obj, func);
+		if (ret)
+			return ret;
+	}
+
 	return 0;
 }
 
@@ -853,6 +859,11 @@  static int klp_init_object(struct klp_patch *patch, struct klp_object *obj)
 			return ret;
 	}
 
+	klp_for_each_func_stack(obj, func) {
+		if (strlen(func->old_name) >= KSYM_NAME_LEN)
+			return -EINVAL;
+	}
+
 	if (klp_is_object_loaded(obj))
 		ret = klp_init_object_loaded(patch, obj);
 
@@ -870,6 +881,7 @@  static void klp_init_object_early(struct klp_patch *patch,
 				  struct klp_object *obj)
 {
 	INIT_LIST_HEAD(&obj->func_list);
+	INIT_LIST_HEAD(&obj->func_stack_list);
 	kobject_init(&obj->kobj, &klp_ktype_object);
 	list_add_tail(&obj->node, &patch->obj_list);
 }
@@ -899,6 +911,10 @@  static int klp_init_patch_early(struct klp_patch *patch)
 		klp_for_each_func_static(obj, func) {
 			klp_init_func_early(obj, func);
 		}
+
+		klp_for_each_func_stack_static(obj, func) {
+			list_add_tail(&func->node, &obj->func_stack_list);
+		}
 	}
 
 	if (!try_module_get(patch->mod))
diff --git a/kernel/livepatch/transition.c b/kernel/livepatch/transition.c
index 5683ac0d2566..be7afc5dc275 100644
--- a/kernel/livepatch/transition.c
+++ b/kernel/livepatch/transition.c
@@ -200,7 +200,10 @@  static int klp_check_stack_func(struct klp_func *func, unsigned long *entries,
 	for (i = 0; i < nr_entries; i++) {
 		address = entries[i];
 
-		if (klp_target_state == KLP_UNPATCHED) {
+		if (!func->new_func) {
+			func_addr = (unsigned long)func->old_func;
+			func_size = func->old_size;
+		} else if (klp_target_state == KLP_UNPATCHED) {
 			 /*
 			  * Check for the to-be-unpatched function
 			  * (the func itself).
@@ -256,14 +259,22 @@  static int klp_check_stack(struct task_struct *task, const char **oldname)
 			continue;
 		klp_for_each_func(obj, func) {
 			ret = klp_check_stack_func(func, entries, nr_entries);
-			if (ret) {
-				*oldname = func->old_name;
-				return -EADDRINUSE;
-			}
+			if (ret)
+				goto err;
+		}
+
+		klp_for_each_func_stack(obj, func) {
+			ret = klp_check_stack_func(func, entries, nr_entries);
+			if (ret)
+				goto err;
 		}
 	}
 
 	return 0;
+
+err:
+	*oldname = func->old_name;
+	return -EADDRINUSE;
 }
 
 static int klp_check_and_switch_task(struct task_struct *task, void *arg)