Skip to content

Commit

Permalink
livepatch: Use the default ftrace_ops instead of REGS when ARGS is av…
Browse files Browse the repository at this point in the history
…ailable

When CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS is available, the ftrace call
will be able to set the ip of the calling function. This will improve the
performance of live kernel patching where it does not need all the regs to
be stored just to change the instruction pointer.

If all archs that support live kernel patching also support
HAVE_DYNAMIC_FTRACE_WITH_ARGS, then the architecture specific function
klp_arch_set_pc() could be made generic.

It is possible that an arch can support HAVE_DYNAMIC_FTRACE_WITH_ARGS but
not HAVE_DYNAMIC_FTRACE_WITH_REGS and then have access to live patching.

Cc: Josh Poimboeuf <jpoimboe@redhat.com>
Cc: Jiri Kosina <jikos@kernel.org>
Cc: live-patching@vger.kernel.org
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Acked-by: Miroslav Benes <mbenes@suse.cz>
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
  • Loading branch information
Steven Rostedt (VMware) committed Nov 13, 2020
1 parent 02a474c commit 2860cd8
Show file tree
Hide file tree
Showing 8 changed files with 29 additions and 9 deletions.
4 changes: 3 additions & 1 deletion arch/powerpc/include/asm/livepatch.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@
#include <linux/sched/task_stack.h>

#ifdef CONFIG_LIVEPATCH
static inline void klp_arch_set_pc(struct pt_regs *regs, unsigned long ip)
static inline void klp_arch_set_pc(struct ftrace_regs *fregs, unsigned long ip)
{
struct pt_regs *regs = ftrace_get_regs(fregs);

regs->nip = ip;
}

Expand Down
5 changes: 4 additions & 1 deletion arch/s390/include/asm/livepatch.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,13 @@
#ifndef ASM_LIVEPATCH_H
#define ASM_LIVEPATCH_H

#include <linux/ftrace.h>
#include <asm/ptrace.h>

static inline void klp_arch_set_pc(struct pt_regs *regs, unsigned long ip)
static inline void klp_arch_set_pc(struct ftrace_regs *fregs, unsigned long ip)
{
struct pt_regs *regs = ftrace_get_regs(fregs);

regs->psw.addr = ip;
}

Expand Down
3 changes: 3 additions & 0 deletions arch/x86/include/asm/ftrace.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ arch_ftrace_get_regs(struct ftrace_regs *fregs)
return NULL;
return &fregs->regs;
}

#define ftrace_instruction_pointer_set(fregs, _ip) \
do { (fregs)->regs.ip = (_ip); } while (0)
#endif

#ifdef CONFIG_DYNAMIC_FTRACE
Expand Down
4 changes: 2 additions & 2 deletions arch/x86/include/asm/livepatch.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@
#include <asm/setup.h>
#include <linux/ftrace.h>

static inline void klp_arch_set_pc(struct pt_regs *regs, unsigned long ip)
static inline void klp_arch_set_pc(struct ftrace_regs *fregs, unsigned long ip)
{
regs->ip = ip;
ftrace_instruction_pointer_set(fregs, ip);
}

#endif /* _ASM_X86_LIVEPATCH_H */
4 changes: 4 additions & 0 deletions arch/x86/kernel/ftrace_64.S
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,10 @@ SYM_INNER_LABEL(ftrace_caller_op_ptr, SYM_L_GLOBAL)
SYM_INNER_LABEL(ftrace_call, SYM_L_GLOBAL)
call ftrace_stub

/* Handlers can change the RIP */
movq RIP(%rsp), %rax
movq %rax, MCOUNT_REG_SIZE(%rsp)

restore_mcount_regs

/*
Expand Down
7 changes: 7 additions & 0 deletions include/linux/ftrace.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,13 @@ struct ftrace_regs {
};
#define arch_ftrace_get_regs(fregs) (&(fregs)->regs)

/*
* ftrace_instruction_pointer_set() is to be defined by the architecture
* if to allow setting of the instruction pointer from the ftrace_regs
* when HAVE_DYNAMIC_FTRACE_WITH_ARGS is set and it supports
* live kernel patching.
*/
#define ftrace_instruction_pointer_set(fregs, ip) do { } while (0)
#endif /* CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS */

static __always_inline struct pt_regs *ftrace_get_regs(struct ftrace_regs *fregs)
Expand Down
2 changes: 1 addition & 1 deletion kernel/livepatch/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ config HAVE_LIVEPATCH

config LIVEPATCH
bool "Kernel Live Patching"
depends on DYNAMIC_FTRACE_WITH_REGS
depends on DYNAMIC_FTRACE_WITH_REGS || DYNAMIC_FTRACE_WITH_ARGS
depends on MODULES
depends on SYSFS
depends on KALLSYMS_ALL
Expand Down
9 changes: 5 additions & 4 deletions kernel/livepatch/patch.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ static void notrace klp_ftrace_handler(unsigned long ip,
struct ftrace_ops *fops,
struct ftrace_regs *fregs)
{
struct pt_regs *regs = ftrace_get_regs(fregs);
struct klp_ops *ops;
struct klp_func *func;
int patch_state;
Expand Down Expand Up @@ -118,7 +117,7 @@ static void notrace klp_ftrace_handler(unsigned long ip,
if (func->nop)
goto unlock;

klp_arch_set_pc(regs, (unsigned long)func->new_func);
klp_arch_set_pc(fregs, (unsigned long)func->new_func);

unlock:
preempt_enable_notrace();
Expand Down Expand Up @@ -200,8 +199,10 @@ static int klp_patch_func(struct klp_func *func)
return -ENOMEM;

ops->fops.func = klp_ftrace_handler;
ops->fops.flags = FTRACE_OPS_FL_SAVE_REGS |
FTRACE_OPS_FL_DYNAMIC |
ops->fops.flags = FTRACE_OPS_FL_DYNAMIC |
#ifndef CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS
FTRACE_OPS_FL_SAVE_REGS |
#endif
FTRACE_OPS_FL_IPMODIFY |
FTRACE_OPS_FL_PERMANENT;

Expand Down

0 comments on commit 2860cd8

Please sign in to comment.