diff options
author | Jiri Slaby <jslaby@suse.cz> | 2015-02-26 14:19:38 +0100 |
---|---|---|
committer | Jiri Slaby <jslaby@suse.cz> | 2016-01-13 10:08:58 +0100 |
commit | 750be994eb7c8e376fe599f0527716d69b6c62e4 (patch) | |
tree | dbf9f4c5ff609fc539216ecebdb9d25fbbe4dd3a | |
parent | 4b51e489ce1124e61f3db5e8e019923f856411e1 (diff) | |
download | kgraft-750be994eb7c8e376fe599f0527716d69b6c62e4.tar.gz |
livepatch: add kgr infrastructure
This means:
* add a per-thread flag to indicate whether a task is in the old or in
the new universe,
* reset it in _slow_ paths of syscall's entry/exit,
* add helpers around the flag to sched.h,
* export the status in /proc/<pid>/kgr_in_progress,
This was cherry-picked from the kGraft implementation and will serve
as a base for kGraft-like patching in Live Patching.
Miroslav helped to clean the assembly up and move to C.
Signed-off-by: Jiri Slaby <jslaby@suse.cz>
Cc: Miroslav Benes <mbenes@suse.cz>
Cc: Martin Schwidefsky <schwidefsky@de.ibm.com>
Cc: Heiko Carstens <heiko.carstens@de.ibm.com>
Cc: linux-s390@vger.kernel.org
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: x86@kernel.org
-rw-r--r-- | arch/s390/include/asm/thread_info.h | 2 | ||||
-rw-r--r-- | arch/s390/kernel/Makefile | 1 | ||||
-rw-r--r-- | arch/s390/kernel/entry.S | 15 | ||||
-rw-r--r-- | arch/s390/kernel/livepatch.c | 14 | ||||
-rw-r--r-- | arch/x86/entry/common.c | 15 | ||||
-rw-r--r-- | arch/x86/include/asm/thread_info.h | 6 | ||||
-rw-r--r-- | fs/proc/base.c | 14 | ||||
-rw-r--r-- | include/linux/sched.h | 23 |
8 files changed, 86 insertions, 4 deletions
diff --git a/arch/s390/include/asm/thread_info.h b/arch/s390/include/asm/thread_info.h index 1f28ee49be35ca..811f2e23dea92d 100644 --- a/arch/s390/include/asm/thread_info.h +++ b/arch/s390/include/asm/thread_info.h @@ -75,6 +75,7 @@ void arch_release_task_struct(struct task_struct *tsk); #define TIF_SIGPENDING 1 /* signal pending */ #define TIF_NEED_RESCHED 2 /* rescheduling necessary */ #define TIF_UPROBE 3 /* breakpointed or single-stepping */ +#define TIF_KGR_IN_PROGRESS 4 /* This task has not finished patching */ #define TIF_31BIT 16 /* 32bit process */ #define TIF_MEMDIE 17 /* is terminating due to OOM killer */ @@ -93,6 +94,7 @@ void arch_release_task_struct(struct task_struct *tsk); #define _TIF_SIGPENDING _BITUL(TIF_SIGPENDING) #define _TIF_NEED_RESCHED _BITUL(TIF_NEED_RESCHED) #define _TIF_UPROBE _BITUL(TIF_UPROBE) +#define _TIF_KGR_IN_PROGRESS _BITUL(TIF_KGR_IN_PROGRESS) #define _TIF_31BIT _BITUL(TIF_31BIT) #define _TIF_SINGLE_STEP _BITUL(TIF_SINGLE_STEP) diff --git a/arch/s390/kernel/Makefile b/arch/s390/kernel/Makefile index dc167a23b92055..c93adf5f204fe2 100644 --- a/arch/s390/kernel/Makefile +++ b/arch/s390/kernel/Makefile @@ -56,6 +56,7 @@ obj-$(CONFIG_AUDIT) += audit.o compat-obj-$(CONFIG_AUDIT) += compat_audit.o obj-$(CONFIG_COMPAT) += compat_linux.o compat_signal.o obj-$(CONFIG_COMPAT) += compat_wrapper.o $(compat-obj-y) +obj-$(CONFIG_LIVEPATCH) += livepatch.o obj-$(CONFIG_STACKTRACE) += stacktrace.o obj-$(CONFIG_KPROBES) += kprobes.o diff --git a/arch/s390/kernel/entry.S b/arch/s390/kernel/entry.S index 857b6526d29833..a73464dba2e3b3 100644 --- a/arch/s390/kernel/entry.S +++ b/arch/s390/kernel/entry.S @@ -46,7 +46,7 @@ STACK_SIZE = 1 << STACK_SHIFT STACK_INIT = STACK_SIZE - STACK_FRAME_OVERHEAD - __PT_SIZE _TIF_WORK = (_TIF_SIGPENDING | _TIF_NOTIFY_RESUME | _TIF_NEED_RESCHED | \ - _TIF_UPROBE) + _TIF_UPROBE | _TIF_KGR_IN_PROGRESS) _TIF_TRACE = (_TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT | _TIF_SECCOMP | \ _TIF_SYSCALL_TRACEPOINT) _CIF_WORK = (_CIF_MCCK_PENDING | _CIF_ASCE | _CIF_FPU) @@ -76,6 +76,15 @@ _PIF_WORK = (_PIF_PER_TRAP) #endif .endm + .macro HANDLE_KGRAFT TI_reg +#if IS_ENABLED(CONFIG_LIVEPATCH) + TSTMSK __TI_flags(\TI_reg),_TIF_KGR_IN_PROGRESS + jz 0f + brasl %r14,s390_handle_kgraft +0: +#endif + .endm + .macro CHECK_STACK stacksize,savearea #ifdef CONFIG_CHECK_STACK tml %r15,\stacksize - CONFIG_STACK_GUARD @@ -275,6 +284,7 @@ ENTRY(system_call) mvc __PT_INT_CODE(4,%r11),__LC_SVC_ILC stg %r14,__PT_FLAGS(%r11) .Lsysc_do_svc: + HANDLE_KGRAFT %r12 lg %r10,__TI_sysc_table(%r12) # address of system call table llgh %r8,__PT_INT_CODE+2(%r11) slag %r8,%r8,2 # shift and test for svc 0 @@ -328,6 +338,8 @@ ENTRY(system_call) #endif TSTMSK __PT_FLAGS(%r11),_PIF_PER_TRAP jo .Lsysc_singlestep + HANDLE_KGRAFT %r12 # handle kGraft just before signals and + # possible syscall restart TSTMSK __TI_flags(%r12),_TIF_SIGPENDING jo .Lsysc_sigpending TSTMSK __TI_flags(%r12),_TIF_NOTIFY_RESUME @@ -651,6 +663,7 @@ ENTRY(io_int_handler) jo .Lio_mcck_pending TSTMSK __TI_flags(%r12),_TIF_NEED_RESCHED jo .Lio_reschedule + HANDLE_KGRAFT %r12 TSTMSK __TI_flags(%r12),_TIF_SIGPENDING jo .Lio_sigpending TSTMSK __TI_flags(%r12),_TIF_NOTIFY_RESUME diff --git a/arch/s390/kernel/livepatch.c b/arch/s390/kernel/livepatch.c new file mode 100644 index 00000000000000..99ace9df5576f1 --- /dev/null +++ b/arch/s390/kernel/livepatch.c @@ -0,0 +1,14 @@ +/* + * livepatch.c - s390-specific Kernel Live Patching Core + * + * Copyright (C) 2014-2015 SUSE + * + * This file is licensed under the GPLv2. + */ + +#include <linux/sched.h> + +asmlinkage void s390_handle_kgraft(void) +{ + klp_kgraft_mark_task_safe(current); +} diff --git a/arch/x86/entry/common.c b/arch/x86/entry/common.c index 03663740c86655..30f9924451051e 100644 --- a/arch/x86/entry/common.c +++ b/arch/x86/entry/common.c @@ -141,6 +141,13 @@ unsigned long syscall_trace_enter_phase1(struct pt_regs *regs, u32 arch) } #endif +#if IS_ENABLED(CONFIG_LIVEPATCH) + if (work & _TIF_KGR_IN_PROGRESS) { + klp_kgraft_mark_task_safe(current); + work &= ~_TIF_KGR_IN_PROGRESS; + } +#endif + /* Do our best to finish without phase 2. */ if (work == 0) return ret; /* seccomp and/or nohz only (ret == 0 here) */ @@ -220,7 +227,8 @@ long syscall_trace_enter(struct pt_regs *regs) #define EXIT_TO_USERMODE_LOOP_FLAGS \ (_TIF_SIGPENDING | _TIF_NOTIFY_RESUME | _TIF_UPROBE | \ - _TIF_NEED_RESCHED | _TIF_USER_RETURN_NOTIFY) + _TIF_NEED_RESCHED | _TIF_USER_RETURN_NOTIFY | \ + _TIF_KGR_IN_PROGRESS) static void exit_to_usermode_loop(struct pt_regs *regs, u32 cached_flags) { @@ -242,6 +250,11 @@ static void exit_to_usermode_loop(struct pt_regs *regs, u32 cached_flags) if (cached_flags & _TIF_UPROBE) uprobe_notify_resume(regs); +#if IS_ENABLED(CONFIG_LIVEPATCH) + if (cached_flags & _TIF_KGR_IN_PROGRESS) + klp_kgraft_mark_task_safe(current); +#endif + /* deal with pending signal delivery */ if (cached_flags & _TIF_SIGPENDING) do_signal(regs); diff --git a/arch/x86/include/asm/thread_info.h b/arch/x86/include/asm/thread_info.h index c7b551028740f1..b2173074a9ed4b 100644 --- a/arch/x86/include/asm/thread_info.h +++ b/arch/x86/include/asm/thread_info.h @@ -106,6 +106,7 @@ struct thread_info { #define TIF_IO_BITMAP 22 /* uses I/O bitmap */ #define TIF_FORCED_TF 24 /* true if TF in eflags artificially */ #define TIF_BLOCKSTEP 25 /* set when we want DEBUGCTLMSR_BTF */ +#define TIF_KGR_IN_PROGRESS 26 /* kGraft patching running */ #define TIF_LAZY_MMU_UPDATES 27 /* task is updating the mmu lazily */ #define TIF_SYSCALL_TRACEPOINT 28 /* syscall tracepoint instrumentation */ #define TIF_ADDR32 29 /* 32-bit address space on 64 bits */ @@ -129,6 +130,7 @@ struct thread_info { #define _TIF_IO_BITMAP (1 << TIF_IO_BITMAP) #define _TIF_FORCED_TF (1 << TIF_FORCED_TF) #define _TIF_BLOCKSTEP (1 << TIF_BLOCKSTEP) +#define _TIF_KGR_IN_PROGRESS (1 << TIF_KGR_IN_PROGRESS) #define _TIF_LAZY_MMU_UPDATES (1 << TIF_LAZY_MMU_UPDATES) #define _TIF_SYSCALL_TRACEPOINT (1 << TIF_SYSCALL_TRACEPOINT) #define _TIF_ADDR32 (1 << TIF_ADDR32) @@ -138,12 +140,12 @@ struct thread_info { #define _TIF_WORK_SYSCALL_ENTRY \ (_TIF_SYSCALL_TRACE | _TIF_SYSCALL_EMU | _TIF_SYSCALL_AUDIT | \ _TIF_SECCOMP | _TIF_SINGLESTEP | _TIF_SYSCALL_TRACEPOINT | \ - _TIF_NOHZ) + _TIF_NOHZ | _TIF_KGR_IN_PROGRESS) /* work to do on any return to user space */ #define _TIF_ALLWORK_MASK \ ((0x0000FFFF & ~_TIF_SECCOMP) | _TIF_SYSCALL_TRACEPOINT | \ - _TIF_NOHZ) + _TIF_NOHZ | _TIF_KGR_IN_PROGRESS) /* flags to check in __switch_to() */ #define _TIF_WORK_CTXSW \ diff --git a/fs/proc/base.c b/fs/proc/base.c index 4bd5d3118acd4b..eb3b357aa4d393 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -2243,6 +2243,17 @@ static const struct file_operations proc_timers_operations = { .release = seq_release_private, }; +#if IS_ENABLED(CONFIG_LIVEPATCH) +static int proc_pid_kgr_in_progress(struct seq_file *m, + struct pid_namespace *ns, struct pid *pid, + struct task_struct *task) +{ + seq_printf(m, "%d\n", klp_kgraft_task_in_progress(task)); + + return 0; +} +#endif /* IS_ENABLED(CONFIG_LIVEPATCH) */ + static int proc_pident_instantiate(struct inode *dir, struct dentry *dentry, struct task_struct *task, const void *ptr) { @@ -2820,6 +2831,9 @@ static const struct pid_entry tgid_base_stuff[] = { #ifdef CONFIG_CHECKPOINT_RESTORE REG("timers", S_IRUGO, proc_timers_operations), #endif +#if IS_ENABLED(CONFIG_LIVEPATCH) + ONE("kgr_in_progress", S_IRUSR, proc_pid_kgr_in_progress), +#endif }; static int proc_tgid_base_readdir(struct file *file, struct dir_context *ctx) diff --git a/include/linux/sched.h b/include/linux/sched.h index fa39434e3fdd1c..d9bebaa6fef644 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -3168,6 +3168,29 @@ static inline void mm_update_next_owner(struct mm_struct *mm) } #endif /* CONFIG_MEMCG */ +#if IS_ENABLED(CONFIG_LIVEPATCH) +static inline void klp_kgraft_mark_task_safe(struct task_struct *p) +{ + clear_tsk_thread_flag(p, TIF_KGR_IN_PROGRESS); +} +static inline void klp_kgraft_mark_task_in_progress(struct task_struct *p) +{ + set_tsk_thread_flag(p, TIF_KGR_IN_PROGRESS); +} + +static inline bool klp_kgraft_task_in_progress(struct task_struct *p) +{ + return test_tsk_thread_flag(p, TIF_KGR_IN_PROGRESS); +} +#else +static inline void klp_kgraft_mark_task_safe(struct task_struct *p) { } +static inline void klp_kgraft_mark_task_in_progress(struct task_struct *p) { } +static inline bool klp_kgraft_task_in_progress(struct task_struct *p) +{ + return false; +} +#endif /* IS_ENABLED(CONFIG_LIVEPATCH) */ + static inline unsigned long task_rlimit(const struct task_struct *tsk, unsigned int limit) { |