Skip to contentSkip to conversation

livepatch: hybrid consistency model

RFC PATCH Superseded Josh Poimboeuf 12 participants 118 messages Apr 28, 2016 – Jun 23, 2016

Subsystems (14)
Documentation x86 PowerPC ACPI +10 more subsystems

v2 of 5 Superseded Latest is v5 →

lore
Share & tools
lore
#0 lore raw

v2 cover livepatch: hybrid consistency model

This is v2 of the livepatch hybrid consistency model, based on linux-next/master.

v1 of this patch set was posted over a year ago:

https://lkml.kernel.org/r/[email protected]

The biggest complaint at that time was that stack traces are unreliable. Since CONFIG_STACK_VALIDATION was merged, that issue has been addressed. I've also tried to address all other outstanding complaints and issues.

Ingo and Peter, note that I'm using task_rq_lock() in patch 17/18 to make sure a task stays asleep while its stack gets checked. I'm not sure if there's a better way to achieve that goal -- any suggestions there would be greatly appreciated.

Patches 1-7 create a mechanism for detecting whether a given stack trace can be deemed reliable.

Patches 8-18 add the consistency model. See patch 17/18 for more details about the consistency model itself.

Remaining TODOs:
- how to patch kthreads without RELIABLE_STACKTRACE?
- safe patch module removal
- fake signal facility
- allow user to force a task to the patched state
- enable the patching of kthreads which are sleeping on affected functions, via the livepatch ftrace handler
- WARN on certain stack error conditions

v2:
- "universe" -> "patch state"
- rename klp_update_task_universe() -> klp_patch_task()
- add preempt IRQ tracking (TF_PREEMPT_IRQ)
- fix print_context_stack_reliable() bug
- improve print_context_stack_reliable() comments
- klp_ftrace_handler comment fixes
- add "patch_state" proc file to tid_base_stuff
- schedule work even for !RELIABLE_STACKTRACE
- forked child inherits patch state from parent
- add detailed comment to livepatch.h klp_func definition about the klp_func patched/transition state transitions
- update exit_to_usermode_loop() comment
- clear all TIF_KLP_NEED_UPDATE flags in klp_complete_transition()
- remove unnecessary function externs
- add livepatch documentation, sysfs documentation, /proc documentation
- /proc/pid/patch_state: -1 means no patch is currently being applied/reverted
- "TIF_KLP_NEED_UPDATE" -> "TIF_PATCH_PENDING"
- support for s390 and powerpc-le
- don't assume stacks with dynamic ftrace trampolines are reliable
- add _TIF_ALLWORK_MASK info to commit log

v1.9:
- revive from the dead and rebased
- reliable stacks!
- add support for immediate consistency model
- add a ton of comments
- fix up memory barriers
- remove "allow patch modules to be removed" patch for now, it still needs more discussion and thought - it can be done with something
- "proc/pid/universe" -> "proc/pid/patch_status"
- remove WARN_ON_ONCE from !func condition in ftrace handler -- can happen because of RCU
- keep klp_mutex private by putting the work_fn in core.c
- convert states from int to boolean
- remove obsolete '@state' comments
- several header file and include improvements suggested by Jiri S
- change kallsyms_lookup_size_offset() errors from EINVAL -> ENOENT
- change proc file permissions S_IRUGO -> USR
- use klp_for_each_object/func helpers

Jiri Slaby (1):
livepatch/s390: reorganize TIF thread flag bits

Josh Poimboeuf (16):
x86/asm/head: clean up initial stack variable x86/asm/head: use a common function for starting CPUs x86/asm/head: standardize the bottom of the stack for idle tasks
x86: move _stext marker before head code
sched: add task flag for preempt IRQ tracking
x86: dump_trace() error handling
stacktrace/x86: function for detecting reliable stack traces
livepatch: temporary stubs for klp_patch_pending() and
klp_patch_task()
livepatch/x86: add TIF_PATCH_PENDING thread flag livepatch/powerpc: add TIF_PATCH_PENDING thread flag
livepatch: separate enabled and patched states
livepatch: remove unnecessary object loaded check
livepatch: move patching functions into patch.c
livepatch: store function sizes
livepatch: change to a per-task consistency model
livepatch: add /proc/<pid>/patch_state

Miroslav Benes (1):
livepatch/s390: add TIF_PATCH_PENDING thread flag

 Documentation/ABI/testing/sysfs-kernel-livepatch |   8 +
 Documentation/filesystems/proc.txt               |  18 +
 Documentation/livepatch/livepatch.txt            | 132 ++++++-
 arch/Kconfig                                     |   6 +
 arch/powerpc/include/asm/thread_info.h           |   4 +-
 arch/powerpc/kernel/signal.c                     |   4 +
 arch/s390/include/asm/thread_info.h              |  24 +-
 arch/s390/kernel/entry.S                         |  31 +-
 arch/x86/Kconfig                                 |   1 +
 arch/x86/entry/common.c                          |   9 +-
 arch/x86/include/asm/realmode.h                  |   2 +-
 arch/x86/include/asm/smp.h                       |   3 -
 arch/x86/include/asm/stacktrace.h                |  36 +-
 arch/x86/include/asm/thread_info.h               |   2 +
 arch/x86/kernel/acpi/sleep.c                     |   2 +-
 arch/x86/kernel/dumpstack.c                      | 108 +++++-
 arch/x86/kernel/dumpstack_32.c                   |  22 +-
 arch/x86/kernel/dumpstack_64.c                   |  53 ++-
 arch/x86/kernel/head_32.S                        |   8 +-
 arch/x86/kernel/head_64.S                        |  34 +-
 arch/x86/kernel/smpboot.c                        |   2 +-
 arch/x86/kernel/stacktrace.c                     |  24 ++
 arch/x86/kernel/vmlinux.lds.S                    |   2 +-
 fs/proc/base.c                                   |  15 +
 include/linux/init_task.h                        |   9 +
 include/linux/kernel.h                           |   1 +
 include/linux/livepatch.h                        |  57 ++-
 include/linux/sched.h                            |   4 +
 include/linux/stacktrace.h                       |  20 +-
 kernel/extable.c                                 |   2 +-
 kernel/fork.c                                    |   5 +-
 kernel/livepatch/Makefile                        |   2 +-
 kernel/livepatch/core.c                          | 342 +++++-----------
 kernel/livepatch/patch.c                         | 254 ++++++++++++
 kernel/livepatch/patch.h                         |  33 ++
 kernel/livepatch/transition.c                    | 474 +++++++++++++++++++++++
 kernel/livepatch/transition.h                    |  14 +
 kernel/sched/core.c                              |   4 +
 kernel/sched/idle.c                              |   4 +
 kernel/stacktrace.c                              |   4 +-
 lib/Kconfig.debug                                |   6 +
 41 files changed, 1413 insertions(+), 372 deletions(-)
 create mode 100644 kernel/livepatch/patch.c
 create mode 100644 kernel/livepatch/patch.h
 create mode 100644 kernel/livepatch/transition.c
 create mode 100644 kernel/livepatch/transition.h
Signature · 1 line
2.4.11
#1 lore raw

RFC PATCH v2 1/18 x86/asm/head: clean up initial stack variable

The stack_start variable is similar in usage to initial_code and
initial_gs: they're all stored in head_64.S and they're all updated by SMP and suspend/resume before starting a CPU.

Rename stack_start to initial_stack to be more consistent with the others.

Also do a few other related cleanups:

- Remove the unused init_rsp variable declaration.

- Remove the ".word 0" statement after the initial_stack definition because it has no apparent function.

---
 arch/x86/include/asm/realmode.h |  2 +-
 arch/x86/include/asm/smp.h      |  3 ---
 arch/x86/kernel/acpi/sleep.c    |  2 +-
 arch/x86/kernel/head_32.S       |  8 ++++----
 arch/x86/kernel/head_64.S       | 10 ++++------
 arch/x86/kernel/smpboot.c       |  2 +-
 6 files changed, 11 insertions(+), 16 deletions(-)
arch/x86/include/asm/realmode.h +1 -1
index 9c6b890..677a671 100644
@@ -44,9 +44,9 @@ struct trampoline_header {
 extern struct real_mode_header *real_mode_header;
 extern unsigned char real_mode_blob_end[];
 
-extern unsigned long init_rsp;
 extern unsigned long initial_code;
 extern unsigned long initial_gs;
+extern unsigned long initial_stack;
 
 extern unsigned char real_mode_blob[];
 extern unsigned char real_mode_relocs[];
arch/x86/include/asm/smp.h +0 -3
index 66b0573..a9ac31b 100644
@@ -38,9 +38,6 @@ DECLARE_EARLY_PER_CPU_READ_MOSTLY(u16, x86_bios_cpu_apicid);
 DECLARE_EARLY_PER_CPU_READ_MOSTLY(int, x86_cpu_to_logical_apicid);
 #endif
 
-/* Static state in head.S used to set up a CPU */
-extern unsigned long stack_start; /* Initial stack pointer address */
-
 struct task_struct;
 
 struct smp_ops {
arch/x86/kernel/acpi/sleep.c +1 -1
index adb3eaf..4858733 100644
@@ -99,7 +99,7 @@ int x86_acpi_suspend_lowlevel(void)
 	saved_magic = 0x12345678;
 #else /* CONFIG_64BIT */
 #ifdef CONFIG_SMP
-	stack_start = (unsigned long)temp_stack + sizeof(temp_stack);
+	initial_stack = (unsigned long)temp_stack + sizeof(temp_stack);
 	early_gdt_descr.address =
 			(unsigned long)get_cpu_gdt_table(smp_processor_id());
 	initial_gs = per_cpu_offset(smp_processor_id());
arch/x86/kernel/head_32.S +4 -4
index 6770865..da840be 100644
@@ -94,7 +94,7 @@ RESERVE_BRK(pagetables, INIT_MAP_SIZE)
  */
 __HEAD
 ENTRY(startup_32)
-	movl pa(stack_start),%ecx
+	movl pa(initial_stack),%ecx
 	
 	/* test KEEP_SEGMENTS flag to see if the bootloader is asking
 		us to not reload segments */
@@ -286,7 +286,7 @@ num_subarch_entries = (. - subarch_entries) / 4
  * start_secondary().
  */
 ENTRY(start_cpu0)
-	movl stack_start, %ecx
+	movl initial_stack, %ecx
 	movl %ecx, %esp
 	jmp  *(initial_code)
 ENDPROC(start_cpu0)
@@ -307,7 +307,7 @@ ENTRY(startup_32_smp)
 	movl %eax,%es
 	movl %eax,%fs
 	movl %eax,%gs
-	movl pa(stack_start),%ecx
+	movl pa(initial_stack),%ecx
 	movl %eax,%ss
 	leal -__PAGE_OFFSET(%ecx),%esp
 
@@ -709,7 +709,7 @@ ENTRY(initial_page_table)
 
 .data
 .balign 4
-ENTRY(stack_start)
+ENTRY(initial_stack)
 	.long init_thread_union+THREAD_SIZE
 
 __INITRODATA
arch/x86/kernel/head_64.S +4 -6
index 5df831e..792c3bb 100644
@@ -226,7 +226,7 @@ ENTRY(secondary_startup_64)
 	movq	%rax, %cr0
 
 	/* Setup a boot time stack */
-	movq stack_start(%rip), %rsp
+	movq initial_stack(%rip), %rsp
 
 	/* zero EFLAGS after setting rsp */
 	pushq $0
@@ -309,7 +309,7 @@ ENTRY(secondary_startup_64)
  * start_secondary().
  */
 ENTRY(start_cpu0)
-	movq stack_start(%rip),%rsp
+	movq initial_stack(%rip),%rsp
 	movq	initial_code(%rip),%rax
 	pushq	$0		# fake return address to stop unwinder
 	pushq	$__KERNEL_CS	# set correct cs
@@ -318,17 +318,15 @@ ENTRY(start_cpu0)
 ENDPROC(start_cpu0)
 #endif
 
-	/* SMP bootup changes these two */
+	/* SMP bootup changes these variables */
 	__REFDATA
 	.balign	8
 	GLOBAL(initial_code)
 	.quad	x86_64_start_kernel
 	GLOBAL(initial_gs)
 	.quad	INIT_PER_CPU_VAR(irq_stack_union)
-
-	GLOBAL(stack_start)
+	GLOBAL(initial_stack)
 	.quad  init_thread_union+THREAD_SIZE-8
-	.word  0
 	__FINITDATA
 
 bad_address:
arch/x86/kernel/smpboot.c +1 -1
index 1fe4130..503682a 100644
@@ -950,7 +950,7 @@ static int do_boot_cpu(int apicid, int cpu, struct task_struct *idle)
 
 	early_gdt_descr.address = (unsigned long)get_cpu_gdt_table(cpu);
 	initial_code = (unsigned long)start_secondary;
-	stack_start  = idle->thread.sp;
+	initial_stack  = idle->thread.sp;
 
 	/*
 	 * Enable the espfix hack for this CPU
Signature · 1 line
2.4.11
#2 lore raw

RFC PATCH v2 2/18 x86/asm/head: use a common function for starting CPUs

In reply to Josh Poimboeuf

There are two different pieces of code for starting a CPU: start_cpu0() and the end of secondary_startup_64(). They're identical except for the stack setup. Combine the common parts into a shared start_cpu() function.

---
 arch/x86/kernel/head_64.S | 19 +++++++++----------
 1 file changed, 9 insertions(+), 10 deletions(-)
arch/x86/kernel/head_64.S +9 -10
index 792c3bb..6dbd2c0 100644
@@ -264,13 +264,15 @@ ENTRY(secondary_startup_64)
 	movl	$MSR_GS_BASE,%ecx
 	movl	initial_gs(%rip),%eax
 	movl	initial_gs+4(%rip),%edx
-	wrmsr	
+	wrmsr
 
 	/* rsi is pointer to real mode structure with interesting info.
 	   pass it to C */
 	movq	%rsi, %rdi
-	
-	/* Finally jump to run C code and to be on real kernel address
+
+ENTRY(start_cpu)
+	/*
+	 * Jump to run C code and to be on a real kernel address.
 	 * Since we are running on identity-mapped space we have to jump
 	 * to the full 64bit address, this is only possible as indirect
 	 * jump.  In addition we need to ensure %cs is set so we make this
@@ -299,6 +301,7 @@ ENTRY(secondary_startup_64)
 	pushq	$__KERNEL_CS	# set correct cs
 	pushq	%rax		# target address in negative space
 	lretq
+ENDPROC(start_cpu)
 
 #include "verify_cpu.S"
 
@@ -306,15 +309,11 @@ ENTRY(secondary_startup_64)
 /*
  * Boot CPU0 entry point. It's called from play_dead(). Everything has been set
  * up already except stack. We just set up stack here. Then call
- * start_secondary().
+ * start_secondary() via start_cpu().
  */
 ENTRY(start_cpu0)
-	movq initial_stack(%rip),%rsp
-	movq	initial_code(%rip),%rax
-	pushq	$0		# fake return address to stop unwinder
-	pushq	$__KERNEL_CS	# set correct cs
-	pushq	%rax		# target address in negative space
-	lretq
+	movq	initial_stack(%rip), %rsp
+	jmp	start_cpu
 ENDPROC(start_cpu0)
 #endif
 
Signature · 1 line
2.4.11
#3 lore raw

RFC PATCH v2 3/18 x86/asm/head: standardize the bottom of the stack for idle tasks

In reply to Josh Poimboeuf

Thanks to all the recent x86 entry code refactoring, most tasks' kernel stacks start at the same offset right above their saved pt_regs, regardless of which syscall was used to enter the kernel. That creates a nice convention which makes it straightforward to identify the "bottom" of the stack, which can be useful for stack walking code which needs to verify the stack is sane.

However there are still a few types of tasks which don't yet follow that convention:

1) CPU idle tasks, aka the "swapper" tasks

2) freshly forked TIF_FORK tasks which don't have a stack at all

Make the idle tasks conform to the new stack bottom convention by starting their stack at a sizeof(pt_regs) offset from the end of the stack page.

---
 arch/x86/kernel/head_64.S | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)
arch/x86/kernel/head_64.S +4 -3
index 6dbd2c0..0b12311 100644
@@ -296,8 +296,9 @@ ENTRY(start_cpu)
 	 *	REX.W + FF /5 JMP m16:64 Jump far, absolute indirect,
 	 *		address given in m16:64.
 	 */
-	movq	initial_code(%rip),%rax
-	pushq	$0		# fake return address to stop unwinder
+	call	1f		# put return address on stack for unwinder
+1:	xorq	%rbp, %rbp	# clear frame pointer
+	movq	initial_code(%rip), %rax
 	pushq	$__KERNEL_CS	# set correct cs
 	pushq	%rax		# target address in negative space
 	lretq
@@ -325,7 +326,7 @@ ENDPROC(start_cpu0)
 	GLOBAL(initial_gs)
 	.quad	INIT_PER_CPU_VAR(irq_stack_union)
 	GLOBAL(initial_stack)
-	.quad  init_thread_union+THREAD_SIZE-8
+	.quad  init_thread_union + THREAD_SIZE - SIZEOF_PTREGS
 	__FINITDATA
 
 bad_address:
Signature · 1 line
2.4.11
#11 lore raw

RFC PATCH v2 4/18 x86: move _stext marker before head code

In reply to Josh Poimboeuf

When core_kernel_text() is used to determine whether an address on a task's stack trace is a kernel text address, it incorrectly returns false for early text addresses for the head code between the _text and _stext markers.

Head code is text code too, so mark it as such. This seems to match the intent of other users of the _stext symbol, and it also seems consistent with what other architectures are already doing.

---
 arch/x86/kernel/vmlinux.lds.S | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
arch/x86/kernel/vmlinux.lds.S +1 -1
index 4c941f8..79e15ef 100644
@@ -91,10 +91,10 @@ SECTIONS
 	/* Text and read-only data */
 	.text :  AT(ADDR(.text) - LOAD_OFFSET) {
 		_text = .;
+		_stext = .;
 		/* bootstrapping code */
 		HEAD_TEXT
 		. = ALIGN(8);
-		_stext = .;
 		TEXT_TEXT
 		SCHED_TEXT
 		LOCK_TEXT
Signature · 1 line
2.4.11
#12 lore raw

RFC PATCH v2 5/18 sched: add task flag for preempt IRQ tracking

In reply to Josh Poimboeuf

A preempted function might not have had a chance to save the frame pointer to the stack yet, which can result in its caller getting skipped on a stack trace.

Add a flag to indicate when the task has been preempted so that stack dump code can determine whether the stack trace is reliable.

---
 include/linux/sched.h | 1 +
 kernel/fork.c         | 2 +-
 kernel/sched/core.c   | 4 ++++
 3 files changed, 6 insertions(+), 1 deletion(-)
include/linux/sched.h +1 -0
index 3d31572..fb364a0 100644
@@ -2137,6 +2137,7 @@ extern void thread_group_cputime_adjusted(struct task_struct *p, cputime_t *ut,
 #define PF_SWAPWRITE	0x00800000	/* Allowed to write to swap */
 #define PF_NO_SETAFFINITY 0x04000000	/* Userland is not allowed to meddle with cpus_allowed */
 #define PF_MCE_EARLY    0x08000000      /* Early kill for mce process policy */
+#define PF_PREEMPT_IRQ	0x10000000	/* Thread is preempted by an irq */
 #define PF_MUTEX_TESTER	0x20000000	/* Thread belongs to the rt mutex tester */
 #define PF_FREEZER_SKIP	0x40000000	/* Freezer should not count it as freezable */
 #define PF_SUSPEND_TASK 0x80000000      /* this thread called freeze_processes and should not be frozen */
kernel/fork.c +1 -1
index b73a539..d2fe04a 100644
@@ -1373,7 +1373,7 @@ static struct task_struct *copy_process(unsigned long clone_flags,
 		goto bad_fork_cleanup_count;
 
 	delayacct_tsk_init(p);	/* Must remain after dup_task_struct() */
-	p->flags &= ~(PF_SUPERPRIV | PF_WQ_WORKER);
+	p->flags &= ~(PF_SUPERPRIV | PF_WQ_WORKER | PF_PREEMPT_IRQ);
 	p->flags |= PF_FORKNOEXEC;
 	INIT_LIST_HEAD(&p->children);
 	INIT_LIST_HEAD(&p->sibling);
kernel/sched/core.c +4 -0
index 9d84d60..7594267 100644
@@ -3422,6 +3422,8 @@ asmlinkage __visible void __sched preempt_schedule_irq(void)
 
 	prev_state = exception_enter();
 
+	current->flags |= PF_PREEMPT_IRQ;
+
 	do {
 		preempt_disable();
 		local_irq_enable();
@@ -3430,6 +3432,8 @@ asmlinkage __visible void __sched preempt_schedule_irq(void)
 		sched_preempt_enable_no_resched();
 	} while (need_resched());
 
+	current->flags &= ~PF_PREEMPT_IRQ;
+
 	exception_exit(prev_state);
 }
 
Signature · 1 line
2.4.11
#60 lore raw

RFC PATCH v2 6/18 x86: dump_trace() error handling

In reply to Josh Poimboeuf

In preparation for being able to determine whether a given stack trace is reliable, allow the stacktrace_ops functions to propagate errors to dump_trace().

---
 arch/x86/include/asm/stacktrace.h | 36 +++++++++++++++-----------
 arch/x86/kernel/dumpstack.c       | 31 +++++++++++------------
 arch/x86/kernel/dumpstack_32.c    | 22 ++++++++++------
 arch/x86/kernel/dumpstack_64.c    | 53 ++++++++++++++++++++++++++-------------
 4 files changed, 87 insertions(+), 55 deletions(-)
arch/x86/include/asm/stacktrace.h +21 -15
index 7c247e7..a64523f3 100644
@@ -14,26 +14,32 @@ extern int kstack_depth_to_print;
 struct thread_info;
 struct stacktrace_ops;
 
-typedef unsigned long (*walk_stack_t)(struct thread_info *tinfo,
-				      unsigned long *stack,
-				      unsigned long bp,
-				      const struct stacktrace_ops *ops,
-				      void *data,
-				      unsigned long *end,
-				      int *graph);
-
-extern unsigned long
+typedef int (*walk_stack_t)(struct thread_info *tinfo,
+			    unsigned long *stack,
+			    unsigned long *bp,
+			    const struct stacktrace_ops *ops,
+			    void *data,
+			    unsigned long *end,
+			    int *graph);
+
+extern int
 print_context_stack(struct thread_info *tinfo,
-		    unsigned long *stack, unsigned long bp,
+		    unsigned long *stack, unsigned long *bp,
 		    const struct stacktrace_ops *ops, void *data,
 		    unsigned long *end, int *graph);
 
-extern unsigned long
+extern int
 print_context_stack_bp(struct thread_info *tinfo,
-		       unsigned long *stack, unsigned long bp,
+		       unsigned long *stack, unsigned long *bp,
 		       const struct stacktrace_ops *ops, void *data,
 		       unsigned long *end, int *graph);
 
+extern int
+print_context_stack_reliable(struct thread_info *tinfo,
+			     unsigned long *stack, unsigned long *bp,
+			     const struct stacktrace_ops *ops, void *data,
+			     unsigned long *end, int *graph);
+
 /* Generic stack tracer with callbacks */
 
 struct stacktrace_ops {
@@ -43,9 +49,9 @@ struct stacktrace_ops {
 	walk_stack_t	walk_stack;
 };
 
-void dump_trace(struct task_struct *tsk, struct pt_regs *regs,
-		unsigned long *stack, unsigned long bp,
-		const struct stacktrace_ops *ops, void *data);
+int dump_trace(struct task_struct *tsk, struct pt_regs *regs,
+	       unsigned long *stack, unsigned long bp,
+	       const struct stacktrace_ops *ops, void *data);
 
 #ifdef CONFIG_X86_32
 #define STACKSLOTS_PER_LINE 8
arch/x86/kernel/dumpstack.c +15 -16
index 2bb25c3..13d240c 100644
@@ -92,23 +92,22 @@ static inline int valid_stack_ptr(struct thread_info *tinfo,
 	return p > t && p < t + THREAD_SIZE - size;
 }
 
-unsigned long
-print_context_stack(struct thread_info *tinfo,
-		unsigned long *stack, unsigned long bp,
-		const struct stacktrace_ops *ops, void *data,
-		unsigned long *end, int *graph)
+int print_context_stack(struct thread_info *tinfo,
+			unsigned long *stack, unsigned long *bp,
+			const struct stacktrace_ops *ops, void *data,
+			unsigned long *end, int *graph)
 {
-	struct stack_frame *frame = (struct stack_frame *)bp;
+	struct stack_frame *frame = (struct stack_frame *)*bp;
 
 	while (valid_stack_ptr(tinfo, stack, sizeof(*stack), end)) {
 		unsigned long addr;
 
 		addr = *stack;
 		if (__kernel_text_address(addr)) {
-			if ((unsigned long) stack == bp + sizeof(long)) {
+			if ((unsigned long) stack == *bp + sizeof(long)) {
 				ops->address(data, addr, 1);
 				frame = frame->next_frame;
-				bp = (unsigned long) frame;
+				*bp = (unsigned long) frame;
 			} else {
 				ops->address(data, addr, 0);
 			}
@@ -116,17 +115,16 @@ print_context_stack(struct thread_info *tinfo,
 		}
 		stack++;
 	}
-	return bp;
+	return 0;
 }
 EXPORT_SYMBOL_GPL(print_context_stack);
 
-unsigned long
-print_context_stack_bp(struct thread_info *tinfo,
-		       unsigned long *stack, unsigned long bp,
-		       const struct stacktrace_ops *ops, void *data,
-		       unsigned long *end, int *graph)
+int print_context_stack_bp(struct thread_info *tinfo,
+			   unsigned long *stack, unsigned long *bp,
+			   const struct stacktrace_ops *ops, void *data,
+			   unsigned long *end, int *graph)
 {
-	struct stack_frame *frame = (struct stack_frame *)bp;
+	struct stack_frame *frame = (struct stack_frame *)*bp;
 	unsigned long *ret_addr = &frame->return_address;
 
 	while (valid_stack_ptr(tinfo, ret_addr, sizeof(*ret_addr), end)) {
@@ -142,7 +140,8 @@ print_context_stack_bp(struct thread_info *tinfo,
 		print_ftrace_graph_addr(addr, data, ops, tinfo, graph);
 	}
 
-	return (unsigned long)frame;
+	*bp = (unsigned long)frame;
+	return 0;
 }
 EXPORT_SYMBOL_GPL(print_context_stack_bp);
 
arch/x86/kernel/dumpstack_32.c +15 -7
index 464ffd6..e710bab 100644
@@ -38,13 +38,14 @@ static void *is_softirq_stack(unsigned long *stack, int cpu)
 	return is_irq_stack(stack, irq);
 }
 
-void dump_trace(struct task_struct *task, struct pt_regs *regs,
-		unsigned long *stack, unsigned long bp,
-		const struct stacktrace_ops *ops, void *data)
+int dump_trace(struct task_struct *task, struct pt_regs *regs,
+	       unsigned long *stack, unsigned long bp,
+	       const struct stacktrace_ops *ops, void *data)
 {
 	const unsigned cpu = get_cpu();
 	int graph = 0;
 	u32 *prev_esp;
+	int ret;
 
 	if (!task)
 		task = current;
@@ -69,8 +70,10 @@ void dump_trace(struct task_struct *task, struct pt_regs *regs,
 			end_stack = is_softirq_stack(stack, cpu);
 
 		context = task_thread_info(task);
-		bp = ops->walk_stack(context, stack, bp, ops, data,
-				     end_stack, &graph);
+		ret = ops->walk_stack(context, stack, &bp, ops, data,
+				      end_stack, &graph);
+		if (ret)
+			goto out;
 
 		/* Stop if not on irq stack */
 		if (!end_stack)
@@ -82,11 +85,16 @@ void dump_trace(struct task_struct *task, struct pt_regs *regs,
 		if (!stack)
 			break;
 
-		if (ops->stack(data, "IRQ") < 0)
-			break;
+		ret = ops->stack(data, "IRQ");
+		if (ret)
+			goto out;
+
 		touch_nmi_watchdog();
 	}
+
+out:
 	put_cpu();
+	return ret;
 }
 EXPORT_SYMBOL(dump_trace);
 
arch/x86/kernel/dumpstack_64.c +36 -17

95 lines not on this page · raw message

Signature · 1 line
2.4.11
#63 lore raw

RFC PATCH v2 7/18 stacktrace/x86: function for detecting reliable stack traces

In reply to Josh Poimboeuf

For live patching and possibly other use cases, a stack trace is only useful if it can be assured that it's completely reliable. Add a new save_stack_trace_tsk_reliable() function to achieve that.

Scenarios which indicate that a stack trace may be unreliable:

- running tasks
- interrupt stacks
- preemption
- corrupted stack data
- the stack grows the wrong way
- the stack walk doesn't reach the bottom
- the user didn't provide a large enough entries array

Also add a config option so arch-independent code can determine at build time whether the function is implemented.

---
 arch/Kconfig                 |  6 ++++
 arch/x86/Kconfig             |  1 +
 arch/x86/kernel/dumpstack.c  | 77 ++++++++++++++++++++++++++++++++++++++++++++
 arch/x86/kernel/stacktrace.c | 24 ++++++++++++++
 include/linux/kernel.h       |  1 +
 include/linux/stacktrace.h   | 20 +++++++++---
 kernel/extable.c             |  2 +-
 kernel/stacktrace.c          |  4 +--
 lib/Kconfig.debug            |  6 ++++
 9 files changed, 134 insertions(+), 7 deletions(-)
arch/Kconfig +6 -0
index 8f84fd2..ec4d480 100644
@@ -598,6 +598,12 @@ config HAVE_STACK_VALIDATION
 	  Architecture supports the 'objtool check' host tool command, which
 	  performs compile-time stack metadata validation.
 
+config HAVE_RELIABLE_STACKTRACE
+	bool
+	help
+	  Architecture has a save_stack_trace_tsk_reliable() function which
+	  only returns a stack trace if it can guarantee the trace is reliable.
+
 #
 # ABI hall of shame
 #
arch/x86/Kconfig +1 -0
index 0b128b4..78c4e00 100644
@@ -140,6 +140,7 @@ config X86
 	select HAVE_PERF_REGS
 	select HAVE_PERF_USER_STACK_DUMP
 	select HAVE_REGS_AND_STACK_ACCESS_API
+	select HAVE_RELIABLE_STACKTRACE		if X86_64 && FRAME_POINTER
 	select HAVE_SYSCALL_TRACEPOINTS
 	select HAVE_UID16			if X86_32 || IA32_EMULATION
 	select HAVE_UNSTABLE_SCHED_CLOCK
arch/x86/kernel/dumpstack.c +77 -0

83 lines not on this page · raw message

arch/x86/kernel/stacktrace.c +24 -0
index 9ee98ee..10882e4 100644
@@ -148,3 +148,27 @@ void save_stack_trace_user(struct stack_trace *trace)
 		trace->entries[trace->nr_entries++] = ULONG_MAX;
 }
 
+#ifdef CONFIG_RELIABLE_STACKTRACE
+
+static int save_stack_stack_reliable(void *data, char *name)
+{
+	return -EINVAL;
+}
+
+static const struct stacktrace_ops save_stack_ops_reliable = {
+	.stack		= save_stack_stack_reliable,
+	.address	= save_stack_address,
+	.walk_stack	= print_context_stack_reliable,
+};
+
+/*
+ * Returns 0 if the stack trace is deemed reliable.  The caller must ensure
+ * that the task is either sleeping or is the current task.
+ */
+int save_stack_trace_tsk_reliable(struct task_struct *tsk,
+				  struct stack_trace *trace)
+{
+	return dump_trace(tsk, NULL, NULL, 0, &save_stack_ops_reliable, trace);
+}
+
+#endif /* CONFIG_RELIABLE_STACKTRACE */
include/linux/kernel.h +1 -0
index cc73982..6be1e82 100644
@@ -429,6 +429,7 @@ extern char *get_options(const char *str, int nints, int *ints);
 extern unsigned long long memparse(const char *ptr, char **retptr);
 extern bool parse_option_str(const char *str, const char *option);
 
+extern int init_kernel_text(unsigned long addr);
 extern int core_kernel_text(unsigned long addr);
 extern int core_kernel_data(unsigned long addr);
 extern int __kernel_text_address(unsigned long addr);
include/linux/stacktrace.h +16 -4
index 0a34489..527e4cc 100644
@@ -2,17 +2,18 @@
 #define __LINUX_STACKTRACE_H
 
 #include <linux/types.h>
+#include <linux/errno.h>
 
 struct task_struct;
 struct pt_regs;
 
-#ifdef CONFIG_STACKTRACE
 struct stack_trace {
 	unsigned int nr_entries, max_entries;
 	unsigned long *entries;
 	int skip;	/* input argument: How many entries to skip */
 };
 
+#ifdef CONFIG_STACKTRACE
 extern void save_stack_trace(struct stack_trace *trace);
 extern void save_stack_trace_regs(struct pt_regs *regs,
 				  struct stack_trace *trace);
@@ -29,12 +30,23 @@ extern void save_stack_trace_user(struct stack_trace *trace);
 # define save_stack_trace_user(trace)              do { } while (0)
 #endif
 
-#else
+#else /* !CONFIG_STACKTRACE */
 # define save_stack_trace(trace)			do { } while (0)
 # define save_stack_trace_tsk(tsk, trace)		do { } while (0)
 # define save_stack_trace_user(trace)			do { } while (0)
 # define print_stack_trace(trace, spaces)		do { } while (0)
 # define snprint_stack_trace(buf, size, trace, spaces)	do { } while (0)
-#endif
+#endif /* CONFIG_STACKTRACE */
 
-#endif
+#ifdef CONFIG_RELIABLE_STACKTRACE
+extern int save_stack_trace_tsk_reliable(struct task_struct *tsk,
+					 struct stack_trace *trace);
+#else
+static inline int save_stack_trace_tsk_reliable(struct task_struct *tsk,
+						struct stack_trace *trace)
+{
+	return -ENOSYS;
+}
+#endif /* CONFIG_RELIABLE_STACKTRACE */
+
+#endif /* __LINUX_STACKTRACE_H */
kernel/extable.c +1 -1
index e820cce..c085844 100644
@@ -58,7 +58,7 @@ const struct exception_table_entry *search_exception_tables(unsigned long addr)
 	return e;
 }
 
-static inline int init_kernel_text(unsigned long addr)
+int init_kernel_text(unsigned long addr)
 {
 	if (addr >= (unsigned long)_sinittext &&
 	    addr < (unsigned long)_einittext)
kernel/stacktrace.c +2 -2
index b6e4c16..f35bc5d 100644
@@ -58,8 +58,8 @@ int snprint_stack_trace(char *buf, size_t size,
 EXPORT_SYMBOL_GPL(snprint_stack_trace);
 
 /*
- * Architectures that do not implement save_stack_trace_tsk or
- * save_stack_trace_regs get this weak alias and a once-per-bootup warning
+ * Architectures that do not implement save_stack_trace_*()
+ * get this weak alias and a once-per-bootup warning
  * (whenever this facility is utilized - for example by procfs):
  */
 __weak void
lib/Kconfig.debug +6 -0
index 5d57177..189a2d7 100644
@@ -1164,6 +1164,12 @@ config STACKTRACE
 	  It is also used by various kernel debugging features that require
 	  stack trace generation.
 
+config RELIABLE_STACKTRACE
+	def_bool y
+	depends on HAVE_RELIABLE_STACKTRACE
+	depends on STACKTRACE
+	depends on STACK_VALIDATION
+
 config DEBUG_KOBJECT
 	bool "kobject debugging"
 	depends on DEBUG_KERNEL
Signature · 1 line
2.4.11
#64 lore raw

RFC PATCH v2 8/18 livepatch: temporary stubs for klp_patch_pending() and klp_patch_task()

In reply to Josh Poimboeuf

Create temporary stubs for klp_patch_pending() and klp_patch_task() so we can add TIF_PATCH_PENDING to different architectures in separate patches without breaking build bisectability.

---
 include/linux/livepatch.h | 7 ++++++-
 kernel/livepatch/core.c   | 3 +++
 2 files changed, 9 insertions(+), 1 deletion(-)
include/linux/livepatch.h +6 -1
index 0933ca4..a8c6c9c 100644
@@ -118,10 +118,15 @@ int klp_disable_patch(struct klp_patch *);
 int klp_module_coming(struct module *mod);
 void klp_module_going(struct module *mod);
 
+static inline bool klp_patch_pending(struct task_struct *task) { return false; }
+void klp_patch_task(struct task_struct *task);
+
 #else /* !CONFIG_LIVEPATCH */
 
 static inline int klp_module_coming(struct module *mod) { return 0; }
-static inline void klp_module_going(struct module *mod) { }
+static inline void klp_module_going(struct module *mod) {}
+static inline bool klp_patch_pending(struct task_struct *task) { return false; }
+static inline void klp_patch_task(struct task_struct *task) {}
 
 #endif /* CONFIG_LIVEPATCH */
 
kernel/livepatch/core.c +3 -0
index a19f195..6ea6880 100644
@@ -64,6 +64,9 @@ static LIST_HEAD(klp_ops);
 
 static struct kobject *klp_root_kobj;
 
+/* TODO: temporary stub */
+void klp_patch_task(struct task_struct *task) {}
+
 static struct klp_ops *klp_find_ops(unsigned long old_addr)
 {
 	struct klp_ops *ops;
Signature · 1 line
2.4.11
#65 lore raw

RFC PATCH v2 9/18 livepatch/x86: add TIF_PATCH_PENDING thread flag

In reply to Josh Poimboeuf

Add the TIF_PATCH_PENDING thread flag to enable the new livepatch per-task consistency model for x86_64. The bit getting set indicates the thread has a pending patch which needs to be applied when the thread exits the kernel.

The bit is placed in the least-significant word of the thread_info flags so that it gets automatically included in the _TIF_ALLWORK_MASK macro. This results in exit_to_usermode_loop() and klp_patch_task() getting called when the bit is set.

---
 arch/x86/entry/common.c            | 9 ++++++---
 arch/x86/include/asm/thread_info.h | 2 ++
 2 files changed, 8 insertions(+), 3 deletions(-)
arch/x86/entry/common.c +6 -3
index ec138e5..0eaa1d9 100644
@@ -21,6 +21,7 @@
 #include <linux/context_tracking.h>
 #include <linux/user-return-notifier.h>
 #include <linux/uprobes.h>
+#include <linux/livepatch.h>
 
 #include <asm/desc.h>
 #include <asm/traps.h>
@@ -202,14 +203,13 @@ 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_PATCH_PENDING)
 
 static void exit_to_usermode_loop(struct pt_regs *regs, u32 cached_flags)
 {
 	/*
 	 * In order to return to user mode, we need to have IRQs off with
-	 * none of _TIF_SIGPENDING, _TIF_NOTIFY_RESUME, _TIF_USER_RETURN_NOTIFY,
-	 * _TIF_UPROBE, or _TIF_NEED_RESCHED set.  Several of these flags
+	 * none of EXIT_TO_USERMODE_LOOP_FLAGS set.  Several of these flags
 	 * can be set at any time on preemptable kernels if we have IRQs on,
 	 * so we need to loop.  Disabling preemption wouldn't help: doing the
 	 * work to clear some of the flags can sleep.
@@ -236,6 +236,9 @@ static void exit_to_usermode_loop(struct pt_regs *regs, u32 cached_flags)
 		if (cached_flags & _TIF_USER_RETURN_NOTIFY)
 			fire_user_return_notifiers();
 
+		if (cached_flags & _TIF_PATCH_PENDING)
+			klp_patch_task(current);
+
 		/* Disable IRQs and retry */
 		local_irq_disable();
 
arch/x86/include/asm/thread_info.h +2 -0
index 30c133a..4e4f50e 100644
@@ -97,6 +97,7 @@ struct thread_info {
 #define TIF_SECCOMP		8	/* secure computing */
 #define TIF_USER_RETURN_NOTIFY	11	/* notify kernel of userspace return */
 #define TIF_UPROBE		12	/* breakpointed or singlestepping */
+#define TIF_PATCH_PENDING	13	/* pending live patching update */
 #define TIF_NOTSC		16	/* TSC is not accessible in userland */
 #define TIF_IA32		17	/* IA32 compatibility process */
 #define TIF_FORK		18	/* ret_from_fork */
@@ -121,6 +122,7 @@ struct thread_info {
 #define _TIF_SECCOMP		(1 << TIF_SECCOMP)
 #define _TIF_USER_RETURN_NOTIFY	(1 << TIF_USER_RETURN_NOTIFY)
 #define _TIF_UPROBE		(1 << TIF_UPROBE)
+#define _TIF_PATCH_PENDING	(1 << TIF_PATCH_PENDING)
 #define _TIF_NOTSC		(1 << TIF_NOTSC)
 #define _TIF_IA32		(1 << TIF_IA32)
 #define _TIF_FORK		(1 << TIF_FORK)
Signature · 1 line
2.4.11
#68 lore raw

RFC PATCH v2 10/18 livepatch/powerpc: add TIF_PATCH_PENDING thread flag

In reply to Josh Poimboeuf

Add the TIF_PATCH_PENDING thread flag to enable the new livepatch per-task consistency model for powerpc. The bit getting set indicates the thread has a pending patch which needs to be applied when the thread exits the kernel.

The bit is included in the _TIF_USER_WORK_MASK macro so that do_notify_resume() and klp_patch_task() get called when the bit is set.

---
 arch/powerpc/include/asm/thread_info.h | 4 +++-
 arch/powerpc/kernel/signal.c           | 4 ++++
 2 files changed, 7 insertions(+), 1 deletion(-)
arch/powerpc/include/asm/thread_info.h +3 -1
index 8febc3f..df262ca 100644
@@ -88,6 +88,7 @@ static inline struct thread_info *current_thread_info(void)
 					   TIF_NEED_RESCHED */
 #define TIF_32BIT		4	/* 32 bit binary */
 #define TIF_RESTORE_TM		5	/* need to restore TM FP/VEC/VSX */
+#define TIF_PATCH_PENDING	6	/* pending live patching update */
 #define TIF_SYSCALL_AUDIT	7	/* syscall auditing active */
 #define TIF_SINGLESTEP		8	/* singlestepping active */
 #define TIF_NOHZ		9	/* in adaptive nohz mode */
@@ -111,6 +112,7 @@ static inline struct thread_info *current_thread_info(void)
 #define _TIF_POLLING_NRFLAG	(1<<TIF_POLLING_NRFLAG)
 #define _TIF_32BIT		(1<<TIF_32BIT)
 #define _TIF_RESTORE_TM		(1<<TIF_RESTORE_TM)
+#define _TIF_PATCH_PENDING	(1<<TIF_PATCH_PENDING)
 #define _TIF_SYSCALL_AUDIT	(1<<TIF_SYSCALL_AUDIT)
 #define _TIF_SINGLESTEP		(1<<TIF_SINGLESTEP)
 #define _TIF_SECCOMP		(1<<TIF_SECCOMP)
@@ -127,7 +129,7 @@ static inline struct thread_info *current_thread_info(void)
 
 #define _TIF_USER_WORK_MASK	(_TIF_SIGPENDING | _TIF_NEED_RESCHED | \
 				 _TIF_NOTIFY_RESUME | _TIF_UPROBE | \
-				 _TIF_RESTORE_TM)
+				 _TIF_RESTORE_TM | _TIF_PATCH_PENDING)
 #define _TIF_PERSYSCALL_MASK	(_TIF_RESTOREALL|_TIF_NOERROR)
 
 /* Bits in local_flags */
arch/powerpc/kernel/signal.c +4 -0
index cb64d6f..844497b 100644
@@ -14,6 +14,7 @@
 #include <linux/uprobes.h>
 #include <linux/key.h>
 #include <linux/context_tracking.h>
+#include <linux/livepatch.h>
 #include <asm/hw_breakpoint.h>
 #include <asm/uaccess.h>
 #include <asm/unistd.h>
@@ -159,6 +160,9 @@ void do_notify_resume(struct pt_regs *regs, unsigned long thread_info_flags)
 		tracehook_notify_resume(regs);
 	}
 
+	if (thread_info_flags & _TIF_PATCH_PENDING)
+		klp_patch_task(current);
+
 	user_enter();
 }
 
Signature · 1 line
2.4.11
#71 lore raw

RFC PATCH v2 11/18 livepatch/s390: reorganize TIF thread flag bits

In reply to Josh Poimboeuf

From: Jiri Slaby <[email protected]>

Group the TIF thread flag bits by their inclusion in the _TIF_WORK and _TIF_TRACE macros.

---
 arch/s390/include/asm/thread_info.h | 22 ++++++++++++++--------
 1 file changed, 14 insertions(+), 8 deletions(-)
arch/s390/include/asm/thread_info.h +14 -8
index 2fffc2c..8642c1d 100644
@@ -70,14 +70,12 @@ void arch_release_task_struct(struct task_struct *tsk);
 /*
  * thread information flags bit numbers
  */
+/* _TIF_WORK bits */
 #define TIF_NOTIFY_RESUME	0	/* callback before returning to user */
 #define TIF_SIGPENDING		1	/* signal pending */
 #define TIF_NEED_RESCHED	2	/* rescheduling necessary */
-#define TIF_SYSCALL_TRACE	3	/* syscall trace active */
-#define TIF_SYSCALL_AUDIT	4	/* syscall auditing active */
-#define TIF_SECCOMP		5	/* secure computing */
-#define TIF_SYSCALL_TRACEPOINT	6	/* syscall tracepoint instrumentation */
-#define TIF_UPROBE		7	/* breakpointed or single-stepping */
+#define TIF_UPROBE		3	/* breakpointed or single-stepping */
+
 #define TIF_31BIT		16	/* 32bit process */
 #define TIF_MEMDIE		17	/* is terminating due to OOM killer */
 #define TIF_RESTORE_SIGMASK	18	/* restore signal mask in do_signal() */
@@ -85,15 +83,23 @@ void arch_release_task_struct(struct task_struct *tsk);
 #define TIF_BLOCK_STEP		20	/* This task is block stepped */
 #define TIF_UPROBE_SINGLESTEP	21	/* This task is uprobe single stepped */
 
+/* _TIF_TRACE bits */
+#define TIF_SYSCALL_TRACE	24	/* syscall trace active */
+#define TIF_SYSCALL_AUDIT	25	/* syscall auditing active */
+#define TIF_SECCOMP		26	/* secure computing */
+#define TIF_SYSCALL_TRACEPOINT	27	/* syscall tracepoint instrumentation */
+
 #define _TIF_NOTIFY_RESUME	_BITUL(TIF_NOTIFY_RESUME)
 #define _TIF_SIGPENDING		_BITUL(TIF_SIGPENDING)
 #define _TIF_NEED_RESCHED	_BITUL(TIF_NEED_RESCHED)
+#define _TIF_UPROBE		_BITUL(TIF_UPROBE)
+
+#define _TIF_31BIT		_BITUL(TIF_31BIT)
+#define _TIF_SINGLE_STEP	_BITUL(TIF_SINGLE_STEP)
+
 #define _TIF_SYSCALL_TRACE	_BITUL(TIF_SYSCALL_TRACE)
 #define _TIF_SYSCALL_AUDIT	_BITUL(TIF_SYSCALL_AUDIT)
 #define _TIF_SECCOMP		_BITUL(TIF_SECCOMP)
 #define _TIF_SYSCALL_TRACEPOINT	_BITUL(TIF_SYSCALL_TRACEPOINT)
-#define _TIF_UPROBE		_BITUL(TIF_UPROBE)
-#define _TIF_31BIT		_BITUL(TIF_31BIT)
-#define _TIF_SINGLE_STEP	_BITUL(TIF_SINGLE_STEP)
 
 #endif /* _ASM_THREAD_INFO_H */
Signature · 1 line
2.4.11
#72 lore raw

RFC PATCH v2 12/18 livepatch/s390: add TIF_PATCH_PENDING thread flag

In reply to Josh Poimboeuf

From: Miroslav Benes <[email protected]>

Update a task's patch state when returning from a system call or user space interrupt, or after handling a signal.

This greatly increases the chances of a patch operation succeeding. If a task is I/O bound, it can be patched when returning from a system call. If a task is CPU bound, it can be patched when returning from an interrupt. If a task is sleeping on a to-be-patched function, the user can send SIGSTOP and SIGCONT to force it to switch.

Since there are two ways the syscall can be restarted on return from a signal handling process, it is important to clear the flag before do_signal() is called. Otherwise we could miss the migration if we used SIGSTOP/SIGCONT procedure or fake signal to migrate patching blocking tasks. If we place our hook to sysc_work label in entry before TIF_SIGPENDING is evaluated we kill two birds with one stone. The task is correctly migrated in all return paths from a syscall.

---
 arch/s390/include/asm/thread_info.h |  2 ++
 arch/s390/kernel/entry.S            | 31 ++++++++++++++++++++++++++++++-
 2 files changed, 32 insertions(+), 1 deletion(-)
arch/s390/include/asm/thread_info.h +2 -0
index 8642c1d..b69d538 100644
@@ -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_PATCH_PENDING	4	/* pending live patching update */
 
 #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_PATCH_PENDING	_BITUL(TIF_PATCH_PENDING)
 
 #define _TIF_31BIT		_BITUL(TIF_31BIT)
 #define _TIF_SINGLE_STEP	_BITUL(TIF_SINGLE_STEP)
arch/s390/kernel/entry.S +30 -1
index 2d47f9c..5db5959 100644
@@ -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_PATCH_PENDING)
 _TIF_TRACE	= (_TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT | _TIF_SECCOMP | \
 		   _TIF_SYSCALL_TRACEPOINT)
 _CIF_WORK	= (_CIF_MCCK_PENDING | _CIF_ASCE | _CIF_FPU)
@@ -329,6 +329,11 @@ ENTRY(system_call)
 #endif
 	TSTMSK	__PT_FLAGS(%r11),_PIF_PER_TRAP
 	jo	.Lsysc_singlestep
+#ifdef CONFIG_LIVEPATCH
+	TSTMSK	__TI_flags(%r12),_TIF_PATCH_PENDING
+	jo	.Lsysc_patch_pending	# handle live patching just before
+					# signals and possible syscall restart
+#endif
 	TSTMSK	__TI_flags(%r12),_TIF_SIGPENDING
 	jo	.Lsysc_sigpending
 	TSTMSK	__TI_flags(%r12),_TIF_NOTIFY_RESUME
@@ -404,6 +409,16 @@ ENTRY(system_call)
 #endif
 
 #
+# _TIF_PATCH_PENDING is set, call klp_patch_task
+#
+#ifdef CONFIG_LIVEPATCH
+.Lsysc_patch_pending:
+	lg	%r2,__TI_task(%r12)
+	larl	%r14,.Lsysc_return
+	jg	klp_patch_task
+#endif
+
+#
 # _PIF_PER_TRAP is set, call do_per_trap
 #
 .Lsysc_singlestep:
@@ -652,6 +667,10 @@ ENTRY(io_int_handler)
 	jo	.Lio_mcck_pending
 	TSTMSK	__TI_flags(%r12),_TIF_NEED_RESCHED
 	jo	.Lio_reschedule
+#ifdef CONFIG_LIVEPATCH
+	TSTMSK	__TI_flags(%r12),_TIF_PATCH_PENDING
+	jo	.Lio_patch_pending
+#endif
 	TSTMSK	__TI_flags(%r12),_TIF_SIGPENDING
 	jo	.Lio_sigpending
 	TSTMSK	__TI_flags(%r12),_TIF_NOTIFY_RESUME
@@ -698,6 +717,16 @@ ENTRY(io_int_handler)
 	j	.Lio_return
 
 #
+# _TIF_PATCH_PENDING is set, call klp_patch_task
+#
+#ifdef CONFIG_LIVEPATCH
+.Lio_patch_pending:
+	lg	%r2,__TI_task(%r12)
+	larl	%r14,.Lio_return
+	jg	klp_patch_task
+#endif
+
+#
 # _TIF_SIGPENDING or is set, call do_signal
 #
 .Lio_sigpending:
Signature · 1 line
2.4.11
#73 lore raw

RFC PATCH v2 13/18 livepatch: separate enabled and patched states

In reply to Josh Poimboeuf

Once we have a consistency model, patches and their objects will be enabled and disabled at different times. For example, when a patch is disabled, its loaded objects' funcs can remain registered with ftrace indefinitely until the unpatching operation is complete and they're no longer in use.

It's less confusing if we give them different names: patches can be enabled or disabled; objects (and their funcs) can be patched or unpatched:

- Enabled means that a patch is logically enabled (but not necessarily fully applied).

- Patched means that an object's funcs are registered with ftrace and added to the klp_ops func stack.

Also, since these states are binary, represent them with booleans instead of ints.

---
 include/linux/livepatch.h | 17 ++++-------
 kernel/livepatch/core.c   | 72 +++++++++++++++++++++++------------------------
 2 files changed, 42 insertions(+), 47 deletions(-)
include/linux/livepatch.h +6 -11
index a8c6c9c..9ba26c5 100644
@@ -28,11 +28,6 @@
 
 #include <asm/livepatch.h>
 
-enum klp_state {
-	KLP_DISABLED,
-	KLP_ENABLED
-};
-
 /**
  * struct klp_func - function structure for live patching
  * @old_name:	name of the function to be patched
@@ -41,8 +36,8 @@ enum klp_state {
  *		can be found (optional)
  * @old_addr:	the address of the function being patched
  * @kobj:	kobject for sysfs resources
- * @state:	tracks function-level patch application state
  * @stack_node:	list node for klp_ops func_stack list
+ * @patched:	the func has been added to the klp_ops list
  */
 struct klp_func {
 	/* external */
@@ -60,8 +55,8 @@ struct klp_func {
 	/* internal */
 	unsigned long old_addr;
 	struct kobject kobj;
-	enum klp_state state;
 	struct list_head stack_node;
+	bool patched;
 };
 
 /**
@@ -71,7 +66,7 @@ struct klp_func {
  * @kobj:	kobject for sysfs resources
  * @mod:	kernel module associated with the patched object
  * 		(NULL for vmlinux)
- * @state:	tracks object-level patch application state
+ * @patched:	the object's funcs have been added to the klp_ops list
  */
 struct klp_object {
 	/* external */
@@ -81,7 +76,7 @@ struct klp_object {
 	/* internal */
 	struct kobject kobj;
 	struct module *mod;
-	enum klp_state state;
+	bool patched;
 };
 
 /**
@@ -90,7 +85,7 @@ struct klp_object {
  * @objs:	object entries for kernel objects to be patched
  * @list:	list node for global list of registered patches
  * @kobj:	kobject for sysfs resources
- * @state:	tracks patch-level application state
+ * @enabled:	the patch is enabled (but operation may be incomplete)
  */
 struct klp_patch {
 	/* external */
@@ -100,7 +95,7 @@ struct klp_patch {
 	/* internal */
 	struct list_head list;
 	struct kobject kobj;
-	enum klp_state state;
+	bool enabled;
 };
 
 #define klp_for_each_object(patch, obj) \
kernel/livepatch/core.c +36 -36

233 lines not on this page · raw message

Signature · 1 line
2.4.11
#76 lore raw

RFC PATCH v2 14/18 livepatch: remove unnecessary object loaded check

In reply to Josh Poimboeuf

klp_patch_object()'s callers already ensure that the object is loaded, so its call to klp_is_object_loaded() is unnecessary.

This will also make it possible to move the patching code into a separate file.

---
 kernel/livepatch/core.c | 3 ---
 1 file changed, 3 deletions(-)
kernel/livepatch/core.c +0 -3
index 2b59230..2ad7892 100644
@@ -469,9 +469,6 @@ static int klp_patch_object(struct klp_object *obj)
 	if (WARN_ON(obj->patched))
 		return -EINVAL;
 
-	if (WARN_ON(!klp_is_object_loaded(obj)))
-		return -EINVAL;
-
 	klp_for_each_func(obj, func) {
 		ret = klp_patch_func(func);
 		if (ret) {
Signature · 1 line
2.4.11
#77 lore raw

RFC PATCH v2 15/18 livepatch: move patching functions into patch.c

In reply to Josh Poimboeuf

Move functions related to the actual patching of functions and objects into a new patch.c file.

---
 kernel/livepatch/Makefile |   2 +-
 kernel/livepatch/core.c   | 202 +------------------------------------------
 kernel/livepatch/patch.c  | 213 ++++++++++++++++++++++++++++++++++++++++++++++
 kernel/livepatch/patch.h  |  32 +++++++
 4 files changed, 247 insertions(+), 202 deletions(-)
 create mode 100644 kernel/livepatch/patch.c
 create mode 100644 kernel/livepatch/patch.h
kernel/livepatch/Makefile +1 -1
index e8780c0..e136dad 100644
@@ -1,3 +1,3 @@
 obj-$(CONFIG_LIVEPATCH) += livepatch.o
 
-livepatch-objs := core.o
+livepatch-objs := core.o patch.o
kernel/livepatch/core.c +1 -201

232 lines not on this page · raw message

kernel/livepatch/patch.c new file +213 -0

213 lines not on this page · raw message

kernel/livepatch/patch.h new file +32 -0
new file mode 100644
index 0000000..2d0cce0
@@ -0,0 +1,32 @@
+#ifndef _LIVEPATCH_PATCH_H
+#define _LIVEPATCH_PATCH_H
+
+#include <linux/livepatch.h>
+#include <linux/list.h>
+#include <linux/ftrace.h>
+
+/**
+ * struct klp_ops - structure for tracking registered ftrace ops structs
+ *
+ * A single ftrace_ops is shared between all enabled replacement functions
+ * (klp_func structs) which have the same old_addr.  This allows the switch
+ * between function versions to happen instantaneously by updating the klp_ops
+ * struct's func_stack list.  The winner is the klp_func at the top of the
+ * func_stack (front of the list).
+ *
+ * @node:	node for the global klp_ops list
+ * @func_stack:	list head for the stack of klp_func's (active func is on top)
+ * @fops:	registered ftrace ops struct
+ */
+struct klp_ops {
+	struct list_head node;
+	struct list_head func_stack;
+	struct ftrace_ops fops;
+};
+
+struct klp_ops *klp_find_ops(unsigned long old_addr);
+
+int klp_patch_object(struct klp_object *obj);
+void klp_unpatch_object(struct klp_object *obj);
+
+#endif /* _LIVEPATCH_PATCH_H */
Signature · 1 line
2.4.11
#79 lore raw

RFC PATCH v2 16/18 livepatch: store function sizes

In reply to Josh Poimboeuf

For the consistency model we'll need to know the sizes of the old and new functions to determine if they're on the stacks of any tasks.

---
 include/linux/livepatch.h |  3 +++
 kernel/livepatch/core.c   | 16 ++++++++++++++++
 2 files changed, 19 insertions(+)
include/linux/livepatch.h +3 -0
index 9ba26c5..c38c694 100644
@@ -37,6 +37,8 @@
  * @old_addr:	the address of the function being patched
  * @kobj:	kobject for sysfs resources
  * @stack_node:	list node for klp_ops func_stack list
+ * @old_size:	size of the old function
+ * @new_size:	size of the new function
  * @patched:	the func has been added to the klp_ops list
  */
 struct klp_func {
@@ -56,6 +58,7 @@ struct klp_func {
 	unsigned long old_addr;
 	struct kobject kobj;
 	struct list_head stack_node;
+	unsigned long old_size, new_size;
 	bool patched;
 };
 
kernel/livepatch/core.c +16 -0
index f28504d..aa3dbdf 100644
@@ -577,6 +577,22 @@ static int klp_init_object_loaded(struct klp_patch *patch,
 					     &func->old_addr);
 		if (ret)
 			return ret;
+
+		ret = kallsyms_lookup_size_offset(func->old_addr,
+						  &func->old_size, NULL);
+		if (!ret) {
+			pr_err("kallsyms size lookup failed for '%s'\n",
+			       func->old_name);
+			return -ENOENT;
+		}
+
+		ret = kallsyms_lookup_size_offset((unsigned long)func->new_func,
+						  &func->new_size, NULL);
+		if (!ret) {
+			pr_err("kallsyms size lookup failed for '%s' replacement\n",
+			       func->old_name);
+			return -ENOENT;
+		}
 	}
 
 	return 0;
Signature · 1 line
2.4.11
#80 lore raw

RFC PATCH v2 17/18 livepatch: change to a per-task consistency model

In reply to Josh Poimboeuf

Change livepatch to use a basic per-task consistency model. This is the foundation which will eventually enable us to patch those ~10% of security patches which change function or data semantics. This is the biggest remaining piece needed to make livepatch more generally useful.

This code stems from the design proposal made by Vojtech [1] in November 2014. It's a hybrid of kGraft and kpatch: it uses kGraft's per-task consistency and syscall barrier switching combined with kpatch's stack trace switching. There are also a number of fallback options which make it quite flexible.

Patches are applied on a per-task basis, when the task is deemed safe to switch over. When a patch is enabled, livepatch enters into a transition state where tasks are converging to the patched state. Usually this transition state can complete in a few seconds. The same sequence occurs when a patch is disabled, except the tasks converge from the patched state to the unpatched state.

An interrupt handler inherits the patched state of the task it interrupts. The same is true for forked tasks: the child inherits the patched state of the parent.

Livepatch uses several complementary approaches to determine when it's safe to patch tasks:

1. The first and most effective approach is stack checking of sleeping tasks. If no affected functions are on the stack of a given task, the task is patched. In most cases this will patch most or all of the tasks on the first try. Otherwise it'll keep trying periodically. This option is only available if the architecture has reliable stacks (CONFIG_RELIABLE_STACKTRACE and CONFIG_STACK_VALIDATION).

2. The second approach, if needed, is kernel exit switching. A task is switched when it returns to user space from a system call, a user space IRQ, or a signal. It's useful in the following cases:

a) Patching I/O-bound user tasks which are sleeping on an affected function. In this case you have to send SIGSTOP and SIGCONT to force it to exit the kernel and be patched.
b) Patching CPU-bound user tasks. If the task is highly CPU-bound then it will get patched the next time it gets interrupted by an IRQ.
c) Applying patches for architectures which don't yet have CONFIG_RELIABLE_STACKTRACE. In this case you'll have to signal most of the tasks on the system. However this isn't a complete solution, because there's currently no way to patch kthreads without CONFIG_RELIABLE_STACKTRACE.

Note: since idle "swapper" tasks don't ever exit the kernel, they instead have a kpatch_patch_task() call in the idle loop which allows them to patched before the CPU enters the idle state.

3. A third approach (not yet implemented) is planned for the case where a kthread is sleeping on an affected function. In that case we could kick the kthread with a signal and then try to patch the task from the to-be-patched function's livepatch ftrace handler when it re-enters the function. This will require CONFIG_RELIABLE_STACKTRACE.

All the above approaches may be skipped by setting the 'immediate' flag in the 'klp_patch' struct, which will patch all tasks immediately. This can be useful if the patch doesn't change any function or data semantics. Note that, even with this flag set, it's possible that some tasks may still be running with an old version of the function, until that function returns.

There's also an 'immediate' flag in the 'klp_func' struct which allows you to specify that certain functions in the patch can be applied without per-task consistency. This might be useful if you want to patch a common function like schedule(), and the function change doesn't need consistency but the rest of the patch does.

For architectures which don't have CONFIG_RELIABLE_STACKTRACE, there are two options:

a) the user can set the patch->immediate flag which causes all tasks to be patched immediately. This option should be used with care, only when the patch doesn't change any function or data semantics; or

b) use the kernel exit switching approach (this is the default). Note the patching will never complete because there's no currently no way to patch kthreads without CONFIG_RELIABLE_STACKTRACE.

The /sys/kernel/livepatch/<patch>/transition file shows whether a patch is in transition. Only a single patch (the topmost patch on the stack) can be in transition at a given time. A patch can remain in transition indefinitely, if any of the tasks are stuck in the initial patch state.

A transition can be reversed and effectively canceled by writing the opposite value to the /sys/kernel/livepatch/<patch>/enabled file while the transition is in progress. Then all the tasks will attempt to converge back to the original patch state.

[1] https://lkml.kernel.org/r/[email protected]

---
 Documentation/ABI/testing/sysfs-kernel-livepatch |   8 +
 Documentation/livepatch/livepatch.txt            | 132 ++++++-
 include/linux/init_task.h                        |   9 +
 include/linux/livepatch.h                        |  34 +-
 include/linux/sched.h                            |   3 +
 kernel/fork.c                                    |   3 +
 kernel/livepatch/Makefile                        |   2 +-
 kernel/livepatch/core.c                          |  98 +++--
 kernel/livepatch/patch.c                         |  43 +-
 kernel/livepatch/patch.h                         |   1 +
 kernel/livepatch/transition.c                    | 474 +++++++++++++++++++++++
 kernel/livepatch/transition.h                    |  14 +
 kernel/sched/idle.c                              |   4 +
 13 files changed, 781 insertions(+), 44 deletions(-)
 create mode 100644 kernel/livepatch/transition.c
 create mode 100644 kernel/livepatch/transition.h
Documentation/ABI/testing/sysfs-kernel-livepatch +8 -0
index da87f43..24ca6df 100644
@@ -25,6 +25,14 @@ Description:
 		code is currently applied.  Writing 0 will disable the patch
 		while writing 1 will re-enable the patch.
 
+What:		/sys/kernel/livepatch/<patch>/transition
+Date:		May 2016
+KernelVersion:	4.7.0
+Contact:	[email protected]
+Description:
+		An attribute which indicates whether the patch is currently in
+		transition.
+
 What:		/sys/kernel/livepatch/<patch>/<object>
 Date:		Nov 2014
 KernelVersion:	3.19.0
Documentation/livepatch/livepatch.txt +114 -18

156 lines not on this page · raw message

include/linux/init_task.h +9 -0
index f2cb8d4..12199ef 100644
@@ -14,6 +14,7 @@
 #include <linux/rbtree.h>
 #include <net/net_namespace.h>
 #include <linux/sched/rt.h>
+#include <linux/livepatch.h>
 
 #ifdef CONFIG_SMP
 # define INIT_PUSHABLE_TASKS(tsk)					\
@@ -183,6 +184,13 @@ extern struct task_group root_task_group;
 # define INIT_KASAN(tsk)
 #endif
 
+#ifdef CONFIG_LIVEPATCH
+#define INIT_LIVEPATCH(tsk)						\
+	.patch_state = KLP_UNDEFINED,
+#else
+#define INIT_LIVEPATCH(tsk)
+#endif
+
 /*
  *  INIT_TASK is used to set up the first task table, touch at
  * your own risk!. Base=0, limit=0x1fffff (=2MB)
@@ -260,6 +268,7 @@ extern struct task_group root_task_group;
 	INIT_VTIME(tsk)							\
 	INIT_NUMA_BALANCING(tsk)					\
 	INIT_KASAN(tsk)							\
+	INIT_LIVEPATCH(tsk)						\
 }
 
 
include/linux/livepatch.h +33 -1
index c38c694..6ec50ff 100644
@@ -28,18 +28,40 @@
 
 #include <asm/livepatch.h>
 
+/* task patch states */
+#define KLP_UNDEFINED	-1
+#define KLP_UNPATCHED	0
+#define KLP_PATCHED	1
+
 /**
  * struct klp_func - function structure for live patching
  * @old_name:	name of the function to be patched
  * @new_func:	pointer to the patched function code
  * @old_sympos: a hint indicating which symbol position the old function
  *		can be found (optional)
+ * @immediate:  patch the func immediately, bypassing backtrace safety checks
  * @old_addr:	the address of the function being patched
  * @kobj:	kobject for sysfs resources
  * @stack_node:	list node for klp_ops func_stack list
  * @old_size:	size of the old function
  * @new_size:	size of the new function
  * @patched:	the func has been added to the klp_ops list
+ * @transition:	the func is currently being applied or reverted
+ *
+ * The patched and transition variables define the func's patching state.  When
+ * patching, a func is always in one of the following states:
+ *
+ *   patched=0 transition=0: unpatched
+ *   patched=0 transition=1: unpatched, temporary starting state
+ *   patched=1 transition=1: patched, may be visible to some tasks
+ *   patched=1 transition=0: patched, visible to all tasks
+ *
+ * And when unpatching, it goes in the reverse order:
+ *
+ *   patched=1 transition=0: patched, visible to all tasks
+ *   patched=1 transition=1: patched, may be visible to some tasks
+ *   patched=0 transition=1: unpatched, temporary ending state
+ *   patched=0 transition=0: unpatched
  */
 struct klp_func {
 	/* external */
@@ -53,6 +75,7 @@ struct klp_func {
 	 * in kallsyms for the given object is used.
 	 */
 	unsigned long old_sympos;
+	bool immediate;
 
 	/* internal */
 	unsigned long old_addr;
@@ -60,6 +83,7 @@ struct klp_func {
 	struct list_head stack_node;
 	unsigned long old_size, new_size;
 	bool patched;
+	bool transition;
 };
 
 /**
@@ -86,6 +110,7 @@ struct klp_object {
  * struct klp_patch - patch structure for live patching
  * @mod:	reference to the live patch module
  * @objs:	object entries for kernel objects to be patched
+ * @immediate:  patch all funcs immediately, bypassing safety mechanisms
  * @list:	list node for global list of registered patches
  * @kobj:	kobject for sysfs resources
  * @enabled:	the patch is enabled (but operation may be incomplete)
@@ -94,6 +119,7 @@ struct klp_patch {
 	/* external */
 	struct module *mod;
 	struct klp_object *objs;
+	bool immediate;
 
 	/* internal */
 	struct list_head list;
@@ -116,15 +142,21 @@ int klp_disable_patch(struct klp_patch *);
 int klp_module_coming(struct module *mod);
 void klp_module_going(struct module *mod);
 
-static inline bool klp_patch_pending(struct task_struct *task) { return false; }
+void klp_copy_process(struct task_struct *child);
 void klp_patch_task(struct task_struct *task);
 
+static inline bool klp_patch_pending(struct task_struct *task)
+{
+	return test_tsk_thread_flag(task, TIF_PATCH_PENDING);
+}
+
 #else /* !CONFIG_LIVEPATCH */
 
 static inline int klp_module_coming(struct module *mod) { return 0; }
 static inline void klp_module_going(struct module *mod) {}
 static inline bool klp_patch_pending(struct task_struct *task) { return false; }
 static inline void klp_patch_task(struct task_struct *task) {}
+static inline void klp_copy_process(struct task_struct *child) {}
 
 #endif /* CONFIG_LIVEPATCH */
 
include/linux/sched.h +3 -0
index fb364a0..7fc8b49 100644
@@ -1860,6 +1860,9 @@ struct task_struct {
 #ifdef CONFIG_MMU
 	struct task_struct *oom_reaper_list;
 #endif
+#ifdef CONFIG_LIVEPATCH
+	int patch_state;
+#endif
 /* CPU-specific state of this task */
 	struct thread_struct thread;
 /*
kernel/fork.c +3 -0
index d2fe04a..a12e3b0 100644
@@ -76,6 +76,7 @@
 #include <linux/compiler.h>
 #include <linux/sysctl.h>
 #include <linux/kcov.h>
+#include <linux/livepatch.h>
 
 #include <asm/pgtable.h>
 #include <asm/pgalloc.h>
@@ -1586,6 +1587,8 @@ static struct task_struct *copy_process(unsigned long clone_flags,
 		p->parent_exec_id = current->self_exec_id;
 	}
 
+	klp_copy_process(p);
+
 	spin_lock(&current->sighand->siglock);
 
 	/*
kernel/livepatch/Makefile +1 -1
index e136dad..2b8bdb1 100644
@@ -1,3 +1,3 @@
 obj-$(CONFIG_LIVEPATCH) += livepatch.o
 
-livepatch-objs := core.o patch.o
+livepatch-objs := core.o patch.o transition.o
kernel/livepatch/core.c +75 -23

186 lines not on this page · raw message

kernel/livepatch/patch.c +42 -1

62 lines not on this page · raw message

kernel/livepatch/patch.h +1 -0
index 2d0cce0..0db2271 100644
@@ -28,5 +28,6 @@ struct klp_ops *klp_find_ops(unsigned long old_addr);
 
 int klp_patch_object(struct klp_object *obj);
 void klp_unpatch_object(struct klp_object *obj);
+void klp_unpatch_objects(struct klp_patch *patch);
 
 #endif /* _LIVEPATCH_PATCH_H */
kernel/livepatch/transition.c new file +474 -0

474 lines not on this page · raw message

kernel/livepatch/transition.h new file +14 -0
new file mode 100644
index 0000000..5191b96
@@ -0,0 +1,14 @@
+#ifndef _LIVEPATCH_TRANSITION_H
+#define _LIVEPATCH_TRANSITION_H
+
+#include <linux/livepatch.h>
+
+extern struct klp_patch *klp_transition_patch;
+
+void klp_init_transition(struct klp_patch *patch, int state);
+void klp_start_transition(void);
+void klp_reverse_transition(void);
+bool klp_try_complete_transition(void);
+void klp_complete_transition(void);
+
+#endif /* _LIVEPATCH_TRANSITION_H */
kernel/sched/idle.c +4 -0
index bd12c6c..60d633f 100644
@@ -9,6 +9,7 @@
 #include <linux/mm.h>
 #include <linux/stackprotector.h>
 #include <linux/suspend.h>
+#include <linux/livepatch.h>
 
 #include <asm/tlb.h>
 
@@ -266,6 +267,9 @@ static void cpu_idle_loop(void)
 
 		sched_ttwu_pending();
 		schedule_preempt_disabled();
+
+		if (unlikely(klp_patch_pending(current)))
+			klp_patch_task(current);
 	}
 }
 
Signature · 1 line
2.4.11
#117 lore raw

RFC PATCH v2 18/18 livepatch: add /proc/<pid>/patch_state

In reply to Josh Poimboeuf

Expose the per-task patch state value so users can determine which tasks are holding up completion of a patching operation.

---
 Documentation/filesystems/proc.txt | 18 ++++++++++++++++++
 fs/proc/base.c                     | 15 +++++++++++++++
 2 files changed, 33 insertions(+)
Documentation/filesystems/proc.txt +18 -0
index e8d0075..0b09495 100644
@@ -44,6 +44,7 @@ Table of Contents
   3.8   /proc/<pid>/fdinfo/<fd> - Information about opened file
   3.9   /proc/<pid>/map_files - Information about memory mapped files
   3.10  /proc/<pid>/timerslack_ns - Task timerslack value
+  3.11	/proc/<pid>/patch_state - Livepatch patch operation state
 
   4	Configuring procfs
   4.1	Mount options
@@ -1880,6 +1881,23 @@ Valid values are from 0 - ULLONG_MAX
 An application setting the value must have PTRACE_MODE_ATTACH_FSCREDS level
 permissions on the task specified to change its timerslack_ns value.
 
+3.11	/proc/<pid>/patch_state - Livepatch patch operation state
+-----------------------------------------------------------------
+When CONFIG_LIVEPATCH is enabled, this file displays the value of the
+patch state for the task.
+
+A value of '-1' indicates that no patch is in transition.
+
+A value of '0' indicates that a patch is in transition and the task is
+unpatched.  If the patch is being enabled, then the task hasn't been
+patched yet.  If the patch is being disabled, then the task has already
+been unpatched.
+
+A value of '1' indicates that a patch is in transition and the task is
+patched.  If the patch is being enabled, then the task has already been
+patched.  If the patch is being disabled, then the task hasn't been
+unpatched yet.
+
 
 ------------------------------------------------------------------------------
 Configuring procfs
fs/proc/base.c +15 -0
index 2868cdf..c485450 100644
@@ -2801,6 +2801,15 @@ static int proc_pid_personality(struct seq_file *m, struct pid_namespace *ns,
 	return err;
 }
 
+#ifdef CONFIG_LIVEPATCH
+static int proc_pid_patch_state(struct seq_file *m, struct pid_namespace *ns,
+				struct pid *pid, struct task_struct *task)
+{
+	seq_printf(m, "%d\n", task->patch_state);
+	return 0;
+}
+#endif /* CONFIG_LIVEPATCH */
+
 /*
  * Thread groups
  */
@@ -2900,6 +2909,9 @@ static const struct pid_entry tgid_base_stuff[] = {
 	REG("timers",	  S_IRUGO, proc_timers_operations),
 #endif
 	REG("timerslack_ns", S_IRUGO|S_IWUGO, proc_pid_set_timerslack_ns_operations),
+#ifdef CONFIG_LIVEPATCH
+	ONE("patch_state",  S_IRUSR, proc_pid_patch_state),
+#endif
 };
 
 static int proc_tgid_base_readdir(struct file *file, struct dir_context *ctx)
@@ -3282,6 +3294,9 @@ static const struct pid_entry tid_base_stuff[] = {
 	REG("projid_map", S_IRUGO|S_IWUSR, proc_projid_map_operations),
 	REG("setgroups",  S_IRUGO|S_IWUSR, proc_setgroups_operations),
 #endif
+#ifdef CONFIG_LIVEPATCH
+	ONE("patch_state",  S_IRUSR, proc_pid_patch_state),
+#endif
 };
 
 static int proc_tid_base_readdir(struct file *file, struct dir_context *ctx)
Signature · 1 line
2.4.11
#61 lore raw

Re: [RFC PATCH v2 06/18] x86: dump_trace() error handling

In reply to Josh Poimboeuf
Quoted 3 lines · Josh Poimboeuf

In preparation for being able to determine whether a given stack trace is reliable, allow the stacktrace_ops functions to propagate errors to dump_trace().

Hi, Josh.

Have you considered to make walk_stack function as non-return function, since there is no obvious error during detecting the frame points?

Thanks
Minfei

#62 lore raw

Re: [RFC PATCH v2 06/18] x86: dump_trace() error handling

Quoted 9 lines · Minfei Huang
Quoted 3 lines · Josh Poimboeuf

In preparation for being able to determine whether a given stack trace is reliable, allow the stacktrace_ops functions to propagate errors to dump_trace().

Hi, Josh.

Have you considered to make walk_stack function as non-return function, since there is no obvious error during detecting the frame points?

If you look at the next patch 07/18, there are several cases where walk_stack (print_context_stack_reliable) returns an error.

For example, if a function gets preempted before it gets a chance to save the frame pointer, the function's caller would get skipped on the stack trace. So for preempted tasks, we always have to consider their stacks unreliable.

Signature · 1 line
Josh
#13 lore raw

Re: [RFC PATCH v2 05/18] sched: add task flag for preempt IRQ tracking

In reply to Josh Poimboeuf
Quoted 6 lines · Josh Poimboeuf

A preempted function might not have had a chance to save the frame pointer to the stack yet, which can result in its caller getting skipped on a stack trace.

Add a flag to indicate when the task has been preempted so that stack dump code can determine whether the stack trace is reliable.

I think I like this, but how do you handle the rather similar case in which a task goes to sleep because it's waiting on IO that happened in response to get_user, put_user, copy_from_user, etc?

--Andy

#66 lore raw

Re: [RFC PATCH v2 09/18] livepatch/x86: add TIF_PATCH_PENDING thread flag

In reply to Josh Poimboeuf
Quoted 6 lines · Josh Poimboeuf

Add the TIF_PATCH_PENDING thread flag to enable the new livepatch per-task consistency model for x86_64. The bit getting set indicates the thread has a pending patch which needs to be applied when the thread exits the kernel.

The bit is placed in the least-significant word of the thread_info flags

NAK to that part.

The least-significant word thing is a huge hack that has gotten out of control. Please add the thing explicitly to all relevant masks.

--Andy

#4 lore raw

Re: [RFC PATCH v2 03/18] x86/asm/head: standardize the bottom of the stack for idle tasks

In reply to Josh Poimboeuf
Quoted 29 lines · Josh Poimboeuf

Thanks to all the recent x86 entry code refactoring, most tasks' kernel stacks start at the same offset right above their saved pt_regs, regardless of which syscall was used to enter the kernel. That creates a nice convention which makes it straightforward to identify the "bottom" of the stack, which can be useful for stack walking code which needs to verify the stack is sane.

However there are still a few types of tasks which don't yet follow that convention:

1) CPU idle tasks, aka the "swapper" tasks

2) freshly forked TIF_FORK tasks which don't have a stack at all

Make the idle tasks conform to the new stack bottom convention by starting their stack at a sizeof(pt_regs) offset from the end of the stack page.

---
 arch/x86/kernel/head_64.S | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)
arch/x86/kernel/head_64.S +0 -0
index 6dbd2c0..0b12311 100644
@@ -296,8 +296,9 @@ ENTRY(start_cpu)
         *      REX.W + FF /5 JMP m16:64 Jump far, absolute indirect,

arch/x86/kernel/head_64.S ENTRY(start_cpu) · line 304

Quoted 10 lines
arch/x86/kernel/head_64.S +3 -2
         *              address given in m16:64.
         */
-       movq    initial_code(%rip),%rax
-       pushq   $0              # fake return address to stop unwinder
+       call    1f              # put return address on stack for unwinder
+1:     xorq    %rbp, %rbp      # clear frame pointer
+       movq    initial_code(%rip), %rax
        pushq   $__KERNEL_CS    # set correct cs
        pushq   %rax            # target address in negative space
        lretq

This chunk looks like it should be a separate patch.

--
Brian Gerst

maintainer
#6 lore raw

Re: [RFC PATCH v2 03/18] x86/asm/head: standardize the bottom of the stack for idle tasks

In reply to Josh Poimboeuf
Quoted 40 lines · Josh Poimboeuf

Thanks to all the recent x86 entry code refactoring, most tasks' kernel stacks start at the same offset right above their saved pt_regs, regardless of which syscall was used to enter the kernel. That creates a nice convention which makes it straightforward to identify the "bottom" of the stack, which can be useful for stack walking code which needs to verify the stack is sane.

However there are still a few types of tasks which don't yet follow that convention:

1) CPU idle tasks, aka the "swapper" tasks

2) freshly forked TIF_FORK tasks which don't have a stack at all

Make the idle tasks conform to the new stack bottom convention by starting their stack at a sizeof(pt_regs) offset from the end of the stack page.

---
 arch/x86/kernel/head_64.S | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)
arch/x86/kernel/head_64.S +3 -2
index 6dbd2c0..0b12311 100644
@@ -296,8 +296,9 @@ ENTRY(start_cpu)
         *      REX.W + FF /5 JMP m16:64 Jump far, absolute indirect,
         *              address given in m16:64.
         */
-       movq    initial_code(%rip),%rax
-       pushq   $0              # fake return address to stop unwinder
+       call    1f              # put return address on stack for unwinder
+1:     xorq    %rbp, %rbp      # clear frame pointer
+       movq    initial_code(%rip), %rax
        pushq   $__KERNEL_CS    # set correct cs
        pushq   %rax            # target address in negative space
        lretq

arch/x86/kernel/head_64.S ENDPROC(start_cpu0) · line 329

Quoted 5 lines
arch/x86/kernel/head_64.S +1 -1
@@ -325,7 +326,7 @@ ENDPROC(start_cpu0)
        GLOBAL(initial_gs)
        .quad   INIT_PER_CPU_VAR(irq_stack_union)
        GLOBAL(initial_stack)
-       .quad  init_thread_union+THREAD_SIZE-8
+       .quad  init_thread_union + THREAD_SIZE - SIZEOF_PTREGS

As long as you're doing this, could you also set orig_ax to -1? I remember running into some oddities resulting from orig_ax containing garbage at some point.

--Andy

#14 lore raw

Re: [RFC PATCH v2 05/18] sched: add task flag for preempt IRQ tracking

In reply to Andy Lutomirski
Quoted 11 lines · Andy Lutomirski
Quoted 6 lines · Josh Poimboeuf

A preempted function might not have had a chance to save the frame pointer to the stack yet, which can result in its caller getting skipped on a stack trace.

Add a flag to indicate when the task has been preempted so that stack dump code can determine whether the stack trace is reliable.

I think I like this, but how do you handle the rather similar case in which a task goes to sleep because it's waiting on IO that happened in response to get_user, put_user, copy_from_user, etc?

Hm, good question. I was thinking that page faults had a dedicated stack, but now looking at the entry and traps code, that doesn't seem to be the case.

Anyway I think it shouldn't be a problem if we make sure that any kernel function which might trigger a valid page fault (e.g., copy_user_generic_string) do the proper frame pointer setup first. Then the stack should still be reliable.

In fact I might be able to teach objtool to enforce that: any function which uses an exception table should create a stack frame.

Or alternatively, maybe set some kind of flag for page faults, similar to what I did with this patch.

Signature · 1 line
Josh
#67 lore raw

Re: [RFC PATCH v2 09/18] livepatch/x86: add TIF_PATCH_PENDING thread flag

In reply to Andy Lutomirski
Quoted 6 lines · Andy Lutomirski
Quoted 5 lines · Josh Poimboeuf

Add the TIF_PATCH_PENDING thread flag to enable the new livepatch per-task consistency model for x86_64. The bit getting set indicates the thread has a pending patch which needs to be applied when the thread exits the kernel.

Quoted 6 lines
Quoted 1 line

The bit is placed in the least-significant word of the thread_info flags

NAK to that part.

The least-significant word thing is a huge hack that has gotten out of control. Please add the thing explicitly to all relevant masks.

Yeah, it is quite dangerous. I'll make it explicit, and make all the other _TIF_ALLWORK_MASK flags explicit while I'm at it.

Signature · 1 line
Josh
#15 lore raw

Re: [RFC PATCH v2 05/18] sched: add task flag for preempt IRQ tracking

In reply to Josh Poimboeuf
Quoted 22 lines · Josh Poimboeuf
Quoted 11 lines · Andy Lutomirski
Quoted 6 lines · Josh Poimboeuf

A preempted function might not have had a chance to save the frame pointer to the stack yet, which can result in its caller getting skipped on a stack trace.

Add a flag to indicate when the task has been preempted so that stack dump code can determine whether the stack trace is reliable.

I think I like this, but how do you handle the rather similar case in which a task goes to sleep because it's waiting on IO that happened in response to get_user, put_user, copy_from_user, etc?

Hm, good question. I was thinking that page faults had a dedicated stack, but now looking at the entry and traps code, that doesn't seem to be the case.

Anyway I think it shouldn't be a problem if we make sure that any kernel function which might trigger a valid page fault (e.g., copy_user_generic_string) do the proper frame pointer setup first. Then the stack should still be reliable.

Quoted 6 lines

In fact I might be able to teach objtool to enforce that: any function which uses an exception table should create a stack frame.

Or alternatively, maybe set some kind of flag for page faults, similar to what I did with this patch.

How about doing it the other way around: teach the unwinder to detect when it hits a non-outermost entry (i.e. it lands in idtentry, etc) and use some reasonable heuristic as to whether it's okay to keep unwinding. You should be able to handle preemption like that, too -- the unwind process will end up in an IRQ frame.

--Andy

#16 lore raw

Re: [RFC PATCH v2 05/18] sched: add task flag for preempt IRQ tracking

Quoted 30 lines · Andy Lutomirski
Quoted 22 lines · Josh Poimboeuf
Quoted 11 lines · Andy Lutomirski
Quoted 6 lines · Josh Poimboeuf

A preempted function might not have had a chance to save the frame pointer to the stack yet, which can result in its caller getting skipped on a stack trace.

Add a flag to indicate when the task has been preempted so that stack dump code can determine whether the stack trace is reliable.

I think I like this, but how do you handle the rather similar case in which a task goes to sleep because it's waiting on IO that happened in response to get_user, put_user, copy_from_user, etc?

Hm, good question. I was thinking that page faults had a dedicated stack, but now looking at the entry and traps code, that doesn't seem to be the case.

Anyway I think it shouldn't be a problem if we make sure that any kernel function which might trigger a valid page fault (e.g., copy_user_generic_string) do the proper frame pointer setup first. Then the stack should still be reliable.

Quoted 6 lines

In fact I might be able to teach objtool to enforce that: any function which uses an exception table should create a stack frame.

Or alternatively, maybe set some kind of flag for page faults, similar to what I did with this patch.

Quoted 5 lines

How about doing it the other way around: teach the unwinder to detect when it hits a non-outermost entry (i.e. it lands in idtentry, etc) and use some reasonable heuristic as to whether it's okay to keep unwinding. You should be able to handle preemption like that, too -- the unwind process will end up in an IRQ frame.

How exactly would the unwinder detect if a text address is in an idtentry? Maybe put all the idt entries in a special ELF section?

Signature · 1 line
Josh
#5 lore raw

Re: [RFC PATCH v2 03/18] x86/asm/head: standardize the bottom of the stack for idle tasks

In reply to Brian Gerst
Quoted 34 lines · Brian Gerst
Quoted 33 lines · Josh Poimboeuf

Thanks to all the recent x86 entry code refactoring, most tasks' kernel stacks start at the same offset right above their saved pt_regs, regardless of which syscall was used to enter the kernel. That creates a nice convention which makes it straightforward to identify the "bottom" of the stack, which can be useful for stack walking code which needs to verify the stack is sane.

However there are still a few types of tasks which don't yet follow that convention:

1) CPU idle tasks, aka the "swapper" tasks

2) freshly forked TIF_FORK tasks which don't have a stack at all

Make the idle tasks conform to the new stack bottom convention by starting their stack at a sizeof(pt_regs) offset from the end of the stack page.

---
 arch/x86/kernel/head_64.S | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)
arch/x86/kernel/head_64.S +0 -2
index 6dbd2c0..0b12311 100644
@@ -296,8 +296,9 @@ ENTRY(start_cpu)
         *      REX.W + FF /5 JMP m16:64 Jump far, absolute indirect,
         *              address given in m16:64.
         */
-       movq    initial_code(%rip),%rax
-       pushq   $0              # fake return address to stop unwinder
Quoted 8 lines

arch/x86/kernel/head_64.S ENTRY(start_cpu) · line 304

Quoted 6 lines
arch/x86/kernel/head_64.S +3 -0
+       call    1f              # put return address on stack for unwinder
+1:     xorq    %rbp, %rbp      # clear frame pointer
+       movq    initial_code(%rip), %rax
        pushq   $__KERNEL_CS    # set correct cs
        pushq   %rax            # target address in negative space
        lretq

This chunk looks like it should be a separate patch.

Agreed, thanks.

Signature · 1 line
Josh
#17 lore raw

Re: [RFC PATCH v2 05/18] sched: add task flag for preempt IRQ tracking

In reply to Josh Poimboeuf
Quoted 31 lines · Josh Poimboeuf
Quoted 30 lines · Andy Lutomirski
Quoted 28 lines · Josh Poimboeuf
Quoted 11 lines · Andy Lutomirski
Quoted 6 lines · Josh Poimboeuf

A preempted function might not have had a chance to save the frame pointer to the stack yet, which can result in its caller getting skipped on a stack trace.

Add a flag to indicate when the task has been preempted so that stack dump code can determine whether the stack trace is reliable.

I think I like this, but how do you handle the rather similar case in which a task goes to sleep because it's waiting on IO that happened in response to get_user, put_user, copy_from_user, etc?

Hm, good question. I was thinking that page faults had a dedicated stack, but now looking at the entry and traps code, that doesn't seem to be the case.

Anyway I think it shouldn't be a problem if we make sure that any kernel function which might trigger a valid page fault (e.g., copy_user_generic_string) do the proper frame pointer setup first. Then the stack should still be reliable.

In fact I might be able to teach objtool to enforce that: any function which uses an exception table should create a stack frame.

Or alternatively, maybe set some kind of flag for page faults, similar to what I did with this patch.

Quoted 9 lines
Quoted 5 lines

How about doing it the other way around: teach the unwinder to detect when it hits a non-outermost entry (i.e. it lands in idtentry, etc) and use some reasonable heuristic as to whether it's okay to keep unwinding. You should be able to handle preemption like that, too -- the unwind process will end up in an IRQ frame.

How exactly would the unwinder detect if a text address is in an idtentry? Maybe put all the idt entries in a special ELF section?

Hmm.

What actually happens when you unwind all the way into the entry code?
Don't you end up in something that isn't in an ELF function? Can you
detect that? Ideally, the unwinder could actually detect that it's hit a pt_regs struct and report that. If used for stack dumps, it could display some indication of this and then continue its unwinding by decoding the pt_regs. If used for patching, it could take some other appropriate action.

I would have no objection to annotating all the pt_regs-style entry code, whether by putting it in a separate section or by making a table of addresses.

There are a couple of nasty cases if NMI or MCE is involved but, as of 4.6, outside of NMI, MCE, and vmalloc faults (ugh!), there should always be a complete pt_regs on the stack before interrupts get enabled for each entry. Of course, finding the thing may be nontrivial in case other things were pushed. I suppose we could try to rejigger the code so that rbp points to pt_regs or similar.

--Andy

#7 lore raw

Re: [RFC PATCH v2 03/18] x86/asm/head: standardize the bottom of the stack for idle tasks

In reply to Andy Lutomirski
Quoted 42 lines · Andy Lutomirski
Quoted 41 lines · Josh Poimboeuf

Thanks to all the recent x86 entry code refactoring, most tasks' kernel stacks start at the same offset right above their saved pt_regs, regardless of which syscall was used to enter the kernel. That creates a nice convention which makes it straightforward to identify the "bottom" of the stack, which can be useful for stack walking code which needs to verify the stack is sane.

However there are still a few types of tasks which don't yet follow that convention:

1) CPU idle tasks, aka the "swapper" tasks

2) freshly forked TIF_FORK tasks which don't have a stack at all

Make the idle tasks conform to the new stack bottom convention by starting their stack at a sizeof(pt_regs) offset from the end of the stack page.

---
 arch/x86/kernel/head_64.S | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)
arch/x86/kernel/head_64.S +3 -2
index 6dbd2c0..0b12311 100644
@@ -296,8 +296,9 @@ ENTRY(start_cpu)
         *      REX.W + FF /5 JMP m16:64 Jump far, absolute indirect,
         *              address given in m16:64.
         */
-       movq    initial_code(%rip),%rax
-       pushq   $0              # fake return address to stop unwinder
+       call    1f              # put return address on stack for unwinder
+1:     xorq    %rbp, %rbp      # clear frame pointer
+       movq    initial_code(%rip), %rax
        pushq   $__KERNEL_CS    # set correct cs
        pushq   %rax            # target address in negative space
        lretq
@@ -325,7 +326,7 @@ ENDPROC(start_cpu0)
        GLOBAL(initial_gs)
Quoted 8 lines

arch/x86/kernel/head_64.S ENDPROC(start_cpu0) · line 329

Quoted 4 lines
arch/x86/kernel/head_64.S +1 -1
        .quad   INIT_PER_CPU_VAR(irq_stack_union)
        GLOBAL(initial_stack)
-       .quad  init_thread_union+THREAD_SIZE-8
+       .quad  init_thread_union + THREAD_SIZE - SIZEOF_PTREGS

As long as you're doing this, could you also set orig_ax to -1? I remember running into some oddities resulting from orig_ax containing garbage at some point.

I assume you mean to initialize the orig_rax value in the pt_regs at the bottom of the stack of the idle task?

How could that cause a problem? Since the idle task never returns from a system call, I'd assume that memory never gets accessed?

Signature · 1 line
Josh
#18 lore raw

Re: [RFC PATCH v2 05/18] sched: add task flag for preempt IRQ tracking

In reply to Andy Lutomirski
Quoted 42 lines · Andy Lutomirski
Quoted 31 lines · Josh Poimboeuf
Quoted 30 lines · Andy Lutomirski
Quoted 28 lines · Josh Poimboeuf
Quoted 11 lines · Andy Lutomirski
Quoted 6 lines · Josh Poimboeuf

A preempted function might not have had a chance to save the frame pointer to the stack yet, which can result in its caller getting skipped on a stack trace.

Add a flag to indicate when the task has been preempted so that stack dump code can determine whether the stack trace is reliable.

I think I like this, but how do you handle the rather similar case in which a task goes to sleep because it's waiting on IO that happened in response to get_user, put_user, copy_from_user, etc?

Hm, good question. I was thinking that page faults had a dedicated stack, but now looking at the entry and traps code, that doesn't seem to be the case.

Anyway I think it shouldn't be a problem if we make sure that any kernel function which might trigger a valid page fault (e.g., copy_user_generic_string) do the proper frame pointer setup first. Then the stack should still be reliable.

In fact I might be able to teach objtool to enforce that: any function which uses an exception table should create a stack frame.

Or alternatively, maybe set some kind of flag for page faults, similar to what I did with this patch.

Quoted 9 lines
Quoted 5 lines

How about doing it the other way around: teach the unwinder to detect when it hits a non-outermost entry (i.e. it lands in idtentry, etc) and use some reasonable heuristic as to whether it's okay to keep unwinding. You should be able to handle preemption like that, too -- the unwind process will end up in an IRQ frame.

How exactly would the unwinder detect if a text address is in an idtentry? Maybe put all the idt entries in a special ELF section?

Quoted 5 lines

Hmm.

What actually happens when you unwind all the way into the entry code?
Don't you end up in something that isn't in an ELF function? Can you
detect that?

For entry from user space (e.g., syscalls), it's easy to detect because there's always a pt_regs at the bottom of the stack. So if the unwinder reaches the stack address at (thread.sp0 - sizeof(pt_regs)), it knows it's done.

But for nested entry (e.g. in-kernel irqs/exceptions like preemption and page faults which don't have dedicated stacks), where the pt_regs is stored somewhere in the middle of the stack instead of the bottom, there's no reliable way to detect that.

Quoted 9 lines

Ideally, the unwinder could actually detect that it's
hit a pt_regs struct and report that. If used for stack dumps, it could display some indication of this and then continue its unwinding by decoding the pt_regs. If used for patching, it could take some other appropriate action.

I would have no objection to annotating all the pt_regs-style entry code, whether by putting it in a separate section or by making a table of addresses.

I think the easiest way to make it work would be to modify the idtentry macro to put all the idt entries in a dedicated section. Then the unwinder could easily detect any calls from that code.

Quoted 5 lines

There are a couple of nasty cases if NMI or MCE is involved but, as of 4.6, outside of NMI, MCE, and vmalloc faults (ugh!), there should always be a complete pt_regs on the stack before interrupts get enabled for each entry. Of course, finding the thing may be nontrivial in case other things were pushed.

NMI, MCE and interrupts aren't a problem because they have dedicated stacks, which are easy to detect. If the tasks' stack is on an exception stack or an irq stack, we consider it unreliable.

And also, they don't sleep. The stack of any running task (other than current) is automatically considered unreliable anyway, since they could be modifying it while we're reading it.

Quoted 2 lines

I suppose we could try to rejigger the code so that rbp points to pt_regs or similar.

I think we should avoid doing something like that because it would break gdb and all the other unwinders who don't know about it.

Signature · 1 line
Josh
#19 lore raw

Re: [RFC PATCH v2 05/18] sched: add task flag for preempt IRQ tracking

Quoted 66 lines · Josh Poimboeuf
Quoted 42 lines · Andy Lutomirski
Quoted 31 lines · Josh Poimboeuf
Quoted 30 lines · Andy Lutomirski
Quoted 28 lines · Josh Poimboeuf
Quoted 11 lines · Andy Lutomirski
Quoted 6 lines · Josh Poimboeuf

A preempted function might not have had a chance to save the frame pointer to the stack yet, which can result in its caller getting skipped on a stack trace.

Add a flag to indicate when the task has been preempted so that stack dump code can determine whether the stack trace is reliable.

I think I like this, but how do you handle the rather similar case in which a task goes to sleep because it's waiting on IO that happened in response to get_user, put_user, copy_from_user, etc?

Hm, good question. I was thinking that page faults had a dedicated stack, but now looking at the entry and traps code, that doesn't seem to be the case.

Anyway I think it shouldn't be a problem if we make sure that any kernel function which might trigger a valid page fault (e.g., copy_user_generic_string) do the proper frame pointer setup first. Then the stack should still be reliable.

In fact I might be able to teach objtool to enforce that: any function which uses an exception table should create a stack frame.

Or alternatively, maybe set some kind of flag for page faults, similar to what I did with this patch.

Quoted 9 lines
Quoted 5 lines

How about doing it the other way around: teach the unwinder to detect when it hits a non-outermost entry (i.e. it lands in idtentry, etc) and use some reasonable heuristic as to whether it's okay to keep unwinding. You should be able to handle preemption like that, too -- the unwind process will end up in an IRQ frame.

How exactly would the unwinder detect if a text address is in an idtentry? Maybe put all the idt entries in a special ELF section?

Quoted 5 lines

Hmm.

What actually happens when you unwind all the way into the entry code?
Don't you end up in something that isn't in an ELF function? Can you
detect that?

For entry from user space (e.g., syscalls), it's easy to detect because there's always a pt_regs at the bottom of the stack. So if the unwinder reaches the stack address at (thread.sp0 - sizeof(pt_regs)), it knows it's done.

But for nested entry (e.g. in-kernel irqs/exceptions like preemption and page faults which don't have dedicated stacks), where the pt_regs is stored somewhere in the middle of the stack instead of the bottom, there's no reliable way to detect that.

Quoted 6 lines

Ideally, the unwinder could actually detect that it's
hit a pt_regs struct and report that. If used for stack dumps, it could display some indication of this and then continue its unwinding by decoding the pt_regs. If used for patching, it could take some other appropriate action.

Quoted 7 lines
Quoted 3 lines

I would have no objection to annotating all the pt_regs-style entry code, whether by putting it in a separate section or by making a table of addresses.

I think the easiest way to make it work would be to modify the idtentry macro to put all the idt entries in a dedicated section. Then the unwinder could easily detect any calls from that code.

That would work. Would it make sense to do the same for the irq entries?

I'd be glad to review a patch. It should be straightforward.

Quoted 10 lines
Quoted 6 lines

There are a couple of nasty cases if NMI or MCE is involved but, as of 4.6, outside of NMI, MCE, and vmalloc faults (ugh!), there should always be a complete pt_regs on the stack before interrupts get enabled for each entry. Of course, finding the thing may be nontrivial in case other things were pushed.

NMI, MCE and interrupts aren't a problem because they have dedicated stacks, which are easy to detect. If the tasks' stack is on an exception stack or an irq stack, we consider it unreliable.

Only on x86_64.

Quoted 4 lines

And also, they don't sleep. The stack of any running task (other than current) is automatically considered unreliable anyway, since they could be modifying it while we're reading it.

True.

Quoted 6 lines
Quoted 3 lines

I suppose we could try to rejigger the code so that rbp points to pt_regs or similar.

I think we should avoid doing something like that because it would break gdb and all the other unwinders who don't know about it.

How so?

Currently, rbp in the entry code is meaningless. I'm suggesting that, when we do, for example, 'call \do_sym' in idtentry, we point rbp to the pt_regs. Currently it points to something stale (which the dump_stack code might be relying on. Hmm.) But it's probably also safe to assume that if you unwind to the 'call \do_sym', then pt_regs is the next thing on the stack, so just doing the section thing would work.

We should really re-add DWARF some day.

--Andy

Signature · 2 lines
Andy Lutomirski
AMA Capital Management, LLC
#8 lore raw

Re: [RFC PATCH v2 03/18] x86/asm/head: standardize the bottom of the stack for idle tasks

In reply to Josh Poimboeuf
Quoted 52 lines · Josh Poimboeuf
Quoted 42 lines · Andy Lutomirski
Quoted 41 lines · Josh Poimboeuf

Thanks to all the recent x86 entry code refactoring, most tasks' kernel stacks start at the same offset right above their saved pt_regs, regardless of which syscall was used to enter the kernel. That creates a nice convention which makes it straightforward to identify the "bottom" of the stack, which can be useful for stack walking code which needs to verify the stack is sane.

However there are still a few types of tasks which don't yet follow that convention:

1) CPU idle tasks, aka the "swapper" tasks

2) freshly forked TIF_FORK tasks which don't have a stack at all

Make the idle tasks conform to the new stack bottom convention by starting their stack at a sizeof(pt_regs) offset from the end of the stack page.

---
 arch/x86/kernel/head_64.S | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)
arch/x86/kernel/head_64.S +3 -2
index 6dbd2c0..0b12311 100644
@@ -296,8 +296,9 @@ ENTRY(start_cpu)
         *      REX.W + FF /5 JMP m16:64 Jump far, absolute indirect,
         *              address given in m16:64.
         */
-       movq    initial_code(%rip),%rax
-       pushq   $0              # fake return address to stop unwinder
+       call    1f              # put return address on stack for unwinder
+1:     xorq    %rbp, %rbp      # clear frame pointer
+       movq    initial_code(%rip), %rax
        pushq   $__KERNEL_CS    # set correct cs
        pushq   %rax            # target address in negative space
        lretq
@@ -325,7 +326,7 @@ ENDPROC(start_cpu0)
        GLOBAL(initial_gs)
Quoted 8 lines

arch/x86/kernel/head_64.S ENDPROC(start_cpu0) · line 329

Quoted 4 lines
arch/x86/kernel/head_64.S +1 -1
        .quad   INIT_PER_CPU_VAR(irq_stack_union)
        GLOBAL(initial_stack)
-       .quad  init_thread_union+THREAD_SIZE-8
+       .quad  init_thread_union + THREAD_SIZE - SIZEOF_PTREGS

As long as you're doing this, could you also set orig_ax to -1? I remember running into some oddities resulting from orig_ax containing garbage at some point.

Quoted 6 lines

I assume you mean to initialize the orig_rax value in the pt_regs at the bottom of the stack of the idle task?

How could that cause a problem? Since the idle task never returns from a system call, I'd assume that memory never gets accessed?

Look at collect_syscall in lib/syscall.c

Quoted 2 lines

--
Josh

Signature · 2 lines
Andy Lutomirski
AMA Capital Management, LLC
#20 lore raw

Re: [RFC PATCH v2 05/18] sched: add task flag for preempt IRQ tracking

In reply to Andy Lutomirski
Quoted 5 lines · Andy Lutomirski
Quoted 3 lines

NMI, MCE and interrupts aren't a problem because they have dedicated stacks, which are easy to detect. If the tasks' stack is on an exception stack or an irq stack, we consider it unreliable.

Only on x86_64.

Well, MCEs are more or less x86-specific as well. But otherwise good point, thanks Andy.

So, how does stack layout generally look like in case when NMI is actually running on proper kernel stack? I thought it's guaranteed to contain pt_regs anyway in all cases. Is that not guaranteed to be the case?

Thanks,

Signature · 2 lines
Jiri Kosina
SUSE Labs
#23 lore raw

Re: [RFC PATCH v2 05/18] sched: add task flag for preempt IRQ tracking

In reply to Andy Lutomirski
Quoted 6 lines · Andy Lutomirski
Quoted 3 lines · Josh Poimboeuf

I think the easiest way to make it work would be to modify the idtentry macro to put all the idt entries in a dedicated section. Then the unwinder could easily detect any calls from that code.

That would work. Would it make sense to do the same for the irq entries?

Yes, I think so.

Quoted 8 lines
Quoted 5 lines
Quoted 2 lines

I suppose we could try to rejigger the code so that rbp points to pt_regs or similar.

I think we should avoid doing something like that because it would break gdb and all the other unwinders who don't know about it.

How so?

Quoted 7 lines

Currently, rbp in the entry code is meaningless. I'm suggesting that, when we do, for example, 'call \do_sym' in idtentry, we point rbp to the pt_regs. Currently it points to something stale (which the dump_stack code might be relying on. Hmm.) But it's probably also safe to assume that if you unwind to the 'call \do_sym', then pt_regs is the next thing on the stack, so just doing the section thing would work.

Yes, rbp is meaningless on the entry from user space. But if an in-kernel interrupt occurs (e.g. page fault, preemption) and you have nested entry, rbp keeps its old value, right? So the unwinder can walk past the nested entry frame and keep going until it gets to the original entry.

Quoted 1 line

We should really re-add DWARF some day.

Working on it :-)

Signature · 1 line
Josh
#21 lore raw

Re: [RFC PATCH v2 05/18] sched: add task flag for preempt IRQ tracking

In reply to Jiri Kosina
Quoted 5 lines · Jiri Kosina
Quoted 4 lines · Andy Lutomirski
Quoted 3 lines

NMI, MCE and interrupts aren't a problem because they have dedicated stacks, which are easy to detect. If the tasks' stack is on an exception stack or an irq stack, we consider it unreliable.

Quoted 8 lines
Quoted 1 line

Only on x86_64.

Well, MCEs are more or less x86-specific as well. But otherwise good point, thanks Andy.

So, how does stack layout generally look like in case when NMI is actually running on proper kernel stack? I thought it's guaranteed to contain pt_regs anyway in all cases. Is that not guaranteed to be the case?

If the NMI were using the normal kernel stack and it interrupted kernel space, pt_regs would be placed in the "middle" of the stack rather than the bottom, and there's currently no way to detect that.

However, NMIs don't sleep, and we only consider sleeping tasks for stack reliability, so it wouldn't be an issue anyway.

Signature · 1 line
Josh
#9 lore raw

Re: [RFC PATCH v2 03/18] x86/asm/head: standardize the bottom of the stack for idle tasks

In reply to Andy Lutomirski
Quoted 53 lines · Andy Lutomirski
Quoted 52 lines · Josh Poimboeuf
Quoted 50 lines · Andy Lutomirski
Quoted 40 lines · Josh Poimboeuf

Thanks to all the recent x86 entry code refactoring, most tasks' kernel stacks start at the same offset right above their saved pt_regs, regardless of which syscall was used to enter the kernel. That creates a nice convention which makes it straightforward to identify the "bottom" of the stack, which can be useful for stack walking code which needs to verify the stack is sane.

However there are still a few types of tasks which don't yet follow that convention:

1) CPU idle tasks, aka the "swapper" tasks

2) freshly forked TIF_FORK tasks which don't have a stack at all

Make the idle tasks conform to the new stack bottom convention by starting their stack at a sizeof(pt_regs) offset from the end of the stack page.

---
 arch/x86/kernel/head_64.S | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)
arch/x86/kernel/head_64.S +3 -2
index 6dbd2c0..0b12311 100644
@@ -296,8 +296,9 @@ ENTRY(start_cpu)
         *      REX.W + FF /5 JMP m16:64 Jump far, absolute indirect,
         *              address given in m16:64.
         */
-       movq    initial_code(%rip),%rax
-       pushq   $0              # fake return address to stop unwinder
+       call    1f              # put return address on stack for unwinder
+1:     xorq    %rbp, %rbp      # clear frame pointer
+       movq    initial_code(%rip), %rax
        pushq   $__KERNEL_CS    # set correct cs
        pushq   %rax            # target address in negative space
        lretq

arch/x86/kernel/head_64.S ENDPROC(start_cpu0) · line 329

Quoted 5 lines
arch/x86/kernel/head_64.S +1 -1
@@ -325,7 +326,7 @@ ENDPROC(start_cpu0)
        GLOBAL(initial_gs)
        .quad   INIT_PER_CPU_VAR(irq_stack_union)
        GLOBAL(initial_stack)
-       .quad  init_thread_union+THREAD_SIZE-8
+       .quad  init_thread_union + THREAD_SIZE - SIZEOF_PTREGS

As long as you're doing this, could you also set orig_ax to -1? I remember running into some oddities resulting from orig_ax containing garbage at some point.

Quoted 8 lines
Quoted 6 lines

I assume you mean to initialize the orig_rax value in the pt_regs at the bottom of the stack of the idle task?

How could that cause a problem? Since the idle task never returns from a system call, I'd assume that memory never gets accessed?

Look at collect_syscall in lib/syscall.c

I don't see how collect_syscall() can be called for the per-cpu idle "swapper" tasks (which is what the above code affects). They don't have pids or /proc entries so you can't do /proc/<pid>/syscall on them.

Signature · 1 line
Josh
#24 lore raw

Re: [RFC PATCH v2 05/18] sched: add task flag for preempt IRQ tracking

In reply to Josh Poimboeuf
Quoted 27 lines · Josh Poimboeuf
Quoted 6 lines · Andy Lutomirski
Quoted 3 lines · Josh Poimboeuf

I think the easiest way to make it work would be to modify the idtentry macro to put all the idt entries in a dedicated section. Then the unwinder could easily detect any calls from that code.

That would work. Would it make sense to do the same for the irq entries?

Yes, I think so.

Quoted 8 lines
Quoted 5 lines
Quoted 2 lines

I suppose we could try to rejigger the code so that rbp points to pt_regs or similar.

I think we should avoid doing something like that because it would break gdb and all the other unwinders who don't know about it.

How so?

Quoted 7 lines

Currently, rbp in the entry code is meaningless. I'm suggesting that, when we do, for example, 'call \do_sym' in idtentry, we point rbp to the pt_regs. Currently it points to something stale (which the dump_stack code might be relying on. Hmm.) But it's probably also safe to assume that if you unwind to the 'call \do_sym', then pt_regs is the next thing on the stack, so just doing the section thing would work.

Quoted 5 lines

Yes, rbp is meaningless on the entry from user space. But if an in-kernel interrupt occurs (e.g. page fault, preemption) and you have nested entry, rbp keeps its old value, right? So the unwinder can walk past the nested entry frame and keep going until it gets to the original entry.

Yes.

It would be nice if we could do better, though, and actually notice the pt_regs and identify the entry. For example, I'd love to see "page fault, RIP=xyz" printed in the middle of a stack dump on a crash. Also, I think that just following rbp links will lose the actual function that took the page fault (or whatever function pt_regs->ip actually points to).

Quoted 4 lines
Quoted 2 lines

We should really re-add DWARF some day.

Working on it :-)

Excellent.

Have you looked at my vdso unwinding test at all? If we could do something similar for the kernel, IMO it would make testing much more pleasant.

--Andy

#22 lore raw

Re: [RFC PATCH v2 05/18] sched: add task flag for preempt IRQ tracking

In reply to Jiri Kosina
Quoted 7 lines · Jiri Kosina
Quoted 4 lines · Andy Lutomirski
Quoted 3 lines

NMI, MCE and interrupts aren't a problem because they have dedicated stacks, which are easy to detect. If the tasks' stack is on an exception stack or an irq stack, we consider it unreliable.

Quoted 9 lines
Quoted 1 line

Only on x86_64.

Well, MCEs are more or less x86-specific as well. But otherwise good point, thanks Andy.

So, how does stack layout generally look like in case when NMI is actually running on proper kernel stack? I thought it's guaranteed to contain pt_regs anyway in all cases. Is that not guaranteed to be the case?

On x86, at least, there will still be pt_regs for the NMI. For the interrupted state, though, there might not be pt_regs, as the NMI might have happened while still populating pt_regs. In fact, the NMI stack could overlap task_pt_regs.

For x86_32, there's no guarantee that pt_regs contains sp due to hardware silliness. You need to parse it more carefully, as, !user_mode(regs), then the old sp is just above pt_regs.

--Andy

#10 lore raw

Re: [RFC PATCH v2 03/18] x86/asm/head: standardize the bottom of the stack for idle tasks

In reply to Josh Poimboeuf
Quoted 62 lines · Josh Poimboeuf
Quoted 60 lines · Andy Lutomirski
Quoted 58 lines · Josh Poimboeuf
Quoted 42 lines · Andy Lutomirski
Quoted 41 lines · Josh Poimboeuf

Thanks to all the recent x86 entry code refactoring, most tasks' kernel stacks start at the same offset right above their saved pt_regs, regardless of which syscall was used to enter the kernel. That creates a nice convention which makes it straightforward to identify the "bottom" of the stack, which can be useful for stack walking code which needs to verify the stack is sane.

However there are still a few types of tasks which don't yet follow that convention:

1) CPU idle tasks, aka the "swapper" tasks

2) freshly forked TIF_FORK tasks which don't have a stack at all

Make the idle tasks conform to the new stack bottom convention by starting their stack at a sizeof(pt_regs) offset from the end of the stack page.

---
 arch/x86/kernel/head_64.S | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)
arch/x86/kernel/head_64.S +3 -2
index 6dbd2c0..0b12311 100644
@@ -296,8 +296,9 @@ ENTRY(start_cpu)
         *      REX.W + FF /5 JMP m16:64 Jump far, absolute indirect,
         *              address given in m16:64.
         */
-       movq    initial_code(%rip),%rax
-       pushq   $0              # fake return address to stop unwinder
+       call    1f              # put return address on stack for unwinder
+1:     xorq    %rbp, %rbp      # clear frame pointer
+       movq    initial_code(%rip), %rax
        pushq   $__KERNEL_CS    # set correct cs
        pushq   %rax            # target address in negative space
        lretq
@@ -325,7 +326,7 @@ ENDPROC(start_cpu0)
        GLOBAL(initial_gs)
Quoted 8 lines

arch/x86/kernel/head_64.S ENDPROC(start_cpu0) · line 329

Quoted 4 lines
arch/x86/kernel/head_64.S +1 -1
        .quad   INIT_PER_CPU_VAR(irq_stack_union)
        GLOBAL(initial_stack)
-       .quad  init_thread_union+THREAD_SIZE-8
+       .quad  init_thread_union + THREAD_SIZE - SIZEOF_PTREGS

As long as you're doing this, could you also set orig_ax to -1? I remember running into some oddities resulting from orig_ax containing garbage at some point.

I assume you mean to initialize the orig_rax value in the pt_regs at the bottom of the stack of the idle task?

How could that cause a problem? Since the idle task never returns from a system call, I'd assume that memory never gets accessed?

Quoted 5 lines
Quoted 1 line

Look at collect_syscall in lib/syscall.c

I don't see how collect_syscall() can be called for the per-cpu idle "swapper" tasks (which is what the above code affects). They don't have pids or /proc entries so you can't do /proc/<pid>/syscall on them.

If so, then never mind.

--Andy

Quoted 3 lines

--
Josh

#25 lore raw

Re: [RFC PATCH v2 05/18] sched: add task flag for preempt IRQ tracking

In reply to Andy Lutomirski
Quoted 28 lines · Andy Lutomirski
Quoted 19 lines · Josh Poimboeuf
Quoted 9 lines · Andy Lutomirski
Quoted 5 lines · Josh Poimboeuf
Quoted 2 lines

I suppose we could try to rejigger the code so that rbp points to pt_regs or similar.

I think we should avoid doing something like that because it would break gdb and all the other unwinders who don't know about it.

How so?

Quoted 7 lines

Currently, rbp in the entry code is meaningless. I'm suggesting that, when we do, for example, 'call \do_sym' in idtentry, we point rbp to the pt_regs. Currently it points to something stale (which the dump_stack code might be relying on. Hmm.) But it's probably also safe to assume that if you unwind to the 'call \do_sym', then pt_regs is the next thing on the stack, so just doing the section thing would work.

Quoted 5 lines

Yes, rbp is meaningless on the entry from user space. But if an in-kernel interrupt occurs (e.g. page fault, preemption) and you have nested entry, rbp keeps its old value, right? So the unwinder can walk past the nested entry frame and keep going until it gets to the original entry.

Yes.

Quoted 8 lines

It would be nice if we could do better, though, and actually notice the pt_regs and identify the entry. For example, I'd love to see "page fault, RIP=xyz" printed in the middle of a stack dump on a crash.

Also, I think that just following rbp links will lose the actual function that took the page fault (or whatever function pt_regs->ip actually points to).

Hm. I think we could fix all that in a more standard way. Whenever a new pt_regs frame gets saved on entry, we could also create a new stack frame which points to a fake kernel_entry() function. That would tell the unwinder there's a pt_regs frame without otherwise breaking frame pointers across the frame.

Then I guess we wouldn't need my other solution of putting the idt entries in a special section.

How does that sound?

Quoted 3 lines

Have you looked at my vdso unwinding test at all? If we could do something similar for the kernel, IMO it would make testing much more pleasant.

I found it, but I'm not sure what it would mean to do something similar for the kernel. Do you mean doing something like an NMI sampling-based approach where we periodically do a random stack sanity check?

(If so, I do have something like that planned.)

Signature · 1 line
Josh
#26 lore raw

Re: [RFC PATCH v2 05/18] sched: add task flag for preempt IRQ tracking

Quoted 44 lines · Josh Poimboeuf
Quoted 28 lines · Andy Lutomirski
Quoted 19 lines · Josh Poimboeuf
Quoted 9 lines · Andy Lutomirski
Quoted 5 lines · Josh Poimboeuf
Quoted 2 lines

I suppose we could try to rejigger the code so that rbp points to pt_regs or similar.

I think we should avoid doing something like that because it would break gdb and all the other unwinders who don't know about it.

How so?

Quoted 7 lines

Currently, rbp in the entry code is meaningless. I'm suggesting that, when we do, for example, 'call \do_sym' in idtentry, we point rbp to the pt_regs. Currently it points to something stale (which the dump_stack code might be relying on. Hmm.) But it's probably also safe to assume that if you unwind to the 'call \do_sym', then pt_regs is the next thing on the stack, so just doing the section thing would work.

Quoted 5 lines

Yes, rbp is meaningless on the entry from user space. But if an in-kernel interrupt occurs (e.g. page fault, preemption) and you have nested entry, rbp keeps its old value, right? So the unwinder can walk past the nested entry frame and keep going until it gets to the original entry.

Yes.

Quoted 8 lines

It would be nice if we could do better, though, and actually notice the pt_regs and identify the entry. For example, I'd love to see "page fault, RIP=xyz" printed in the middle of a stack dump on a crash.

Also, I think that just following rbp links will lose the actual function that took the page fault (or whatever function pt_regs->ip actually points to).

Hm. I think we could fix all that in a more standard way. Whenever a new pt_regs frame gets saved on entry, we could also create a new stack frame which points to a fake kernel_entry() function. That would tell the unwinder there's a pt_regs frame without otherwise breaking frame pointers across the frame.

Quoted 4 lines

Then I guess we wouldn't need my other solution of putting the idt entries in a special section.

How does that sound?

Let me try to understand.

The normal call sequence is call; push %rbp; mov %rsp, %rbp. So rbp points to (prev rbp, prev rip) on the stack, and you can follow the chain back. Right now, on a user access page fault or similar, we have rbp (probably) pointing to the interrupted frame, and the interrupted rip isn't saved anywhere that a naive unwinder can find it. (It's in pt_regs, but the rbp chain skips right over that.)

We could change the entry code so that an interrupt / idtentry does:

push pt_regs
push kernel_entry
push %rbp
mov %rsp, %rbp
call handler
pop %rbp
addq $8, %rsp

or similar. That would make it appear that the actual C handler was caused by a dummy function "kernel_entry". Now the unwinder would get to kernel_entry, but it *still* wouldn't find its way to the calling frame, which only solves part of the problem. We could at least teach the unwinder how kernel_entry works and let it decode pt_regs to continue unwinding. This would be nice, and I think it could work.

I think I like this, except that, if it used a separate section, it could potentially be faster, as, for each actual entry type, the offset from the C handler frame to pt_regs is a foregone conclusion. But this is pretty simple and performance is already abysmal in most handlers.

There's an added benefit to using a separate section, though: we could also annotate the calls with what type of entry they were so the unwinder could print it out nicely.

I could be convinced either way.

Quoted 8 lines
Quoted 4 lines

Have you looked at my vdso unwinding test at all? If we could do something similar for the kernel, IMO it would make testing much more pleasant.

I found it, but I'm not sure what it would mean to do something similar for the kernel. Do you mean doing something like an NMI sampling-based approach where we periodically do a random stack sanity check?

I was imagining something a little more strict: single-step interesting parts of the kernel and make sure that each step unwinds correctly. That could detect missing frames and similar.

#27 lore raw

Re: [RFC PATCH v2 05/18] sched: add task flag for preempt IRQ tracking

Quoted 69 lines · Andy Lutomirski
Quoted 44 lines · Josh Poimboeuf
Quoted 28 lines · Andy Lutomirski
Quoted 19 lines · Josh Poimboeuf
Quoted 9 lines · Andy Lutomirski
Quoted 5 lines · Josh Poimboeuf
Quoted 2 lines

I suppose we could try to rejigger the code so that rbp points to pt_regs or similar.

I think we should avoid doing something like that because it would break gdb and all the other unwinders who don't know about it.

How so?

Quoted 7 lines

Currently, rbp in the entry code is meaningless. I'm suggesting that, when we do, for example, 'call \do_sym' in idtentry, we point rbp to the pt_regs. Currently it points to something stale (which the dump_stack code might be relying on. Hmm.) But it's probably also safe to assume that if you unwind to the 'call \do_sym', then pt_regs is the next thing on the stack, so just doing the section thing would work.

Quoted 5 lines

Yes, rbp is meaningless on the entry from user space. But if an in-kernel interrupt occurs (e.g. page fault, preemption) and you have nested entry, rbp keeps its old value, right? So the unwinder can walk past the nested entry frame and keep going until it gets to the original entry.

Yes.

Quoted 8 lines

It would be nice if we could do better, though, and actually notice the pt_regs and identify the entry. For example, I'd love to see "page fault, RIP=xyz" printed in the middle of a stack dump on a crash.

Also, I think that just following rbp links will lose the actual function that took the page fault (or whatever function pt_regs->ip actually points to).

Hm. I think we could fix all that in a more standard way. Whenever a new pt_regs frame gets saved on entry, we could also create a new stack frame which points to a fake kernel_entry() function. That would tell the unwinder there's a pt_regs frame without otherwise breaking frame pointers across the frame.

Quoted 4 lines

Then I guess we wouldn't need my other solution of putting the idt entries in a special section.

How does that sound?

Let me try to understand.

The normal call sequence is call; push %rbp; mov %rsp, %rbp. So rbp points to (prev rbp, prev rip) on the stack, and you can follow the chain back. Right now, on a user access page fault or similar, we have rbp (probably) pointing to the interrupted frame, and the interrupted rip isn't saved anywhere that a naive unwinder can find it. (It's in pt_regs, but the rbp chain skips right over that.)

We could change the entry code so that an interrupt / idtentry does:

push pt_regs
push kernel_entry
push %rbp
mov %rsp, %rbp
call handler
pop %rbp
addq $8, %rsp

Quoted 6 lines

or similar. That would make it appear that the actual C handler was caused by a dummy function "kernel_entry". Now the unwinder would get to kernel_entry, but it *still* wouldn't find its way to the calling frame, which only solves part of the problem. We could at least teach the unwinder how kernel_entry works and let it decode pt_regs to continue unwinding. This would be nice, and I think it could work.

Yeah, that's about what I had in mind.

Quoted 3 lines

I think I like this, except that, if it used a separate section, it could potentially be faster, as, for each actual entry type, the offset from the C handler frame to pt_regs is a foregone conclusion.

Hm, this I don't really follow. It's true that the unwinder can easily find RIP from pt_regs, which will always be a known offset from the kernel_entry pointer on the stack. But why would having the entry code in a separate section make that faster?

Quoted 6 lines

But this is pretty simple and performance is already abysmal in most handlers.

There's an added benefit to using a separate section, though: we could also annotate the calls with what type of entry they were so the unwinder could print it out nicely.

Yeah, that could be a nice feature... but doesn't printing the name of the C handler pretty much already give that information?

In any case, once we have a working DWARF unwinder, I think it will show the name of the idt entry anyway.

Quoted 11 lines
Quoted 7 lines
Quoted 3 lines

Have you looked at my vdso unwinding test at all? If we could do something similar for the kernel, IMO it would make testing much more pleasant.

I found it, but I'm not sure what it would mean to do something similar for the kernel. Do you mean doing something like an NMI sampling-based approach where we periodically do a random stack sanity check?

I was imagining something a little more strict: single-step interesting parts of the kernel and make sure that each step unwinds correctly. That could detect missing frames and similar.

Interesting idea, though I wonder how hard it would be to reliably distinguish a missing frame from the case where gcc decides to inline a function.

Another idea to detect missing frames: for each return address on the stack, ensure there's a corresponding "call <func>" instruction immediately preceding the return location, where <func> matches what's on the stack.

That wouldn't work so well for indirect calls using function pointers, but even then maybe we could use the DWARF CFI to find the function pointer value and validate that it matches the stack function.

Signature · 1 line
Josh
#28 lore raw

Re: [RFC PATCH v2 05/18] sched: add task flag for preempt IRQ tracking

Quoted 70 lines · Josh Poimboeuf
Quoted 69 lines · Andy Lutomirski
Quoted 44 lines · Josh Poimboeuf
Quoted 28 lines · Andy Lutomirski
Quoted 19 lines · Josh Poimboeuf
Quoted 9 lines · Andy Lutomirski
Quoted 5 lines · Josh Poimboeuf
Quoted 2 lines

I suppose we could try to rejigger the code so that rbp points to pt_regs or similar.

I think we should avoid doing something like that because it would break gdb and all the other unwinders who don't know about it.

How so?

Quoted 7 lines

Currently, rbp in the entry code is meaningless. I'm suggesting that, when we do, for example, 'call \do_sym' in idtentry, we point rbp to the pt_regs. Currently it points to something stale (which the dump_stack code might be relying on. Hmm.) But it's probably also safe to assume that if you unwind to the 'call \do_sym', then pt_regs is the next thing on the stack, so just doing the section thing would work.

Quoted 5 lines

Yes, rbp is meaningless on the entry from user space. But if an in-kernel interrupt occurs (e.g. page fault, preemption) and you have nested entry, rbp keeps its old value, right? So the unwinder can walk past the nested entry frame and keep going until it gets to the original entry.

Yes.

Quoted 8 lines

It would be nice if we could do better, though, and actually notice the pt_regs and identify the entry. For example, I'd love to see "page fault, RIP=xyz" printed in the middle of a stack dump on a crash.

Also, I think that just following rbp links will lose the actual function that took the page fault (or whatever function pt_regs->ip actually points to).

Hm. I think we could fix all that in a more standard way. Whenever a new pt_regs frame gets saved on entry, we could also create a new stack frame which points to a fake kernel_entry() function. That would tell the unwinder there's a pt_regs frame without otherwise breaking frame pointers across the frame.

Quoted 4 lines

Then I guess we wouldn't need my other solution of putting the idt entries in a special section.

How does that sound?

Let me try to understand.

The normal call sequence is call; push %rbp; mov %rsp, %rbp. So rbp points to (prev rbp, prev rip) on the stack, and you can follow the chain back. Right now, on a user access page fault or similar, we have rbp (probably) pointing to the interrupted frame, and the interrupted rip isn't saved anywhere that a naive unwinder can find it. (It's in pt_regs, but the rbp chain skips right over that.)

We could change the entry code so that an interrupt / idtentry does:

push pt_regs
push kernel_entry
push %rbp
mov %rsp, %rbp
call handler
pop %rbp
addq $8, %rsp

Quoted 8 lines
Quoted 6 lines

or similar. That would make it appear that the actual C handler was caused by a dummy function "kernel_entry". Now the unwinder would get to kernel_entry, but it *still* wouldn't find its way to the calling frame, which only solves part of the problem. We could at least teach the unwinder how kernel_entry works and let it decode pt_regs to continue unwinding. This would be nice, and I think it could work.

Yeah, that's about what I had in mind.

FWIW, I just tried this:

static bool is_entry_text(unsigned long addr)

{
    return addr >= (unsigned long)__entry_text_start &&
        addr < (unsigned long)__entry_text_end;
}

it works. So the entry code is already annotated reasonably well :)

I just hacked it up here:

https://git.kernel.org/cgit/linux/kernel/git/luto/linux.git/commit/?h=stack&id=085eacfe0edfc18768e48340084415dba9a6bd21

and it seems to work, at least for page faults. A better implementation would print out the entire contents of pt_regs so that people reading the stack trace will know the registers at the time of the exception, which might be helpful.

Quoted 9 lines
Quoted 4 lines

I think I like this, except that, if it used a separate section, it could potentially be faster, as, for each actual entry type, the offset from the C handler frame to pt_regs is a foregone conclusion.

Hm, this I don't really follow. It's true that the unwinder can easily find RIP from pt_regs, which will always be a known offset from the kernel_entry pointer on the stack. But why would having the entry code in a separate section make that faster?

It doesn't make the unwinder faster -- it makes the entry code faster.

Quoted 8 lines
Quoted 7 lines

But this is pretty simple and performance is already abysmal in most handlers.

There's an added benefit to using a separate section, though: we could also annotate the calls with what type of entry they were so the unwinder could print it out nicely.

Quoted 5 lines

Yeah, that could be a nice feature... but doesn't printing the name of the C handler pretty much already give that information?

In any case, once we have a working DWARF unwinder, I think it will show the name of the idt entry anyway.

True. And it'll automatically follow pt_regs.

Quoted 13 lines
Quoted 5 lines
Quoted 5 lines
Quoted 4 lines

Have you looked at my vdso unwinding test at all? If we could do something similar for the kernel, IMO it would make testing much more pleasant.

Quoted 7 lines
Quoted 3 lines

I found it, but I'm not sure what it would mean to do something similar for the kernel. Do you mean doing something like an NMI sampling-based approach where we periodically do a random stack sanity check?

I was imagining something a little more strict: single-step interesting parts of the kernel and make sure that each step unwinds correctly. That could detect missing frames and similar.

Quoted 8 lines

Interesting idea, though I wonder how hard it would be to reliably distinguish a missing frame from the case where gcc decides to inline a function.

Another idea to detect missing frames: for each return address on the stack, ensure there's a corresponding "call <func>" instruction immediately preceding the return location, where <func> matches what's on the stack.

Hmm, interesting.

I hope your plans include rewriting the current stack unwinder completely. The thing in print_context_stack is (a) hard-to-understand and hard-to-modify crap and (b) is called in a loop from another file using totally ridiculous conventions.

--Andy

#29 lore raw

Re: [RFC PATCH v2 05/18] sched: add task flag for preempt IRQ tracking

Quoted 10 lines · Andy Lutomirski
Quoted 3 lines

Another idea to detect missing frames: for each return address on the stack, ensure there's a corresponding "call <func>" instruction immediately preceding the return location, where <func> matches what's on the stack.

Hmm, interesting.

I hope your plans include rewriting the current stack unwinder completely. The thing in print_context_stack is (a) hard-to-understand and hard-to-modify crap and (b) is called in a loop from another file using totally ridiculous conventions.

So we had several attempts at making it better, any further improvements (including radical rewrites) are more than welcome!

The generalization between the various stack walking methods certainly didn't make things easier to read - we might want to eliminate that by using better primitives to iterate over the stack frame.

Thanks,

Ingo

#30 lore raw

Re: [RFC PATCH v2 05/18] sched: add task flag for preempt IRQ tracking

In reply to Andy Lutomirski
Quoted 90 lines · Andy Lutomirski
Quoted 70 lines · Josh Poimboeuf
Quoted 69 lines · Andy Lutomirski
Quoted 44 lines · Josh Poimboeuf
Quoted 28 lines · Andy Lutomirski
Quoted 19 lines · Josh Poimboeuf
Quoted 9 lines · Andy Lutomirski
Quoted 5 lines · Josh Poimboeuf
Quoted 2 lines

I suppose we could try to rejigger the code so that rbp points to pt_regs or similar.

I think we should avoid doing something like that because it would break gdb and all the other unwinders who don't know about it.

How so?

Quoted 7 lines

Currently, rbp in the entry code is meaningless. I'm suggesting that, when we do, for example, 'call \do_sym' in idtentry, we point rbp to the pt_regs. Currently it points to something stale (which the dump_stack code might be relying on. Hmm.) But it's probably also safe to assume that if you unwind to the 'call \do_sym', then pt_regs is the next thing on the stack, so just doing the section thing would work.

Quoted 5 lines

Yes, rbp is meaningless on the entry from user space. But if an in-kernel interrupt occurs (e.g. page fault, preemption) and you have nested entry, rbp keeps its old value, right? So the unwinder can walk past the nested entry frame and keep going until it gets to the original entry.

Yes.

Quoted 8 lines

It would be nice if we could do better, though, and actually notice the pt_regs and identify the entry. For example, I'd love to see "page fault, RIP=xyz" printed in the middle of a stack dump on a crash.

Also, I think that just following rbp links will lose the actual function that took the page fault (or whatever function pt_regs->ip actually points to).

Hm. I think we could fix all that in a more standard way. Whenever a new pt_regs frame gets saved on entry, we could also create a new stack frame which points to a fake kernel_entry() function. That would tell the unwinder there's a pt_regs frame without otherwise breaking frame pointers across the frame.

Quoted 4 lines

Then I guess we wouldn't need my other solution of putting the idt entries in a special section.

How does that sound?

Let me try to understand.

The normal call sequence is call; push %rbp; mov %rsp, %rbp. So rbp points to (prev rbp, prev rip) on the stack, and you can follow the chain back. Right now, on a user access page fault or similar, we have rbp (probably) pointing to the interrupted frame, and the interrupted rip isn't saved anywhere that a naive unwinder can find it. (It's in pt_regs, but the rbp chain skips right over that.)

We could change the entry code so that an interrupt / idtentry does:

push pt_regs
push kernel_entry
push %rbp
mov %rsp, %rbp
call handler
pop %rbp
addq $8, %rsp

Quoted 8 lines
Quoted 6 lines

or similar. That would make it appear that the actual C handler was caused by a dummy function "kernel_entry". Now the unwinder would get to kernel_entry, but it *still* wouldn't find its way to the calling frame, which only solves part of the problem. We could at least teach the unwinder how kernel_entry works and let it decode pt_regs to continue unwinding. This would be nice, and I think it could work.

Yeah, that's about what I had in mind.

FWIW, I just tried this:

static bool is_entry_text(unsigned long addr)

{
    return addr >= (unsigned long)__entry_text_start &&
        addr < (unsigned long)__entry_text_end;
}

it works. So the entry code is already annotated reasonably well :)

Quoted 8 lines

I just hacked it up here:

https://git.kernel.org/cgit/linux/kernel/git/luto/linux.git/commit/?h=stack&id=085eacfe0edfc18768e48340084415dba9a6bd21

and it seems to work, at least for page faults. A better implementation would print out the entire contents of pt_regs so that people reading the stack trace will know the registers at the time of the exception, which might be helpful.

I still think we would need more specific annotations to do that
reliably: a call from entry code doesn't necessarily correlate with a pt_regs frame.

Quoted 10 lines
Quoted 8 lines
Quoted 3 lines

I think I like this, except that, if it used a separate section, it could potentially be faster, as, for each actual entry type, the offset from the C handler frame to pt_regs is a foregone conclusion.

Hm, this I don't really follow. It's true that the unwinder can easily find RIP from pt_regs, which will always be a known offset from the kernel_entry pointer on the stack. But why would having the entry code in a separate section make that faster?

It doesn't make the unwinder faster -- it makes the entry code faster.

Oh, right. But I don't think a few extra frame pointer instructions are much of an issue if you already have CONFIG_FRAME_POINTER enabled.

Anyway I'm not sure which way is better. I'll think about it.

Quoted 4 lines

I hope your plans include rewriting the current stack unwinder completely. The thing in print_context_stack is (a) hard-to-understand and hard-to-modify crap and (b) is called in a loop from another file using totally ridiculous conventions.

I agree, that code is quite confusing. I haven't really thought about how specifically it could be improved or replaced though.

Along those lines, I think it would be awesome if we could have an arch-independent DWARF unwinder so that most of the stack dumping code could be shared amongst all the arches.

Signature · 1 line
Josh
#31 lore raw

Re: [RFC PATCH v2 05/18] sched: add task flag for preempt IRQ tracking

In reply to Andy Lutomirski
Quoted 10 lines · Andy Lutomirski

FWIW, I just tried this:

static bool is_entry_text(unsigned long addr)

{
    return addr >= (unsigned long)__entry_text_start &&
        addr < (unsigned long)__entry_text_end;
}

it works. So the entry code is already annotated reasonably well :)

Quoted 8 lines

I just hacked it up here:

https://git.kernel.org/cgit/linux/kernel/git/luto/linux.git/commit/?h=stack&id=085eacfe0edfc18768e48340084415dba9a6bd21

and it seems to work, at least for page faults. A better implementation would print out the entire contents of pt_regs so that people reading the stack trace will know the registers at the time of the exception, which might be helpful.

Sorry for being dense, but how do you distinguish here between a "real" kernel entry, that pushes pt_regs, and any "non-entry" function call that passes pt_regs around?

Signature · 2 lines
Jiri Kosina
SUSE Labs
#32 lore raw

Re: [RFC PATCH v2 05/18] sched: add task flag for preempt IRQ tracking

Quoted 14 lines · Jiri Kosina
Quoted 14 lines

FWIW, I just tried this:

static bool is_entry_text(unsigned long addr)

{
    return addr >= (unsigned long)__entry_text_start &&
        addr < (unsigned long)__entry_text_end;
}

it works. So the entry code is already annotated reasonably well :)

I just hacked it up here:

https://git.kernel.org/cgit/linux/kernel/git/luto/linux.git/commit/?h=stack&id=085eacfe0edfc18768e48340084415dba9a6bd21

Quoted 8 lines
Quoted 4 lines

and it seems to work, at least for page faults. A better implementation would print out the entire contents of pt_regs so that people reading the stack trace will know the registers at the time of the exception, which might be helpful.

Sorry for being dense, but how do you distinguish here between a "real" kernel entry, that pushes pt_regs, and any "non-entry" function call that passes pt_regs around?

Umm, actually, the more tricky part is the other way around -- how do you make sure that whenever you are calling out from a code between __entry_text_start and __entry_text_end, pt_regs will be at the place you're looking for it? How's that guaranteed?

Thanks,

Signature · 2 lines
Jiri Kosina
SUSE Labs
#33 lore raw

Re: [RFC PATCH v2 05/18] sched: add task flag for preempt IRQ tracking

Quoted 21 lines · Jiri Kosina
Quoted 19 lines · Jiri Kosina
Quoted 18 lines

FWIW, I just tried this:

static bool is_entry_text(unsigned long addr)

{
    return addr >= (unsigned long)__entry_text_start &&
        addr < (unsigned long)__entry_text_end;
}

it works. So the entry code is already annotated reasonably well :)

I just hacked it up here:

https://git.kernel.org/cgit/linux/kernel/git/luto/linux.git/commit/?h=stack&id=085eacfe0edfc18768e48340084415dba9a6bd21

and it seems to work, at least for page faults. A better implementation would print out the entire contents of pt_regs so that people reading the stack trace will know the registers at the time of the exception, which might be helpful.

Quoted 8 lines
Quoted 3 lines

Sorry for being dense, but how do you distinguish here between a "real" kernel entry, that pushes pt_regs, and any "non-entry" function call that passes pt_regs around?

Umm, actually, the more tricky part is the other way around -- how do you make sure that whenever you are calling out from a code between __entry_text_start and __entry_text_end, pt_regs will be at the place you're looking for it? How's that guaranteed?

It's not guaranteed in my code. I think we'd want to add a little table of call sites and their pt_regs offsets. This was just meant to test that the general idea works (and it does indeed generate better traces than the stock kernel, which gets it unconditionally wrong).

--Andy

Quoted 7 lines

Thanks,

--
Jiri Kosina
SUSE Labs

Signature · 2 lines
Andy Lutomirski
AMA Capital Management, LLC
maintainer
#69 lore raw

Re: [RFC PATCH v2 10/18] livepatch/powerpc: add TIF_PATCH_PENDING thread flag

In reply to Josh Poimboeuf
Quoted 56 lines · Josh Poimboeuf

Add the TIF_PATCH_PENDING thread flag to enable the new livepatch per-task consistency model for powerpc. The bit getting set indicates the thread has a pending patch which needs to be applied when the thread exits the kernel.

The bit is included in the _TIF_USER_WORK_MASK macro so that do_notify_resume() and klp_patch_task() get called when the bit is set.

---
 arch/powerpc/include/asm/thread_info.h | 4 +++-
 arch/powerpc/kernel/signal.c           | 4 ++++
 2 files changed, 7 insertions(+), 1 deletion(-)
arch/powerpc/include/asm/thread_info.h +3 -1
index 8febc3f..df262ca 100644
@@ -88,6 +88,7 @@ static inline struct thread_info *current_thread_info(void)
 					   TIF_NEED_RESCHED */
 #define TIF_32BIT		4	/* 32 bit binary */
 #define TIF_RESTORE_TM		5	/* need to restore TM FP/VEC/VSX */
+#define TIF_PATCH_PENDING	6	/* pending live patching update */
 #define TIF_SYSCALL_AUDIT	7	/* syscall auditing active */
 #define TIF_SINGLESTEP		8	/* singlestepping active */
 #define TIF_NOHZ		9	/* in adaptive nohz mode */
@@ -111,6 +112,7 @@ static inline struct thread_info *current_thread_info(void)
 #define _TIF_POLLING_NRFLAG	(1<<TIF_POLLING_NRFLAG)
 #define _TIF_32BIT		(1<<TIF_32BIT)
 #define _TIF_RESTORE_TM		(1<<TIF_RESTORE_TM)
+#define _TIF_PATCH_PENDING	(1<<TIF_PATCH_PENDING)
 #define _TIF_SYSCALL_AUDIT	(1<<TIF_SYSCALL_AUDIT)
 #define _TIF_SINGLESTEP		(1<<TIF_SINGLESTEP)
 #define _TIF_SECCOMP		(1<<TIF_SECCOMP)
@@ -127,7 +129,7 @@ static inline struct thread_info *current_thread_info(void)
 
 #define _TIF_USER_WORK_MASK	(_TIF_SIGPENDING | _TIF_NEED_RESCHED | \
 				 _TIF_NOTIFY_RESUME | _TIF_UPROBE | \
-				 _TIF_RESTORE_TM)
+				 _TIF_RESTORE_TM | _TIF_PATCH_PENDING)
 #define _TIF_PERSYSCALL_MASK	(_TIF_RESTOREALL|_TIF_NOERROR)
 
 /* Bits in local_flags */
arch/powerpc/kernel/signal.c +1 -0
index cb64d6f..844497b 100644
@@ -14,6 +14,7 @@
 #include <linux/uprobes.h>
 #include <linux/key.h>
 #include <linux/context_tracking.h>
+#include <linux/livepatch.h>
 #include <asm/hw_breakpoint.h>
 #include <asm/uaccess.h>
 #include <asm/unistd.h>

arch/powerpc/kernel/signal.c void do_notify_resume(struct pt_regs *regs, unsigned long thread_info_flags) · line 165

Quoted 6 lines
arch/powerpc/kernel/signal.c +3 -0
@@ -159,6 +160,9 @@ void do_notify_resume(struct pt_regs *regs, unsigned long thread_info_flags)
 		tracehook_notify_resume(regs);
 	}
 
+	if (thread_info_flags & _TIF_PATCH_PENDING)
+		klp_patch_task(current);
+

JFYI, if we later add the fake signal to speed up migration of sleeping task, we would need to move this up before calling do_signal(). It would help to avoid cycling here twice. Mirek surely knows more details about it.

Best Regards,
Petr

Quoted 6 lines
arch/powerpc/kernel/signal.c +0 -0
 	user_enter();
 }
 
Signature · 1 line
2.4.11
maintainer
#74 lore raw

Re: [RFC PATCH v2 13/18] livepatch: separate enabled and patched states

In reply to Josh Poimboeuf
Quoted 30 lines · Josh Poimboeuf

Once we have a consistency model, patches and their objects will be enabled and disabled at different times. For example, when a patch is disabled, its loaded objects' funcs can remain registered with ftrace indefinitely until the unpatching operation is complete and they're no longer in use.

It's less confusing if we give them different names: patches can be enabled or disabled; objects (and their funcs) can be patched or unpatched:

- Enabled means that a patch is logically enabled (but not necessarily fully applied).

- Patched means that an object's funcs are registered with ftrace and added to the klp_ops func stack.

Also, since these states are binary, represent them with booleans instead of ints.

---
 include/linux/livepatch.h | 17 ++++-------
 kernel/livepatch/core.c   | 72 +++++++++++++++++++++++------------------------
 2 files changed, 42 insertions(+), 47 deletions(-)
kernel/livepatch/core.c +0 -0
index 6ea6880..2b59230 100644

kernel/livepatch/core.c static ssize_t enabled_store(struct kobject *kobj, struct kobj_attribute *attr, · line 626

Quoted 6 lines
kernel/livepatch/core.c +1 -1
@@ -622,20 +622,20 @@ static ssize_t enabled_store(struct kobject *kobj, struct kobj_attribute *attr,
 	if (ret)
 		return -EINVAL;
 
-	if (val != KLP_DISABLED && val != KLP_ENABLED)
+	if (val > 1)
 		return -EINVAL;

It would be cleaner to get "val" via kstrtobool(). It guarantees that the value is true or false. Another nice win is that it accepts Y/y/1/N/n/0 as the input.

kernel/livepatch/core.c static ssize_t enabled_store(struct kobject *kobj, struct kobj_attribute *attr, · line 632

Quoted 6 lines
kernel/livepatch/core.c +1 -1
 	patch = container_of(kobj, struct klp_patch, kobj);
 
 	mutex_lock(&klp_mutex);
 
-	if (val == patch->state) {
+	if (patch->enabled == val) {

Also this check will be cleaner if "val" is a boolean.

Best Regards,
Petr

maintainer
#78 lore raw

Re: [RFC PATCH v2 15/18] livepatch: move patching functions into patch.c

In reply to Josh Poimboeuf
Quoted 9 lines · Josh Poimboeuf

Move functions related to the actual patching of functions and objects into a new patch.c file.

kernel/livepatch/patch.c new file +0 -0
new file mode 100644
index 0000000..782fbb5

kernel/livepatch/patch.c line 6

Quoted 6 lines
kernel/livepatch/patch.c new file +6 -0
@@ -0,0 +1,213 @@
+/*
+ * patch.c - livepatch patching functions
+ *
+ * Copyright (C) 2014 Seth Jennings <[email protected]>
+ * Copyright (C) 2014 SUSE
+ * Copyright (C) 2015 Josh Poimboeuf <[email protected]
                                                         ^^^^^^

Missing '>' ;-)

Best Regards,
Petr

maintainer
#70 lore raw

Re: [RFC PATCH v2 10/18] livepatch/powerpc: add TIF_PATCH_PENDING thread flag

In reply to Petr Mladek
Quoted 60 lines · Petr Mladek
Quoted 59 lines · Josh Poimboeuf

Add the TIF_PATCH_PENDING thread flag to enable the new livepatch per-task consistency model for powerpc. The bit getting set indicates the thread has a pending patch which needs to be applied when the thread exits the kernel.

The bit is included in the _TIF_USER_WORK_MASK macro so that do_notify_resume() and klp_patch_task() get called when the bit is set.

---
 arch/powerpc/include/asm/thread_info.h | 4 +++-
 arch/powerpc/kernel/signal.c           | 4 ++++
 2 files changed, 7 insertions(+), 1 deletion(-)
arch/powerpc/include/asm/thread_info.h +3 -1
index 8febc3f..df262ca 100644
@@ -88,6 +88,7 @@ static inline struct thread_info *current_thread_info(void)
 					   TIF_NEED_RESCHED */
 #define TIF_32BIT		4	/* 32 bit binary */
 #define TIF_RESTORE_TM		5	/* need to restore TM FP/VEC/VSX */
+#define TIF_PATCH_PENDING	6	/* pending live patching update */
 #define TIF_SYSCALL_AUDIT	7	/* syscall auditing active */
 #define TIF_SINGLESTEP		8	/* singlestepping active */
 #define TIF_NOHZ		9	/* in adaptive nohz mode */
@@ -111,6 +112,7 @@ static inline struct thread_info *current_thread_info(void)
 #define _TIF_POLLING_NRFLAG	(1<<TIF_POLLING_NRFLAG)
 #define _TIF_32BIT		(1<<TIF_32BIT)
 #define _TIF_RESTORE_TM		(1<<TIF_RESTORE_TM)
+#define _TIF_PATCH_PENDING	(1<<TIF_PATCH_PENDING)
 #define _TIF_SYSCALL_AUDIT	(1<<TIF_SYSCALL_AUDIT)
 #define _TIF_SINGLESTEP		(1<<TIF_SINGLESTEP)
 #define _TIF_SECCOMP		(1<<TIF_SECCOMP)
@@ -127,7 +129,7 @@ static inline struct thread_info *current_thread_info(void)
 
 #define _TIF_USER_WORK_MASK	(_TIF_SIGPENDING | _TIF_NEED_RESCHED | \
 				 _TIF_NOTIFY_RESUME | _TIF_UPROBE | \
-				 _TIF_RESTORE_TM)
+				 _TIF_RESTORE_TM | _TIF_PATCH_PENDING)
 #define _TIF_PERSYSCALL_MASK	(_TIF_RESTOREALL|_TIF_NOERROR)
 
 /* Bits in local_flags */
arch/powerpc/kernel/signal.c +1 -0
index cb64d6f..844497b 100644
@@ -14,6 +14,7 @@
 #include <linux/uprobes.h>
 #include <linux/key.h>
 #include <linux/context_tracking.h>
+#include <linux/livepatch.h>
 #include <asm/hw_breakpoint.h>
 #include <asm/uaccess.h>
 #include <asm/unistd.h>
@@ -159,6 +160,9 @@ void do_notify_resume(struct pt_regs *regs, unsigned long thread_info_flags)
 		tracehook_notify_resume(regs);
 	}
 
Quoted 8 lines

arch/powerpc/kernel/signal.c void do_notify_resume(struct pt_regs *regs, unsigned long thread_info_flags) · line 165

Quoted 3 lines
arch/powerpc/kernel/signal.c +3 -0
+	if (thread_info_flags & _TIF_PATCH_PENDING)
+		klp_patch_task(current);
+

JFYI, if we later add the fake signal to speed up migration of sleeping task, we would need to move this up before calling do_signal(). It would help to avoid cycling here twice. Mirek surely knows more details about it.

Yes, that is true if we go with a fake signal implementation we have in kGraft. Nevertheless it is the issue of that patch so let's discuss that there (when I send it).

Miroslav

#75 lore raw

Re: [RFC PATCH v2 13/18] livepatch: separate enabled and patched states

In reply to Petr Mladek
Quoted 42 lines · Petr Mladek
Quoted 30 lines · Josh Poimboeuf

Once we have a consistency model, patches and their objects will be enabled and disabled at different times. For example, when a patch is disabled, its loaded objects' funcs can remain registered with ftrace indefinitely until the unpatching operation is complete and they're no longer in use.

It's less confusing if we give them different names: patches can be enabled or disabled; objects (and their funcs) can be patched or unpatched:

- Enabled means that a patch is logically enabled (but not necessarily fully applied).

- Patched means that an object's funcs are registered with ftrace and added to the klp_ops func stack.

Also, since these states are binary, represent them with booleans instead of ints.

---
 include/linux/livepatch.h | 17 ++++-------
 kernel/livepatch/core.c   | 72 +++++++++++++++++++++++------------------------
 2 files changed, 42 insertions(+), 47 deletions(-)
kernel/livepatch/core.c +0 -0
index 6ea6880..2b59230 100644

kernel/livepatch/core.c static ssize_t enabled_store(struct kobject *kobj, struct kobj_attribute *attr, · line 626

Quoted 6 lines
kernel/livepatch/core.c +1 -1
@@ -622,20 +622,20 @@ static ssize_t enabled_store(struct kobject *kobj, struct kobj_attribute *attr,
 	if (ret)
 		return -EINVAL;
 
-	if (val != KLP_DISABLED && val != KLP_ENABLED)
+	if (val > 1)
 		return -EINVAL;

It would be cleaner to get "val" via kstrtobool(). It guarantees that the value is true or false. Another nice win is that it accepts Y/y/1/N/n/0 as the input.

Quoted 8 lines

kernel/livepatch/core.c static ssize_t enabled_store(struct kobject *kobj, struct kobj_attribute *attr, · line 632

Quoted 6 lines
kernel/livepatch/core.c +1 -1
 	patch = container_of(kobj, struct klp_patch, kobj);
 
 	mutex_lock(&klp_mutex);
 
-	if (val == patch->state) {
+	if (patch->enabled == val) {

Also this check will be cleaner if "val" is a boolean.

Good idea, thanks.

Signature · 1 line
Josh
maintainer
#81 lore raw

Re: [RFC PATCH v2 17/18] livepatch: change to a per-task consistency model

In reply to Josh Poimboeuf
Quoted 16 lines · Josh Poimboeuf

Change livepatch to use a basic per-task consistency model. This is the foundation which will eventually enable us to patch those ~10% of security patches which change function or data semantics. This is the biggest remaining piece needed to make livepatch more generally useful.

kernel/fork.c +1 -0
@@ -76,6 +76,7 @@
 #include <linux/compiler.h>
 #include <linux/sysctl.h>
 #include <linux/kcov.h>
+#include <linux/livepatch.h>
 
 #include <asm/pgtable.h>
 #include <asm/pgalloc.h>

kernel/fork.c static struct task_struct *copy_process(unsigned long clone_flags, · line 1590

Quoted 4 lines
kernel/fork.c +1 -0
@@ -1586,6 +1587,8 @@ static struct task_struct *copy_process(unsigned long clone_flags,
 		p->parent_exec_id = current->self_exec_id;
 	}
 
+	klp_copy_process(p);

I am in doubts here. We copy the state from the parent here. It means that the new process might still need to be converted. But at the same point print_context_stack_reliable() returns zero without printing any stack trace when TIF_FORK flag is set. It means that a freshly forked task might get be converted immediately. I seems that boot operations are always done when copy_process() is called. But they are contradicting each other.

I guess that print_context_stack_reliable() should either return -EINVAL when TIF_FORK is set. Or it should try to print the stack of the newly forked task.

Or do I miss something, please?

Quoted 4 lines
kernel/fork.c +1 -0
+
 	spin_lock(&current->sighand->siglock);
 
 	/*

[...]

Quoted 17 lines
kernel/livepatch/transition.c new file +12 -0
new file mode 100644
index 0000000..92819bb
+/*
+ * This function can be called in the middle of an existing transition to
+ * reverse the direction of the target patch state.  This can be done to
+ * effectively cancel an existing enable or disable operation if there are any
+ * tasks which are stuck in the initial patch state.
+ */
+void klp_reverse_transition(void)
+{
+	struct klp_patch *patch = klp_transition_patch;
+
+	klp_target_state = !klp_target_state;
+

kernel/livepatch/transition.c line 470

Quoted 10 lines
kernel/livepatch/transition.c new file +10 -0
+	/*
+	 * Ensure that if another CPU goes through the syscall barrier, sees
+	 * the TIF_PATCH_PENDING writes in klp_start_transition(), and calls
+	 * klp_patch_task(), it also sees the above write to the target state.
+	 * Otherwise it can put the task in the wrong universe.
+	 */
+	smp_wmb();
+
+	klp_start_transition();
+	klp_try_complete_transition();

It is a bit strange that we keep the work scheduled. It might be better to use

       mod_delayed_work(system_wq, &klp_work, 0);

Which triggers more ideas from the nitpicking deparment:

I would move the work definition from core.c to transition.c because it is closely related to klp_try_complete_transition();

When on it. I would make it more clear that the work is related to transition. Also I would call queue_delayed_work() directly instead of adding the klp_schedule_work() wrapper. The delay might be defined using a constant, e.g.

#define KLP_TRANSITION_DELAY round_jiffies_relative(HZ)

queue_delayed_work(system_wq, &klp_transition_work, KLP_TRANSITION_DELAY);

Finally, the following is always called right after klp_start_transition(), so I would call it from there.

	if (!klp_try_complete_transition())
		klp_schedule_work();

kernel/livepatch/transition.c line 474

Quoted 4 lines
kernel/livepatch/transition.c new file +4 -0
+
+	patch->enabled = !patch->enabled;
+}
+

It is really great work! I am checking this patch from left, right, top, and even bottom and all seems to work well together.

Best Regards,
Petr

maintainer
#85 lore raw

barriers: was: [RFC PATCH v2 17/18] livepatch: change to a per-task consistency model

In reply to Josh Poimboeuf
Quoted 4 lines · Josh Poimboeuf

Change livepatch to use a basic per-task consistency model. This is the foundation which will eventually enable us to patch those ~10% of security patches which change function or data semantics. This is the biggest remaining piece needed to make livepatch more generally useful.

I spent a lot of time with checking the memory barriers. It seems that they are basically correct. Let me use my own words to show how I understand it. I hope that it will help others with review.

Quoted 19 lines
kernel/livepatch/patch.c +2 -0
index 782fbb5..b3b8639 100644
@@ -29,6 +29,7 @@
 #include <linux/bug.h>
 #include <linux/printk.h>
 #include "patch.h"
+#include "transition.h"
 
 static LIST_HEAD(klp_ops);
 
@@ -58,11 +59,42 @@ static void notrace klp_ftrace_handler(unsigned long ip,
 	ops = container_of(fops, struct klp_ops, fops);
 
 	rcu_read_lock();
+
 	func = list_first_or_null_rcu(&ops->func_stack, struct klp_func,
 				      stack_node);

kernel/livepatch/patch.c static void notrace klp_ftrace_handler(unsigned long ip, · line 73

Quoted 10 lines
kernel/livepatch/patch.c +7 -1
-	if (WARN_ON_ONCE(!func))
+
+	if (!func)
 		goto unlock;
 
+	/*
+	 * See the comment for the 2nd smp_wmb() in klp_init_transition() for
+	 * an explanation of why this read barrier is needed.
+	 */
+	smp_rmb();

I would prefer to be more explicit, e.g.

	/*
	 * Read the right func->transition when the struct appeared on top of
	 * func_stack.  See klp_init_transition and klp_patch_func().
	 */

Note that this barrier is not really needed when the patch is being disabled, see below.

kernel/livepatch/patch.c static void notrace klp_ftrace_handler(unsigned long ip, · line 82

Quoted 9 lines
kernel/livepatch/patch.c +9 -0
+
+	if (unlikely(func->transition)) {
+
+		/*
+		 * See the comment for the 1st smp_wmb() in
+		 * klp_init_transition() for an explanation of why this read
+		 * barrier is needed.
+		 */
+		smp_rmb();

Similar here:

/*
* Read the right initial state when func->transition was
* enabled, see klp_init_transition().
*
* Note that the task must never be migrated to the target
* state when being inside this ftrace handler. */

We might want to move the second paragraph on top of the function. It is a basic and important fact. It actually explains why the first read barrier is not needed when the patch is being disabled.

There are some more details below. I started to check and comment the barriers from klp_init_transition().

Quoted 28 lines
kernel/livepatch/patch.c +14 -0
+		if (current->patch_state == KLP_UNPATCHED) {
+			/*
+			 * Use the previously patched version of the function.
+			 * If no previous patches exist, use the original
+			 * function.
+			 */
+			func = list_entry_rcu(func->stack_node.next,
+					      struct klp_func, stack_node);
+
+			if (&func->stack_node == &ops->func_stack)
+				goto unlock;
+		}
+	}
+
 	klp_arch_set_pc(regs, (unsigned long)func->new_func);
 unlock:
 	rcu_read_unlock();
kernel/livepatch/transition.c new file +6 -0
new file mode 100644
index 0000000..92819bb
+/*
+ * klp_patch_task() - change the patched state of a task
+ * @task:	The task to change
+ *
+ * Switches the patched state of the task to the set of functions in the target
+ * patch state.

kernel/livepatch/transition.c line 58

Quoted 10 lines
kernel/livepatch/transition.c new file +10 -0
+ */
+void klp_patch_task(struct task_struct *task)
+{
+	clear_tsk_thread_flag(task, TIF_PATCH_PENDING);
+
+	/*
+	 * The corresponding write barriers are in klp_init_transition() and
+	 * klp_reverse_transition().  See the comments there for an explanation.
+	 */
+	smp_rmb();

I would prefer to be more explicit, e.g.

/*
* Read the correct klp_target_state when TIF_PATCH_PENDING was set
* and this function was called. See klp_init_transition() and
* klp_reverse_transition().
*/

kernel/livepatch/transition.c line 61

Quoted 3 lines
kernel/livepatch/transition.c new file +3 -0
+
+	task->patch_state = klp_target_state;
+}

The function name confused me few times when klp_target_state was KLP_UNPATCHED. I suggest to rename it to klp_update_task() or klp_transit_task().

Quoted 35 lines
kernel/livepatch/transition.c new file +35 -0
+/*
+ * Initialize the global target patch state and all tasks to the initial patch
+ * state, and initialize all function transition states to true in preparation
+ * for patching or unpatching.
+ */
+void klp_init_transition(struct klp_patch *patch, int state)
+{
+	struct task_struct *g, *task;
+	unsigned int cpu;
+	struct klp_object *obj;
+	struct klp_func *func;
+	int initial_state = !state;
+
+	klp_transition_patch = patch;
+
+	/*
+	 * If the patch can be applied or reverted immediately, skip the
+	 * per-task transitions.
+	 */
+	if (patch->immediate)
+		return;
+
+	/*
+	 * Initialize all tasks to the initial patch state to prepare them for
+	 * switching to the target state.
+	 */
+	read_lock(&tasklist_lock);
+	for_each_process_thread(g, task)
+		task->patch_state = initial_state;
+	read_unlock(&tasklist_lock);
+
+	/*
+	 * Ditto for the idle "swapper" tasks.
+	 */
+	get_online_cpus();

kernel/livepatch/transition.c line 107

Quoted 10 lines
kernel/livepatch/transition.c new file +10 -0
+	for_each_online_cpu(cpu)
+		idle_task(cpu)->patch_state = initial_state;
+	put_online_cpus();
+
+	/*
+	 * Ensure klp_ftrace_handler() sees the task->patch_state updates
+	 * before the func->transition updates.  Otherwise it could read an
+	 * out-of-date task state and pick the wrong function.
+	 */
+	smp_wmb();

This barrier is needed when the patch is being disabled. In this case, the ftrace handler is already in use and the related struct klp_func are on top of func_stack. The purpose is well described above.

It is not needed when the patch is being enabled because it is not visible to the ftrace handler at the moment. The barrier below is enough.

Quoted 16 lines
kernel/livepatch/transition.c new file +16 -0
+	/*
+	 * Set the func transition states so klp_ftrace_handler() will know to
+	 * switch to the transition logic.
+	 *
+	 * When patching, the funcs aren't yet in the func_stack and will be
+	 * made visible to the ftrace handler shortly by the calls to
+	 * klp_patch_object().
+	 *
+	 * When unpatching, the funcs are already in the func_stack and so are
+	 * already visible to the ftrace handler.
+	 */
+	klp_for_each_object(patch, obj)
+		klp_for_each_func(obj, func)
+			func->transition = true;
+
+	/*

kernel/livepatch/transition.c line 134

Quoted 10 lines
kernel/livepatch/transition.c new file +10 -0
+	 * Set the global target patch state which tasks will switch to.  This
+	 * has no effect until the TIF_PATCH_PENDING flags get set later.
+	 */
+	klp_target_state = state;
+
+	/*
+	 * For the enable path, ensure klp_ftrace_handler() will see the
+	 * func->transition updates before the funcs become visible to the
+	 * handler.  Otherwise the handler may wrongly pick the new func before
+	 * the task switches to the patched state.

By other words, it makes sure that the ftrace handler will see the updated func->transition before the ftrace handler is registered and/or before the struct klp_func is listed in func_stack.

kernel/livepatch/transition.c line 140

Quoted 5 lines
kernel/livepatch/transition.c new file +5 -0
+	 * For the disable path, the funcs are already visible to the handler.
+	 * But we still need to ensure the ftrace handler will see the
+	 * func->transition updates before the tasks start switching to the
+	 * unpatched state.  Otherwise the handler can miss a task patch state
+	 * change which would result in it wrongly picking the new function.

If this is true, it would mean that the task might be switched when it is in the middle of klp_ftrace_handler. It would mean that reading task->patch_state would be racy against the modification by klp_patch_task().

Note that before we call klp_patch_task(), the task will stay in the previous state. We are disabling the patch, so the previous state is that the patch is enabled. It means that it should always use the new function before klp_patch_task() is called. It means that it does not matter if it sees func->transition updated or not. In both cases, it will use the new function.

Fortunately, task->patch_state might be set to KLP_UNPATCHED only when it is sleeping or on some other safe location, e.g. when leaving to userspace. In both cases, the barrier is not needed here.

By other words, this barrier is not needed to synchronize func_stack and patch->transition when it is being disabled.

kernel/livepatch/transition.c line 147

Quoted 6 lines
kernel/livepatch/transition.c new file +6 -0
+	 * This barrier also ensures that if another CPU goes through the
+	 * syscall barrier, sees the TIF_PATCH_PENDING writes in
+	 * klp_start_transition(), and calls klp_patch_task(), it also sees the
+	 * above write to the target state.  Otherwise it can put the task in
+	 * the wrong universe.
+	 */

By other words, it makes sure that klp_patch_task() will assign the right patch_state. Where klp_patch_task() could not be called before we set TIF_PATCH_PENDING in klp_start_transition().

Quoted 20 lines
kernel/livepatch/transition.c new file +20 -0
+	smp_wmb();
+}
+
+/*
+ * Start the transition to the specified target patch state so tasks can begin
+ * switching to it.
+ */
+void klp_start_transition(void)
+{
+	struct task_struct *g, *task;
+	unsigned int cpu;
+
+	pr_notice("'%s': %s...\n", klp_transition_patch->mod->name,
+		  klp_target_state == KLP_PATCHED ? "patching" : "unpatching");
+
+	/*
+	 * If the patch can be applied or reverted immediately, skip the
+	 * per-task transitions.
+	 */
+	if (klp_transition_patch->immediate)

kernel/livepatch/transition.c line 177

Quoted 10 lines
kernel/livepatch/transition.c new file +10 -0
+		return;
+
+	/*
+	 * Mark all normal tasks as needing a patch state update.  As they pass
+	 * through the syscall barrier they'll switch over to the target state
+	 * (unless we switch them in klp_try_complete_transition() first).
+	 */
+	read_lock(&tasklist_lock);
+	for_each_process_thread(g, task)
+		set_tsk_thread_flag(task, TIF_PATCH_PENDING);

A bad intuition might suggest that we do not need to set this flag when klp_start_transition() is called from klp_reverse_transition() and the task already is in the right state.

But I think that we actually must set TIF_PATCH_PENDING even in this case to avoid a possible race. We do not know if klp_patch_task() is not running at the moment with the previous klp_target_state().

Quoted 21 lines
kernel/livepatch/transition.c new file +21 -0
+	read_unlock(&tasklist_lock);
+
+	/*
+	 * Ditto for the idle "swapper" tasks, though they never cross the
+	 * syscall barrier.  Instead they switch over in cpu_idle_loop().
+	 */
+	get_online_cpus();
+	for_each_online_cpu(cpu)
+		set_tsk_thread_flag(idle_task(cpu), TIF_PATCH_PENDING);
+	put_online_cpus();
+}
+
+/*
+ * This function can be called in the middle of an existing transition to
+ * reverse the direction of the target patch state.  This can be done to
+ * effectively cancel an existing enable or disable operation if there are any
+ * tasks which are stuck in the initial patch state.
+ */
+void klp_reverse_transition(void)
+{
+	struct klp_patch *patch = klp_transition_patch;

kernel/livepatch/transition.c line 467

Quoted 10 lines
kernel/livepatch/transition.c new file +10 -0
+
+	klp_target_state = !klp_target_state;
+
+	/*
+	 * Ensure that if another CPU goes through the syscall barrier, sees
+	 * the TIF_PATCH_PENDING writes in klp_start_transition(), and calls
+	 * klp_patch_task(), it also sees the above write to the target state.
+	 * Otherwise it can put the task in the wrong universe.
+	 */
+	smp_wmb();

Yup, it is the same reason as for the 2nd barrier in klp_init_transition() regarding klp_target_state and klp_patch_task() that is triggered by TIF_PATCH_PENDING.

kernel/livepatch/transition.c line 474

Quoted 7 lines
kernel/livepatch/transition.c new file +7 -0
+
+	klp_start_transition();
+	klp_try_complete_transition();
+
+	patch->enabled = !patch->enabled;
+}
+

Best Regards,
Petr

maintainer
#86 lore raw

Re: barriers: was: [RFC PATCH v2 17/18] livepatch: change to a per-task consistency model

Quoted 11 lines · Petr Mladek

kernel/livepatch/transition.c line 147

Quoted 6 lines
kernel/livepatch/transition.c new file +6 -0
+	 * This barrier also ensures that if another CPU goes through the
+	 * syscall barrier, sees the TIF_PATCH_PENDING writes in
+	 * klp_start_transition(), and calls klp_patch_task(), it also sees the
+	 * above write to the target state.  Otherwise it can put the task in
+	 * the wrong universe.
+	 */

By other words, it makes sure that klp_patch_task() will assign the right patch_state. Where klp_patch_task() could not be called before we set TIF_PATCH_PENDING in klp_start_transition().

Quoted 2 lines

Review

Quoted 2 lines
(quoted diff) +2 -0
+	smp_wmb();
+}

So I've not read the patch; but ending a function with an smp_wmb() feels wrong.

A wmb orders two stores, and I feel both stores should be well visible in the same function.

maintainer
#88 lore raw

Re: barriers: was: [RFC PATCH v2 17/18] livepatch: change to a per-task consistency model

In reply to Petr Mladek
Quoted 8 lines · Petr Mladek
		 *
		 * Note that the task must never be migrated to the target
		 * state when being inside this ftrace handler.
		 */

We might want to move the second paragraph on top of the function. It is a basic and important fact. It actually explains why the first read barrier is not needed when the patch is being disabled.

I wrote the statement partly intuitively. I think that it is really somehow important. And I am slightly in doubts if we are on the safe side.

First, why is it important that the task->patch_state is not switched when being inside the ftrace handler?

If we are inside the handler, we are kind-of inside the called function. And the basic idea of this consistency model is that we must not switch a task when it is inside a patched function. This is normally decided by the stack.

The handler is a bit special because it is called right before the function. If it was the only patched function on the stack, it would not matter if we choose the new or old code. Both decisions would be safe for the moment.

The fun starts when the function calls another patched function. The other patched function must be called consistently with the first one. If the first function was from the patch, the other must be from the patch as well and vice versa.

This is why we must not switch task->patch_state dangerously when being inside the ftrace handler.

Now I am not sure if this condition is fulfilled. The ftrace handler is called as the very first instruction of the function. Does not it break the stack validity? Could we sleep inside the ftrace handler? Will the patched function be detected on the stack?

Or is my brain already too far in the fantasy world?

Best regards,
Petr

maintainer
#94 lore raw

klp_task_patch: was: [RFC PATCH v2 17/18] livepatch: change to a per-task consistency model

In reply to Josh Poimboeuf
Quoted 43 lines · Josh Poimboeuf

Change livepatch to use a basic per-task consistency model. This is the foundation which will eventually enable us to patch those ~10% of security patches which change function or data semantics. This is the biggest remaining piece needed to make livepatch more generally useful.

kernel/livepatch/transition.c new file +20 -0
new file mode 100644
index 0000000..92819bb
+/*
+ * klp_patch_task() - change the patched state of a task
+ * @task:	The task to change
+ *
+ * Switches the patched state of the task to the set of functions in the target
+ * patch state.
+ */
+void klp_patch_task(struct task_struct *task)
+{
+	clear_tsk_thread_flag(task, TIF_PATCH_PENDING);
+
+	/*
+	 * The corresponding write barriers are in klp_init_transition() and
+	 * klp_reverse_transition().  See the comments there for an explanation.
+	 */
+	smp_rmb();
+
+	task->patch_state = klp_target_state;
+}
+
kernel/sched/idle.c +1 -0
index bd12c6c..60d633f 100644
@@ -9,6 +9,7 @@
 #include <linux/mm.h>
 #include <linux/stackprotector.h>
 #include <linux/suspend.h>
+#include <linux/livepatch.h>
 
 #include <asm/tlb.h>
 

kernel/sched/idle.c static void cpu_idle_loop(void) · line 273

Quoted 7 lines
kernel/sched/idle.c +3 -0
@@ -266,6 +267,9 @@ static void cpu_idle_loop(void)
 
 		sched_ttwu_pending();
 		schedule_preempt_disabled();
+
+		if (unlikely(klp_patch_pending(current)))
+			klp_patch_task(current);
 	}

Some more ideas from the world of crazy races. I was shaking my head if this was safe or not.

The problem might be if the task get rescheduled between the check for the pending stuff or inside the klp_patch_task() function. This will get even more important when we use this construct on some more locations, e.g. in some kthreads.

If the task is sleeping on this strange locations, it might assign strange values on strange times.

I think that it is safe only because it is called with the 'current' parameter and on a safe locations. It means that the result is always safe and consistent. Also we could assign an outdated value only when sleeping between reading klp_target_state and storing task->patch_state. But if anyone modified klp_target_state at this point, he also set TIF_PENDING_PATCH, so the change will not get lost.

I think that we should document that klp_patch_func() must be called only from a safe location from within the affected task.

I even suggest to avoid misuse by removing the struct *task_struct parameter. It should always be called with current.

Best Regards,
Petr

#95 lore raw

Re: klp_task_patch: was: [RFC PATCH v2 17/18] livepatch: change to a per-task consistency model

Quoted 10 lines · Petr Mladek

kernel/sched/idle.c static void cpu_idle_loop(void) · line 273

Quoted 4 lines
kernel/sched/idle.c +3 -0
+
+		if (unlikely(klp_patch_pending(current)))
+			klp_patch_task(current);
 	}

Some more ideas from the world of crazy races. I was shaking my head if this was safe or not.

The problem might be if the task get rescheduled between the check for the pending stuff

The code in question is running with preemption disabled.

Quoted 1 line

or inside the klp_patch_task() function.

We must make sure that this function doesn't go to sleep. It's only used to clear the task_struct flag anyway.

Signature · 2 lines
Jiri Kosina
SUSE Labs
#34 lore raw

RE: [RFC PATCH v2 05/18] sched: add task flag for preempt IRQ tracking

In reply to Andy Lutomirski

RnJvbTogQW5keSBMdXRvbWlyc2tpDQo+IFNlbnQ6IDAyIE1heSAyMDE2IDE5OjEzDQouLi4NCj4g SSBob3BlIHlvdXIgcGxhbnMgaW5jbHVkZSByZXdyaXRpbmcgdGhlIGN1cnJlbnQgc3RhY2sgdW53 aW5kZXINCj4gY29tcGxldGVseS4gIFRoZSB0aGluZyBpbiBwcmludF9jb250ZXh0X3N0YWNrIGlz IChhKQ0KPiBoYXJkLXRvLXVuZGVyc3RhbmQgYW5kIGhhcmQtdG8tbW9kaWZ5IGNyYXAgYW5kIChi KSBpcyBjYWxsZWQgaW4gYSBsb29wDQo+IGZyb20gYW5vdGhlciBmaWxlIHVzaW5nIHRvdGFsbHkg cmlkaWN1bG91cyBjb252ZW50aW9ucy4NCg0KSSd2ZSBzZWVuIGEgJ3N0YWNrIHVud2luZGVyJyB0 aGF0IHBhcnNlZCB0aGUgaW5zdHJ1Y3Rpb24gc3RyZWFtDQpmb3J3YXJkcyBsb29raW5nIGZvciAn cmV0dXJuJyBpbnN0cnVjdGlvbnMuDQpJIGZpeGVkIGl0IHRvIGFkZCBhIGZldyBleHRyYSBpbnN0 cnVjdGlvbnMgbmVlZHMgdG8gc29ydCBvdXQgdGhlDQpleGl0IHBhdGggZnJvbSBoYXJkd2FyZSBp bnRlcnJ1cHRzLg0KDQpJdCBvbmx5IGhhZCB0byB1bmRlcnN0YW5kIGluc3RydWN0aW9ucyB0aGF0 IG1vZGlmaWVkICVzcCBhbmQgJWJwDQphbmQgcmVtZW1iZXIgd2hpY2ggYnJhbmNoIGluc3RydWN0 aW9ucyBhbmQgYnJhbmNoIHRhcmdldHMgaXQgaGFkDQp1c2VkIGluIG9yZGVyIHRvIGZpbmQgdGhl IGNvcnJlY3QgZXhpdCBwYXRoIGZyb20gYSBmdW5jdGlvbi4NCg0KV29ya2VkIHJlYXNvbmFibHkg d2VsbCB3aXRob3V0IGFueSBkZWJ1ZyBpbmZvIG9yIGd1YXJhbnRlZWQgZnJhbWUNCnBvaW50ZXJz Lg0KSXQgZGlkIGhhdmUgdG8gZmFsbCBiYWNrIG9uIHNjYW5uaW5nIHRoZSBzdGFjayBpZiBpdCB3 YXMgaW5zaWRlDQphbiBpbmZpbml0ZSBsb29wLiBFdmVuIG9uIHg4NiBpdCBpcyByZWFzb25hYmx5 IHBvc3NpYmxlIHRvIGNoZWNrDQpmb3IgJ2NhbGwnIGluc3RydWN0aW9ucyBpbiB0aGlzIGNhc2Uu DQoNCglEYXZpZA0KDQo=

#82 lore raw

Re: [RFC PATCH v2 17/18] livepatch: change to a per-task consistency model

In reply to Petr Mladek
Quoted 30 lines · Petr Mladek
Quoted 16 lines · Josh Poimboeuf

Change livepatch to use a basic per-task consistency model. This is the foundation which will eventually enable us to patch those ~10% of security patches which change function or data semantics. This is the biggest remaining piece needed to make livepatch more generally useful.

kernel/fork.c +1 -0
@@ -76,6 +76,7 @@
 #include <linux/compiler.h>
 #include <linux/sysctl.h>
 #include <linux/kcov.h>
+#include <linux/livepatch.h>
 
 #include <asm/pgtable.h>
 #include <asm/pgalloc.h>

kernel/fork.c static struct task_struct *copy_process(unsigned long clone_flags, · line 1590

Quoted 4 lines
kernel/fork.c +1 -0
@@ -1586,6 +1587,8 @@ static struct task_struct *copy_process(unsigned long clone_flags,
 		p->parent_exec_id = current->self_exec_id;
 	}
 
+	klp_copy_process(p);

I am in doubts here. We copy the state from the parent here. It means that the new process might still need to be converted. But at the same point print_context_stack_reliable() returns zero without printing any stack trace when TIF_FORK flag is set. It means that a freshly forked task might get be converted immediately. I seems that boot operations are always done when copy_process() is called. But they are contradicting each other.

Quoted 5 lines

I guess that print_context_stack_reliable() should either return -EINVAL when TIF_FORK is set. Or it should try to print the stack of the newly forked task.

Or do I miss something, please?

Ok, I admit it's confusing.

A newly forked task doesn't *have* a stack (other than the pt_regs frame it needs for the return to user space), which is why print_context_stack_reliable() returns success with an empty array of addresses.

For a little background, see the second switch_to() macro in arch/x86/include/asm/switch_to.h. When a newly forked task runs for the first time, it returns from __switch_to() with no stack. It then jumps straight to ret_from_fork in entry_64.S, calls a few C functions, and eventually returns to user space. So, assuming we aren't patching entry code or the switch_to() macro in __schedule(), it should be safe to patch the task before it does all that.

With the current code, if an unpatched task gets forked, the child will also be unpatched. In theory, we could go ahead and patch the child then. In fact, that's what I did in v1.9.

But in v1.9 discussions it was pointed out that someday maybe the ret_from_fork stuff will get cleaned up and instead the child stack will be copied from the parent. In that case the child should inherit its parent's patched state. So we decided to make it more future-proof by having the child inherit the parent's patched state.

So, having said all that, I'm really not sure what the best approach is for print_context_stack_reliable(). Right now I'm thinking I'll change it back to return -EINVAL for a newly forked task, so it'll be more
future-proof: better to have a false positive than a false negative. Either way it will probably need to be changed again if the ret_from_fork code gets cleaned up.

Quoted 38 lines
Quoted 4 lines
kernel/fork.c +1 -0
+
 	spin_lock(&current->sighand->siglock);
 
 	/*

[...]

Quoted 17 lines
kernel/livepatch/transition.c new file +12 -0
new file mode 100644
index 0000000..92819bb
+/*
+ * This function can be called in the middle of an existing transition to
+ * reverse the direction of the target patch state.  This can be done to
+ * effectively cancel an existing enable or disable operation if there are any
+ * tasks which are stuck in the initial patch state.
+ */
+void klp_reverse_transition(void)
+{
+	struct klp_patch *patch = klp_transition_patch;
+
+	klp_target_state = !klp_target_state;
+

kernel/livepatch/transition.c line 470

Quoted 10 lines
kernel/livepatch/transition.c new file +10 -0
+	/*
+	 * Ensure that if another CPU goes through the syscall barrier, sees
+	 * the TIF_PATCH_PENDING writes in klp_start_transition(), and calls
+	 * klp_patch_task(), it also sees the above write to the target state.
+	 * Otherwise it can put the task in the wrong universe.
+	 */
+	smp_wmb();
+
+	klp_start_transition();
+	klp_try_complete_transition();

It is a bit strange that we keep the work scheduled. It might be better to use

Quoted 1 line
       mod_delayed_work(system_wq, &klp_work, 0);

True, I think that would be better.

Quoted 4 lines

Which triggers more ideas from the nitpicking deparment:

I would move the work definition from core.c to transition.c because it is closely related to klp_try_complete_transition();

That could be good, but there's a slight problem: klp_work_fn() requires klp_mutex, which is static to core.c. It's kind of nice to keep the use of the mutex in core.c only.

Quoted 2 lines

When on it. I would make it more clear that the work is related to transition.

How would you recommend doing that? How about:

- rename "klp_work" -> "klp_transition_work"
- rename "klp_work_fn" -> "klp_transition_work_fn"

?

Quoted 7 lines

Also I would call queue_delayed_work() directly instead of adding the klp_schedule_work() wrapper. The delay might be defined using a constant, e.g.

#define KLP_TRANSITION_DELAY round_jiffies_relative(HZ)

queue_delayed_work(system_wq, &klp_transition_work, KLP_TRANSITION_DELAY);

Sure.

Quoted 5 lines

Finally, the following is always called right after klp_start_transition(), so I would call it from there.

	if (!klp_try_complete_transition())
		klp_schedule_work();

Except for when it's called by klp_reverse_transition(). And it really depends on whether we want to allow transition.c to use the mutex. I don't have a strong opinion either way, I may need to think about it some more.

Quoted 7 lines

kernel/livepatch/transition.c line 474

Quoted 4 lines
kernel/livepatch/transition.c new file +4 -0
+
+	patch->enabled = !patch->enabled;
+}
+

It is really great work! I am checking this patch from left, right, top, and even bottom and all seems to work well together.

Great! Thanks a lot for the thorough review!

Signature · 1 line
Josh
#87 lore raw

Re: barriers: was: [RFC PATCH v2 17/18] livepatch: change to a per-task consistency model

In reply to Peter Zijlstra
Quoted 6 lines · Peter Zijlstra
Quoted 5 lines · Petr Mladek

kernel/livepatch/transition.c line 146

Quoted 5 lines
kernel/livepatch/transition.c new file +5 -0
+	 * This barrier also ensures that if another CPU goes through the
+	 * syscall barrier, sees the TIF_PATCH_PENDING writes in
+	 * klp_start_transition(), and calls klp_patch_task(), it also sees the
+	 * above write to the target state.  Otherwise it can put the task in
+	 * the wrong universe.

(oops, missed a "universe" -> "patch state" rename)

Quoted 6 lines
Quoted 6 lines

Review

Quoted 1 line
(quoted diff) +1 -0
+	 */

By other words, it makes sure that klp_patch_task() will assign the right patch_state. Where klp_patch_task() could not be called before we set TIF_PATCH_PENDING in klp_start_transition().

Quoted 8 lines
Quoted 2 lines

Review

Quoted 2 lines
(quoted diff) +2 -0
+	smp_wmb();
+}

So I've not read the patch; but ending a function with an smp_wmb() feels wrong.

A wmb orders two stores, and I feel both stores should be well visible in the same function.

Yeah, I would agree with that. And also, it's probably a red flag that the barrier needs *three* paragraphs to describe the various cases its needed for.

However, there are some complications:

1) The stores are in separate functions (which is a generally a good thing as it greatly helps the readability of the code).

2) Which stores are being ordered depends on whether the function is called in the enable path or the disable path.

3) Either way it actually orders *two* separate pairs of stores.

Anyway I'm thinking I should move that barrier out of klp_init_transition() and into its callers. The stores will still be in separate functions but at least there will be better visibility of where the stores are occurring, and the comments can be a little more focused.

Signature · 1 line
Josh
#92 lore raw

Re: barriers: was: [RFC PATCH v2 17/18] livepatch: change to a per-task consistency model

In reply to Petr Mladek
Quoted 9 lines · Petr Mladek
Quoted 4 lines · Josh Poimboeuf

Change livepatch to use a basic per-task consistency model. This is the foundation which will eventually enable us to patch those ~10% of security patches which change function or data semantics. This is the biggest remaining piece needed to make livepatch more generally useful.

I spent a lot of time with checking the memory barriers. It seems that they are basically correct. Let me use my own words to show how I understand it. I hope that it will help others with review.

[...snip a ton of useful comments...]

Thanks, this will help a lot! I'll try to incorporate your barrier comments into the code.

I also agree that kpatch_patch_task() is poorly named. I was trying to make it clear to external callers that "hey, the task is getting patched now!", but it's internally inconsistent with livepatch code because we make a distinction between patching and unpatching.

Maybe I'll do:

klp_update_task_patch_state()

Signature · 1 line
Josh
#89 lore raw

Re: barriers: was: [RFC PATCH v2 17/18] livepatch: change to a per-task consistency model

In reply to Petr Mladek
Quoted 34 lines · Petr Mladek
Quoted 8 lines · Petr Mladek
		 *
		 * Note that the task must never be migrated to the target
		 * state when being inside this ftrace handler.
		 */

We might want to move the second paragraph on top of the function. It is a basic and important fact. It actually explains why the first read barrier is not needed when the patch is being disabled.

I wrote the statement partly intuitively. I think that it is really somehow important. And I am slightly in doubts if we are on the safe side.

First, why is it important that the task->patch_state is not switched when being inside the ftrace handler?

If we are inside the handler, we are kind-of inside the called function. And the basic idea of this consistency model is that we must not switch a task when it is inside a patched function. This is normally decided by the stack.

The handler is a bit special because it is called right before the function. If it was the only patched function on the stack, it would not matter if we choose the new or old code. Both decisions would be safe for the moment.

The fun starts when the function calls another patched function. The other patched function must be called consistently with the first one. If the first function was from the patch, the other must be from the patch as well and vice versa.

This is why we must not switch task->patch_state dangerously when being inside the ftrace handler.

Quoted 6 lines

Now I am not sure if this condition is fulfilled. The ftrace handler is called as the very first instruction of the function. Does not it break the stack validity? Could we sleep inside the ftrace handler? Will the patched function be detected on the stack?

Or is my brain already too far in the fantasy world?

I think this isn't a possibility.

In today's code base, this can't happen because task patch states are only switched when sleeping or when exiting the kernel. The ftrace handler doesn't sleep directly.

If it were preempted, it couldn't be switched there either because we consider preempted stacks to be unreliable.

In theory, a DWARF stack trace of a preempted task *could* be reliable. But then the DWARF unwinder should be smart enough to see that the original function called the ftrace handler. Right? So the stack would be reliable, but then livepatch would see the original function on the stack and wouldn't switch the task.

Does that make sense?

Signature · 1 line
Josh
#96 lore raw

Re: klp_task_patch: was: [RFC PATCH v2 17/18] livepatch: change to a per-task consistency model

In reply to Petr Mladek
Quoted 71 lines · Petr Mladek
Quoted 43 lines · Josh Poimboeuf

Change livepatch to use a basic per-task consistency model. This is the foundation which will eventually enable us to patch those ~10% of security patches which change function or data semantics. This is the biggest remaining piece needed to make livepatch more generally useful.

kernel/livepatch/transition.c new file +20 -0
new file mode 100644
index 0000000..92819bb
+/*
+ * klp_patch_task() - change the patched state of a task
+ * @task:	The task to change
+ *
+ * Switches the patched state of the task to the set of functions in the target
+ * patch state.
+ */
+void klp_patch_task(struct task_struct *task)
+{
+	clear_tsk_thread_flag(task, TIF_PATCH_PENDING);
+
+	/*
+	 * The corresponding write barriers are in klp_init_transition() and
+	 * klp_reverse_transition().  See the comments there for an explanation.
+	 */
+	smp_rmb();
+
+	task->patch_state = klp_target_state;
+}
+
kernel/sched/idle.c +1 -0
index bd12c6c..60d633f 100644
@@ -9,6 +9,7 @@
 #include <linux/mm.h>
 #include <linux/stackprotector.h>
 #include <linux/suspend.h>
+#include <linux/livepatch.h>
 
 #include <asm/tlb.h>
 

kernel/sched/idle.c static void cpu_idle_loop(void) · line 273

Quoted 7 lines
kernel/sched/idle.c +3 -0
@@ -266,6 +267,9 @@ static void cpu_idle_loop(void)
 
 		sched_ttwu_pending();
 		schedule_preempt_disabled();
+
+		if (unlikely(klp_patch_pending(current)))
+			klp_patch_task(current);
 	}

Some more ideas from the world of crazy races. I was shaking my head if this was safe or not.

The problem might be if the task get rescheduled between the check for the pending stuff or inside the klp_patch_task() function. This will get even more important when we use this construct on some more locations, e.g. in some kthreads.

If the task is sleeping on this strange locations, it might assign strange values on strange times.

I think that it is safe only because it is called with the 'current' parameter and on a safe locations. It means that the result is always safe and consistent. Also we could assign an outdated value only when sleeping between reading klp_target_state and storing task->patch_state. But if anyone modified klp_target_state at this point, he also set TIF_PENDING_PATCH, so the change will not get lost.

Quoted 5 lines

I think that we should document that klp_patch_func() must be called only from a safe location from within the affected task.

I even suggest to avoid misuse by removing the struct *task_struct parameter. It should always be called with current.

Would the race involve two tasks trying to call klp_patch_task() for the same task at the same time? If so I don't think that would be a problem since they would both write the same value for task->patch_state.

(Sorry if I'm being slow, I think I've managed to reach my quota of hard thinking for the day and I don't exactly follow what the race would be.)

Signature · 1 line
Josh
maintainer
#83 lore raw

Re: [RFC PATCH v2 17/18] livepatch: change to a per-task consistency model

In reply to Josh Poimboeuf
Quoted 62 lines · Josh Poimboeuf
Quoted 30 lines · Petr Mladek
Quoted 16 lines · Josh Poimboeuf

Change livepatch to use a basic per-task consistency model. This is the foundation which will eventually enable us to patch those ~10% of security patches which change function or data semantics. This is the biggest remaining piece needed to make livepatch more generally useful.

kernel/fork.c +1 -0
@@ -76,6 +76,7 @@
 #include <linux/compiler.h>
 #include <linux/sysctl.h>
 #include <linux/kcov.h>
+#include <linux/livepatch.h>
 
 #include <asm/pgtable.h>
 #include <asm/pgalloc.h>

kernel/fork.c static struct task_struct *copy_process(unsigned long clone_flags, · line 1590

Quoted 4 lines
kernel/fork.c +1 -0
@@ -1586,6 +1587,8 @@ static struct task_struct *copy_process(unsigned long clone_flags,
 		p->parent_exec_id = current->self_exec_id;
 	}
 
+	klp_copy_process(p);

I am in doubts here. We copy the state from the parent here. It means that the new process might still need to be converted. But at the same point print_context_stack_reliable() returns zero without printing any stack trace when TIF_FORK flag is set. It means that a freshly forked task might get be converted immediately. I seems that boot operations are always done when copy_process() is called. But they are contradicting each other.

Quoted 5 lines

I guess that print_context_stack_reliable() should either return -EINVAL when TIF_FORK is set. Or it should try to print the stack of the newly forked task.

Or do I miss something, please?

Ok, I admit it's confusing.

A newly forked task doesn't *have* a stack (other than the pt_regs frame it needs for the return to user space), which is why print_context_stack_reliable() returns success with an empty array of addresses.

For a little background, see the second switch_to() macro in arch/x86/include/asm/switch_to.h. When a newly forked task runs for the first time, it returns from __switch_to() with no stack. It then jumps straight to ret_from_fork in entry_64.S, calls a few C functions, and eventually returns to user space. So, assuming we aren't patching entry code or the switch_to() macro in __schedule(), it should be safe to patch the task before it does all that.

With the current code, if an unpatched task gets forked, the child will also be unpatched. In theory, we could go ahead and patch the child then. In fact, that's what I did in v1.9.

But in v1.9 discussions it was pointed out that someday maybe the ret_from_fork stuff will get cleaned up and instead the child stack will be copied from the parent. In that case the child should inherit its parent's patched state. So we decided to make it more future-proof by having the child inherit the parent's patched state.

Quoted 6 lines

So, having said all that, I'm really not sure what the best approach is for print_context_stack_reliable(). Right now I'm thinking I'll change it back to return -EINVAL for a newly forked task, so it'll be more
future-proof: better to have a false positive than a false negative. Either way it will probably need to be changed again if the ret_from_fork code gets cleaned up.

I'd be for returning -EINVAL. It is a safe play for now.

[...]

Quoted 5 lines
Quoted 5 lines

Finally, the following is always called right after klp_start_transition(), so I would call it from there.

	if (!klp_try_complete_transition())
		klp_schedule_work();

On the other hand it is quite nice to see the sequence

init
start
try_complete

there. Just my 2 cents.

Quoted 4 lines

Except for when it's called by klp_reverse_transition(). And it really depends on whether we want to allow transition.c to use the mutex. I don't have a strong opinion either way, I may need to think about it some more.

Miroslav

maintainer
#93 lore raw

Re: barriers: was: [RFC PATCH v2 17/18] livepatch: change to a per-task consistency model

In reply to Josh Poimboeuf
Quoted 7 lines · Josh Poimboeuf
Quoted 6 lines · Petr Mladek
Quoted 4 lines · Josh Poimboeuf

Change livepatch to use a basic per-task consistency model. This is the foundation which will eventually enable us to patch those ~10% of security patches which change function or data semantics. This is the biggest remaining piece needed to make livepatch more generally useful.

Quoted 8 lines
Quoted 3 lines

I spent a lot of time with checking the memory barriers. It seems that they are basically correct. Let me use my own words to show how I understand it. I hope that it will help others with review.

[...snip a ton of useful comments...]

Thanks, this will help a lot! I'll try to incorporate your barrier comments into the code.

Thanks a lot.

Quoted 8 lines

I also agree that kpatch_patch_task() is poorly named. I was trying to make it clear to external callers that "hey, the task is getting patched now!", but it's internally inconsistent with livepatch code because we make a distinction between patching and unpatching.

Maybe I'll do:

klp_update_task_patch_state()

I like it. It is long but it well describes the purpose.

Livepatch is using many state variables:

+ global: klp_transition_patch, klp_target_state
+ per task specific: TIF_PENDING_PATCH, patch_state
+ per each new function: transition, patched

  + per old function:      func_stack
  + per object:            patched, loaded
  + per patch:             enabled

The dependency between them and the workflow is important to create a mental picture about the Livepatching. Good names help with it.

Best Regards,
Petr

maintainer
#90 lore raw

Re: barriers: was: [RFC PATCH v2 17/18] livepatch: change to a per-task consistency model

In reply to Josh Poimboeuf
Quoted 42 lines · Josh Poimboeuf
Quoted 34 lines · Petr Mladek
Quoted 8 lines · Petr Mladek
		 *
		 * Note that the task must never be migrated to the target
		 * state when being inside this ftrace handler.
		 */

We might want to move the second paragraph on top of the function. It is a basic and important fact. It actually explains why the first read barrier is not needed when the patch is being disabled.

I wrote the statement partly intuitively. I think that it is really somehow important. And I am slightly in doubts if we are on the safe side.

First, why is it important that the task->patch_state is not switched when being inside the ftrace handler?

If we are inside the handler, we are kind-of inside the called function. And the basic idea of this consistency model is that we must not switch a task when it is inside a patched function. This is normally decided by the stack.

The handler is a bit special because it is called right before the function. If it was the only patched function on the stack, it would not matter if we choose the new or old code. Both decisions would be safe for the moment.

The fun starts when the function calls another patched function. The other patched function must be called consistently with the first one. If the first function was from the patch, the other must be from the patch as well and vice versa.

This is why we must not switch task->patch_state dangerously when being inside the ftrace handler.

Quoted 6 lines

Now I am not sure if this condition is fulfilled. The ftrace handler is called as the very first instruction of the function. Does not it break the stack validity? Could we sleep inside the ftrace handler? Will the patched function be detected on the stack?

Or is my brain already too far in the fantasy world?

Quoted 8 lines

I think this isn't a possibility.

In today's code base, this can't happen because task patch states are only switched when sleeping or when exiting the kernel. The ftrace handler doesn't sleep directly.

If it were preempted, it couldn't be switched there either because we consider preempted stacks to be unreliable.

This was the missing piece.

Quoted 7 lines

In theory, a DWARF stack trace of a preempted task *could* be reliable. But then the DWARF unwinder should be smart enough to see that the original function called the ftrace handler. Right? So the stack would be reliable, but then livepatch would see the original function on the stack and wouldn't switch the task.

Does that make sense?

Yup. I think that we are on the safe side. Thanks for explanation.

Best Regards,
Petr

maintainer
#97 lore raw

Re: klp_task_patch: was: [RFC PATCH v2 17/18] livepatch: change to a per-task consistency model

In reply to Josh Poimboeuf
Quoted 75 lines · Josh Poimboeuf
Quoted 74 lines · Petr Mladek
Quoted 43 lines · Josh Poimboeuf

Change livepatch to use a basic per-task consistency model. This is the foundation which will eventually enable us to patch those ~10% of security patches which change function or data semantics. This is the biggest remaining piece needed to make livepatch more generally useful.

kernel/livepatch/transition.c new file +20 -0
new file mode 100644
index 0000000..92819bb
+/*
+ * klp_patch_task() - change the patched state of a task
+ * @task:	The task to change
+ *
+ * Switches the patched state of the task to the set of functions in the target
+ * patch state.
+ */
+void klp_patch_task(struct task_struct *task)
+{
+	clear_tsk_thread_flag(task, TIF_PATCH_PENDING);
+
+	/*
+	 * The corresponding write barriers are in klp_init_transition() and
+	 * klp_reverse_transition().  See the comments there for an explanation.
+	 */
+	smp_rmb();
+
+	task->patch_state = klp_target_state;
+}
+
kernel/sched/idle.c +1 -0
index bd12c6c..60d633f 100644
@@ -9,6 +9,7 @@
 #include <linux/mm.h>
 #include <linux/stackprotector.h>
 #include <linux/suspend.h>
+#include <linux/livepatch.h>
 
 #include <asm/tlb.h>
 

kernel/sched/idle.c static void cpu_idle_loop(void) · line 273

Quoted 7 lines
kernel/sched/idle.c +3 -0
@@ -266,6 +267,9 @@ static void cpu_idle_loop(void)
 
 		sched_ttwu_pending();
 		schedule_preempt_disabled();
+
+		if (unlikely(klp_patch_pending(current)))
+			klp_patch_task(current);
 	}

Some more ideas from the world of crazy races. I was shaking my head if this was safe or not.

The problem might be if the task get rescheduled between the check for the pending stuff or inside the klp_patch_task() function. This will get even more important when we use this construct on some more locations, e.g. in some kthreads.

If the task is sleeping on this strange locations, it might assign strange values on strange times.

I think that it is safe only because it is called with the 'current' parameter and on a safe locations. It means that the result is always safe and consistent. Also we could assign an outdated value only when sleeping between reading klp_target_state and storing task->patch_state. But if anyone modified klp_target_state at this point, he also set TIF_PENDING_PATCH, so the change will not get lost.

I think that we should document that klp_patch_func() must be called only from a safe location from within the affected task.

Quoted 6 lines
Quoted 2 lines

I even suggest to avoid misuse by removing the struct *task_struct parameter. It should always be called with current.

Would the race involve two tasks trying to call klp_patch_task() for the same task at the same time? If so I don't think that would be a problem since they would both write the same value for task->patch_state.

I have missed that the two commands are called with preemption disabled. So, I had the following crazy scenario in mind:

CPU0 CPU1

klp_enable_patch()

  klp_target_state = KLP_PATCHED;

for_each_task()
set TIF_PENDING_PATCH

# task 123

if (klp_patch_pending(current)
klp_patch_task(current)

clear TIF_PENDING_PATCH

smp_rmb();

# switch to assembly of # klp_patch_task()

mov klp_target_state, %r12

# interrupt and schedule # another task

  klp_reverse_transition();
    klp_target_state = KLP_UNPATCHED;

    klt_try_to_complete_transition()

      task = 123;
      if (task->patch_state == klp_target_state;
         return 0;

    => task 123 is in target state and does
    not block conversion

klp_complete_transition()

  # disable previous patch on the stack
  klp_disable_patch();

klp_target_state = KLP_UNPATCHED;

# task 123 gets scheduled again lea %r12, task->patch_state

=> it happily stores an outdated state

This is why the two functions should get called with preemption disabled. We should document it at least. I imagine that we will use them later also in another context and nobody will remember this crazy scenario.

Well, even disabled preemption does not help. The process on CPU1 might be also interrupted by an NMI and do some long printk in it.

IMHO, the only safe approach is to call klp_patch_task() only for "current" on a safe place. Then this race is harmless. The switch happen on a safe place, so that it does not matter into which state the process is switched.

By other words, the task state might be updated only

+ by the task itself on a safe place
+ by other task when the updated on is sleeping on a safe place

This should be well documented and the API should help to avoid a misuse.

Best Regards,
Petr

maintainer
#84 lore raw

Re: [RFC PATCH v2 17/18] livepatch: change to a per-task consistency model

In reply to Josh Poimboeuf
Quoted 44 lines · Josh Poimboeuf
Quoted 30 lines · Petr Mladek
Quoted 16 lines · Josh Poimboeuf

Change livepatch to use a basic per-task consistency model. This is the foundation which will eventually enable us to patch those ~10% of security patches which change function or data semantics. This is the biggest remaining piece needed to make livepatch more generally useful.

kernel/fork.c +1 -0
@@ -76,6 +76,7 @@
 #include <linux/compiler.h>
 #include <linux/sysctl.h>
 #include <linux/kcov.h>
+#include <linux/livepatch.h>
 
 #include <asm/pgtable.h>
 #include <asm/pgalloc.h>

kernel/fork.c static struct task_struct *copy_process(unsigned long clone_flags, · line 1590

Quoted 4 lines
kernel/fork.c +1 -0
@@ -1586,6 +1587,8 @@ static struct task_struct *copy_process(unsigned long clone_flags,
 		p->parent_exec_id = current->self_exec_id;
 	}
 
+	klp_copy_process(p);

I am in doubts here. We copy the state from the parent here. It means that the new process might still need to be converted. But at the same point print_context_stack_reliable() returns zero without printing any stack trace when TIF_FORK flag is set. It means that a freshly forked task might get be converted immediately. I seems that boot operations are always done when copy_process() is called. But they are contradicting each other.

Quoted 5 lines

I guess that print_context_stack_reliable() should either return -EINVAL when TIF_FORK is set. Or it should try to print the stack of the newly forked task.

Or do I miss something, please?

Ok, I admit it's confusing.

A newly forked task doesn't *have* a stack (other than the pt_regs frame it needs for the return to user space), which is why print_context_stack_reliable() returns success with an empty array of addresses.

Quoted 7 lines

For a little background, see the second switch_to() macro in arch/x86/include/asm/switch_to.h. When a newly forked task runs for the first time, it returns from __switch_to() with no stack. It then jumps straight to ret_from_fork in entry_64.S, calls a few C functions, and eventually returns to user space. So, assuming we aren't patching entry code or the switch_to() macro in __schedule(), it should be safe to patch the task before it does all that.

This is great explanation. Thanks for it.

Quoted 6 lines

So, having said all that, I'm really not sure what the best approach is for print_context_stack_reliable(). Right now I'm thinking I'll change it back to return -EINVAL for a newly forked task, so it'll be more
future-proof: better to have a false positive than a false negative. Either way it will probably need to be changed again if the ret_from_fork code gets cleaned up.

I would prefer the -EINVAL. It might safe some hairs when anyone is working on patching the switch_to stuff. Also it is not that big loss beacuse most tasks will get migrated on the return to userspace.

It might help a bit with the newly forked kthreads. But there should be more safe location where the new kthreads might get migrated, e.g. right before the main function gets called.

Quoted 35 lines
Quoted 31 lines
Quoted 17 lines
kernel/livepatch/transition.c new file +12 -0
new file mode 100644
index 0000000..92819bb
+/*
+ * This function can be called in the middle of an existing transition to
+ * reverse the direction of the target patch state.  This can be done to
+ * effectively cancel an existing enable or disable operation if there are any
+ * tasks which are stuck in the initial patch state.
+ */
+void klp_reverse_transition(void)
+{
+	struct klp_patch *patch = klp_transition_patch;
+
+	klp_target_state = !klp_target_state;
+

kernel/livepatch/transition.c line 470

Quoted 10 lines
kernel/livepatch/transition.c new file +10 -0
+	/*
+	 * Ensure that if another CPU goes through the syscall barrier, sees
+	 * the TIF_PATCH_PENDING writes in klp_start_transition(), and calls
+	 * klp_patch_task(), it also sees the above write to the target state.
+	 * Otherwise it can put the task in the wrong universe.
+	 */
+	smp_wmb();
+
+	klp_start_transition();
+	klp_try_complete_transition();

It is a bit strange that we keep the work scheduled. It might be better to use

Quoted 1 line
       mod_delayed_work(system_wq, &klp_work, 0);

True, I think that would be better.

Quoted 8 lines
Quoted 4 lines

Which triggers more ideas from the nitpicking deparment:

I would move the work definition from core.c to transition.c because it is closely related to klp_try_complete_transition();

That could be good, but there's a slight problem: klp_work_fn() requires klp_mutex, which is static to core.c. It's kind of nice to keep the use of the mutex in core.c only.

I see and am surprised that we take the lock only in core.c ;-)

I do not have a strong opinion then. Just a small one. The lock guards also operations from the other .c files. I think that it is only a matter of time when we will need to access it there. But the work is clearly transition-related. But it is a real nitpicking. I am sorry for it.

Quoted 7 lines
Quoted 2 lines

When on it. I would make it more clear that the work is related to transition.

How would you recommend doing that? How about:

- rename "klp_work" -> "klp_transition_work"
- rename "klp_work_fn" -> "klp_transition_work_fn"

Yup, sounds better.

Quoted 13 lines
Quoted 7 lines

Also I would call queue_delayed_work() directly instead of adding the klp_schedule_work() wrapper. The delay might be defined using a constant, e.g.

#define KLP_TRANSITION_DELAY round_jiffies_relative(HZ)

queue_delayed_work(system_wq, &klp_transition_work, KLP_TRANSITION_DELAY);

Sure.

Quoted 3 lines

Finally, the following is always called right after klp_start_transition(), so I would call it from there.

Quoted 7 lines
Quoted 2 lines
	if (!klp_try_complete_transition())
		klp_schedule_work();

Except for when it's called by klp_reverse_transition(). And it really depends on whether we want to allow transition.c to use the mutex. I don't have a strong opinion either way, I may need to think about it some more.

Ah, I had in mind that it could be replaced by that

	mod_delayed_work(system_wq, &klp_transition_work, 0);

So, that we would never call klp_try_complete_transition() directly. Then it could be the same in all situations. But it might look strange and be ineffective when really starting the transition. So, maybe forget about it.

Best Regards,
Petr

maintainer
#102 lore raw

Re: [RFC PATCH v2 17/18] livepatch: change to a per-task consistency model

In reply to Josh Poimboeuf
Quoted 28 lines · Josh Poimboeuf
kernel/livepatch/patch.c +8 -1
index 782fbb5..b3b8639 100644
@@ -29,6 +29,7 @@
 #include <linux/bug.h>
 #include <linux/printk.h>
 #include "patch.h"
+#include "transition.h"
 
 static LIST_HEAD(klp_ops);
 
@@ -58,11 +59,42 @@ static void notrace klp_ftrace_handler(unsigned long ip,
 	ops = container_of(fops, struct klp_ops, fops);
 
 	rcu_read_lock();
+
 	func = list_first_or_null_rcu(&ops->func_stack, struct klp_func,
 				      stack_node);
-	if (WARN_ON_ONCE(!func))
+
+	if (!func)
 		goto unlock;
 
+	/*
+	 * See the comment for the 2nd smp_wmb() in klp_init_transition() for
+	 * an explanation of why this read barrier is needed.
+	 */

kernel/livepatch/patch.c static void notrace klp_ftrace_handler(unsigned long ip, · line 82

Quoted 10 lines
kernel/livepatch/patch.c +10 -0
+	smp_rmb();
+
+	if (unlikely(func->transition)) {
+
+		/*
+		 * See the comment for the 1st smp_wmb() in
+		 * klp_init_transition() for an explanation of why this read
+		 * barrier is needed.
+		 */
+		smp_rmb();

I would add here:

		WARN_ON_ONCE(current->patch_state == KLP_UNDEFINED);

We do not know in which context this is called, so the printk's are not ideal. But it will get triggered only if there is a bug in the livepatch implementation. It should happen on random locations and rather early when a bug is introduced.

Anyway, better to die and catch the bug that let the system running in an undefined state and produce cryptic errors later on.

kernel/livepatch/patch.c static void notrace klp_ftrace_handler(unsigned long ip, · line 96

Quoted 13 lines
kernel/livepatch/patch.c +13 -0
+		if (current->patch_state == KLP_UNPATCHED) {
+			/*
+			 * Use the previously patched version of the function.
+			 * If no previous patches exist, use the original
+			 * function.
+			 */
+			func = list_entry_rcu(func->stack_node.next,
+					      struct klp_func, stack_node);
+
+			if (&func->stack_node == &ops->func_stack)
+				goto unlock;
+		}
+	}

I am staring into the code for too long now. I need to step back for a while. I'll do another look when you send the next version. Anyway, you did a great work. I speak mainly for the livepatch part and I like it.

Best Regards,
Petr

#98 lore raw

Re: klp_task_patch: was: [RFC PATCH v2 17/18] livepatch: change to a per-task consistency model

In reply to Petr Mladek
Quoted 50 lines · Petr Mladek

I have missed that the two commands are called with preemption disabled. So, I had the following crazy scenario in mind:

CPU0 CPU1

klp_enable_patch()

  klp_target_state = KLP_PATCHED;

for_each_task()
set TIF_PENDING_PATCH

# task 123

if (klp_patch_pending(current)
klp_patch_task(current)

clear TIF_PENDING_PATCH

smp_rmb();

# switch to assembly of # klp_patch_task()

mov klp_target_state, %r12

# interrupt and schedule # another task

  klp_reverse_transition();
    klp_target_state = KLP_UNPATCHED;

    klt_try_to_complete_transition()

      task = 123;
      if (task->patch_state == klp_target_state;
         return 0;

    => task 123 is in target state and does
    not block conversion

klp_complete_transition()

  # disable previous patch on the stack
  klp_disable_patch();
Quoted 9 lines

klp_target_state = KLP_UNPATCHED;

# task 123 gets scheduled again lea %r12, task->patch_state

=> it happily stores an outdated state

Thanks for the clear explanation, this helps a lot.

Quoted 5 lines

This is why the two functions should get called with preemption disabled. We should document it at least. I imagine that we will use them later also in another context and nobody will remember this crazy scenario.

Quoted 8 lines

Well, even disabled preemption does not help. The process on CPU1 might be also interrupted by an NMI and do some long printk in it.

IMHO, the only safe approach is to call klp_patch_task() only for "current" on a safe place. Then this race is harmless. The switch happen on a safe place, so that it does not matter into which state the process is switched.

I'm not sure about this solution. When klp_complete_transition() is called, we need all tasks to be patched, for good. We don't want any of them to randomly switch to the wrong state at some later time in the middle of a future patch operation. How would changing klp_patch_task() to only use "current" prevent that?

Quoted 7 lines

By other words, the task state might be updated only

+ by the task itself on a safe place
+ by other task when the updated on is sleeping on a safe place

This should be well documented and the API should help to avoid a misuse.

I think we could fix it to be safe for future callers who might not have preemption disabled with a couple of changes to klp_patch_task():
disabling preemption and testing/clearing the TIF_PATCH_PENDING flag before changing the patch state:

  void klp_patch_task(struct task_struct *task)
  {
  	preempt_disable();
  
  	if (test_and_clear_tsk_thread_flag(task, TIF_PATCH_PENDING))
  		task->patch_state = READ_ONCE(klp_target_state);
  
  	preempt_enable();
  }

We would also need a synchronize_sched() after the patching is complete, either at the end of klp_try_complete_transition() or in klp_complete_transition(). That would make sure that all existing calls to klp_patch_task() are done.

Signature · 1 line
Josh
#103 lore raw

Re: [RFC PATCH v2 17/18] livepatch: change to a per-task consistency model

In reply to Petr Mladek
Quoted 44 lines · Petr Mladek
Quoted 28 lines · Josh Poimboeuf
kernel/livepatch/patch.c +8 -1
index 782fbb5..b3b8639 100644
@@ -29,6 +29,7 @@
 #include <linux/bug.h>
 #include <linux/printk.h>
 #include "patch.h"
+#include "transition.h"
 
 static LIST_HEAD(klp_ops);
 
@@ -58,11 +59,42 @@ static void notrace klp_ftrace_handler(unsigned long ip,
 	ops = container_of(fops, struct klp_ops, fops);
 
 	rcu_read_lock();
+
 	func = list_first_or_null_rcu(&ops->func_stack, struct klp_func,
 				      stack_node);
-	if (WARN_ON_ONCE(!func))
+
+	if (!func)
 		goto unlock;
 
+	/*
+	 * See the comment for the 2nd smp_wmb() in klp_init_transition() for
+	 * an explanation of why this read barrier is needed.
+	 */

kernel/livepatch/patch.c static void notrace klp_ftrace_handler(unsigned long ip, · line 82

Quoted 10 lines
kernel/livepatch/patch.c +10 -0
+	smp_rmb();
+
+	if (unlikely(func->transition)) {
+
+		/*
+		 * See the comment for the 1st smp_wmb() in
+		 * klp_init_transition() for an explanation of why this read
+		 * barrier is needed.
+		 */
+		smp_rmb();

I would add here:

		WARN_ON_ONCE(current->patch_state == KLP_UNDEFINED);
Quoted 7 lines

We do not know in which context this is called, so the printk's are not ideal. But it will get triggered only if there is a bug in the livepatch implementation. It should happen on random locations and rather early when a bug is introduced.

Anyway, better to die and catch the bug that let the system running in an undefined state and produce cryptic errors later on.

Ok, makes sense.

Quoted 10 lines
Quoted 10 lines
kernel/livepatch/patch.c +10 -0
+		if (current->patch_state == KLP_UNPATCHED) {
+			/*
+			 * Use the previously patched version of the function.
+			 * If no previous patches exist, use the original
+			 * function.
+			 */
+			func = list_entry_rcu(func->stack_node.next,
+					      struct klp_func, stack_node);
+
+			if (&func->stack_node == &ops->func_stack)
Quoted 8 lines

kernel/livepatch/patch.c static void notrace klp_ftrace_handler(unsigned long ip, · line 96

Quoted 3 lines
kernel/livepatch/patch.c +3 -0
+				goto unlock;
+		}
+	}

I am staring into the code for too long now. I need to step back for a while. I'll do another look when you send the next version. Anyway, you did a great work. I speak mainly for the livepatch part and I like it.

Thanks for the helpful reviews! I'll be on vacation again next week so I get a break too :-)

Signature · 1 line
Josh
maintainer
#104 lore raw

Re: [RFC PATCH v2 17/18] livepatch: change to a per-task consistency model

In reply to Josh Poimboeuf

[...]

Quoted 1 line

+static int klp_target_state;

[...]

Quoted 51 lines
kernel/livepatch/transition.c new file +51 -0
+void klp_init_transition(struct klp_patch *patch, int state)
+{
+	struct task_struct *g, *task;
+	unsigned int cpu;
+	struct klp_object *obj;
+	struct klp_func *func;
+	int initial_state = !state;
+
+	klp_transition_patch = patch;
+
+	/*
+	 * If the patch can be applied or reverted immediately, skip the
+	 * per-task transitions.
+	 */
+	if (patch->immediate)
+		return;
+
+	/*
+	 * Initialize all tasks to the initial patch state to prepare them for
+	 * switching to the target state.
+	 */
+	read_lock(&tasklist_lock);
+	for_each_process_thread(g, task)
+		task->patch_state = initial_state;
+	read_unlock(&tasklist_lock);
+
+	/*
+	 * Ditto for the idle "swapper" tasks.
+	 */
+	get_online_cpus();
+	for_each_online_cpu(cpu)
+		idle_task(cpu)->patch_state = initial_state;
+	put_online_cpus();
+
+	/*
+	 * Ensure klp_ftrace_handler() sees the task->patch_state updates
+	 * before the func->transition updates.  Otherwise it could read an
+	 * out-of-date task state and pick the wrong function.
+	 */
+	smp_wmb();
+
+	/*
+	 * Set the func transition states so klp_ftrace_handler() will know to
+	 * switch to the transition logic.
+	 *
+	 * When patching, the funcs aren't yet in the func_stack and will be
+	 * made visible to the ftrace handler shortly by the calls to
+	 * klp_patch_object().
+	 *
+	 * When unpatching, the funcs are already in the func_stack and so are
+	 * already visible to the ftrace handler.

kernel/livepatch/transition.c line 128

Quoted 10 lines
kernel/livepatch/transition.c new file +10 -0
+	 */
+	klp_for_each_object(patch, obj)
+		klp_for_each_func(obj, func)
+			func->transition = true;
+
+	/*
+	 * Set the global target patch state which tasks will switch to.  This
+	 * has no effect until the TIF_PATCH_PENDING flags get set later.
+	 */
+	klp_target_state = state;

I am afraid there is a problem for (patch->immediate == true) patches. klp_target_state is not set for those and the comment is not entirely true, because klp_target_state has an effect in several places.

[...]

kernel/livepatch/transition.c line 161

Quoted 7 lines
kernel/livepatch/transition.c new file +7 -0
+void klp_start_transition(void)
+{
+	struct task_struct *g, *task;
+	unsigned int cpu;
+
+	pr_notice("'%s': %s...\n", klp_transition_patch->mod->name,
+		  klp_target_state == KLP_PATCHED ? "patching" : "unpatching");

Here...

Quoted 8 lines
kernel/livepatch/transition.c new file +8 -0
+
+	/*
+	 * If the patch can be applied or reverted immediately, skip the
+	 * per-task transitions.
+	 */
+	if (klp_transition_patch->immediate)
+		return;
+

[...]

Quoted 40 lines
kernel/livepatch/transition.c new file +40 -0
+bool klp_try_complete_transition(void)
+{
+	unsigned int cpu;
+	struct task_struct *g, *task;
+	bool complete = true;
+
+	/*
+	 * If the patch can be applied or reverted immediately, skip the
+	 * per-task transitions.
+	 */
+	if (klp_transition_patch->immediate)
+		goto success;
+
+	/*
+	 * Try to switch the tasks to the target patch state by walking their
+	 * stacks and looking for any to-be-patched or to-be-unpatched
+	 * functions.  If such functions are found on a stack, or if the stack
+	 * is deemed unreliable, the task can't be switched yet.
+	 *
+	 * Usually this will transition most (or all) of the tasks on a system
+	 * unless the patch includes changes to a very common function.
+	 */
+	read_lock(&tasklist_lock);
+	for_each_process_thread(g, task)
+		if (!klp_try_switch_task(task))
+			complete = false;
+	read_unlock(&tasklist_lock);
+
+	/*
+	 * Ditto for the idle "swapper" tasks.
+	 */
+	get_online_cpus();
+	for_each_online_cpu(cpu)
+		if (!klp_try_switch_task(idle_task(cpu)))
+			complete = false;
+	put_online_cpus();
+
+	/*
+	 * Some tasks weren't able to be switched over.  Try again later and/or
+	 * wait for other methods like syscall barrier switching.

kernel/livepatch/transition.c line 427

Quoted 10 lines
kernel/livepatch/transition.c new file +10 -0
+	 */
+	if (!complete)
+		return false;
+
+success:
+	/*
+	 * When unpatching, all tasks have transitioned to KLP_UNPATCHED so we
+	 * can now remove the new functions from the func_stack.
+	 */
+	if (klp_target_state == KLP_UNPATCHED) {

Here (this is the most important one I think).

Quoted 4 lines
kernel/livepatch/transition.c new file +4 -0
+		klp_unpatch_objects(klp_transition_patch);
+
+		/*
+		 * Don't allow any existing instances of ftrace handlers to

kernel/livepatch/transition.c line 441

Quoted 10 lines
kernel/livepatch/transition.c new file +10 -0
+		 * access any obsolete funcs before we reset the func
+		 * transition states to false.  Otherwise the handler may see
+		 * the deleted "new" func, see that it's not in transition, and
+		 * wrongly pick the new version of the function.
+		 */
+		synchronize_rcu();
+	}
+
+	pr_notice("'%s': %s complete\n", klp_transition_patch->mod->name,
+		  klp_target_state == KLP_PATCHED ? "patching" : "unpatching");

Here

Quoted 8 lines
kernel/livepatch/transition.c new file +8 -0
+
+	/* we're done, now cleanup the data structures */
+	klp_complete_transition();
+
+	return true;
+}
+
+/*

kernel/livepatch/transition.c line 459

Quoted 10 lines
kernel/livepatch/transition.c new file +10 -0
+ * This function can be called in the middle of an existing transition to
+ * reverse the direction of the target patch state.  This can be done to
+ * effectively cancel an existing enable or disable operation if there are any
+ * tasks which are stuck in the initial patch state.
+ */
+void klp_reverse_transition(void)
+{
+	struct klp_patch *patch = klp_transition_patch;
+
+	klp_target_state = !klp_target_state;

And probably here.

All other references look safe.

I guess we need to set klp_target_state even for immediate patches. Should we also initialize it with KLP_UNDEFINED and set it to KLP_UNDEFINED in klp_complete_transition()?

Miroslav

maintainer
#99 lore raw

Re: klp_task_patch: was: [RFC PATCH v2 17/18] livepatch: change to a per-task consistency model

In reply to Josh Poimboeuf
Quoted 77 lines · Josh Poimboeuf
Quoted 50 lines · Petr Mladek

I have missed that the two commands are called with preemption disabled. So, I had the following crazy scenario in mind:

CPU0 CPU1

klp_enable_patch()

  klp_target_state = KLP_PATCHED;

for_each_task()
set TIF_PENDING_PATCH

# task 123

if (klp_patch_pending(current)
klp_patch_task(current)

clear TIF_PENDING_PATCH

smp_rmb();

# switch to assembly of # klp_patch_task()

mov klp_target_state, %r12

# interrupt and schedule # another task

  klp_reverse_transition();
    klp_target_state = KLP_UNPATCHED;

    klt_try_to_complete_transition()

      task = 123;
      if (task->patch_state == klp_target_state;
         return 0;

    => task 123 is in target state and does
    not block conversion

klp_complete_transition()

  # disable previous patch on the stack
  klp_disable_patch();
Quoted 9 lines

klp_target_state = KLP_UNPATCHED;

# task 123 gets scheduled again lea %r12, task->patch_state

=> it happily stores an outdated state

Thanks for the clear explanation, this helps a lot.

Quoted 5 lines

This is why the two functions should get called with preemption disabled. We should document it at least. I imagine that we will use them later also in another context and nobody will remember this crazy scenario.

Quoted 8 lines

Well, even disabled preemption does not help. The process on CPU1 might be also interrupted by an NMI and do some long printk in it.

IMHO, the only safe approach is to call klp_patch_task() only for "current" on a safe place. Then this race is harmless. The switch happen on a safe place, so that it does not matter into which state the process is switched.

Quoted 5 lines

I'm not sure about this solution. When klp_complete_transition() is called, we need all tasks to be patched, for good. We don't want any of them to randomly switch to the wrong state at some later time in the middle of a future patch operation. How would changing klp_patch_task() to only use "current" prevent that?

You are right that it is pity but it really should be safe because it is not entirely random.

If the race happens and assign an outdated value, there are two situations:

1. It is assigned when there is not transition in the progress. Then it is OK because it will be ignored by the ftrace handler. The right state will be set before the next transition starts.

2. It is assigned when some other transition is in progress. Then it is OK as long as the function is called from "current". The "wrong" state will be used consistently. It will switch to the right state on another safe state.

Quoted 13 lines
Quoted 7 lines

By other words, the task state might be updated only

+ by the task itself on a safe place
+ by other task when the updated on is sleeping on a safe place

This should be well documented and the API should help to avoid a misuse.

I think we could fix it to be safe for future callers who might not have preemption disabled with a couple of changes to klp_patch_task():
disabling preemption and testing/clearing the TIF_PATCH_PENDING flag before changing the patch state:

Quoted 9 lines
  void klp_patch_task(struct task_struct *task)
  {
  	preempt_disable();
  
  	if (test_and_clear_tsk_thread_flag(task, TIF_PATCH_PENDING))
  		task->patch_state = READ_ONCE(klp_target_state);
  
  	preempt_enable();
  }

It reduces the race window a bit but it is still there. For example, NMI still might add a huge delay between reading klp_target_state and assigning task->patch state.

What about the following?

/*
* This function might assign an outdated value if the transaction
`* is reverted and finalized in parallel. But it is safe. If the value
* is assigned outside of a transaction, it is ignored and the next
* transaction will set the right one. Or if it gets assigned
* inside another transaction, it will repeat the cycle and
* set the right state.
*/
void klp_update_current_patch_state()

{
	while (test_and_clear_tsk_thread_flag(current, TIF_PATCH_PENDING))
		current->patch_state = READ_ONCE(klp_target_state);
}

Note that the disabled preemption helped only partially, so I think that it was not really needed.

Hmm, it means that the task->patch_state might be either KLP_PATCHED or KLP_UNPATCHED outside a transition. I wonder if the tristate really brings some advantages.

Alternatively, we might synchronize the operation with klp_mutex. The function is called in a slow path and in a safe context. Well, it might cause contention on the lock when many CPUs are trying to update their tasks.

Best Regards,
Petr

maintainer
#91 lore raw

Re: barriers: was: [RFC PATCH v2 17/18] livepatch: change to a per-task consistency model

In reply to Josh Poimboeuf
Quoted 42 lines · Josh Poimboeuf
Quoted 34 lines · Petr Mladek
Quoted 8 lines · Petr Mladek
		 *
		 * Note that the task must never be migrated to the target
		 * state when being inside this ftrace handler.
		 */

We might want to move the second paragraph on top of the function. It is a basic and important fact. It actually explains why the first read barrier is not needed when the patch is being disabled.

I wrote the statement partly intuitively. I think that it is really somehow important. And I am slightly in doubts if we are on the safe side.

First, why is it important that the task->patch_state is not switched when being inside the ftrace handler?

If we are inside the handler, we are kind-of inside the called function. And the basic idea of this consistency model is that we must not switch a task when it is inside a patched function. This is normally decided by the stack.

The handler is a bit special because it is called right before the function. If it was the only patched function on the stack, it would not matter if we choose the new or old code. Both decisions would be safe for the moment.

The fun starts when the function calls another patched function. The other patched function must be called consistently with the first one. If the first function was from the patch, the other must be from the patch as well and vice versa.

This is why we must not switch task->patch_state dangerously when being inside the ftrace handler.

Quoted 6 lines

Now I am not sure if this condition is fulfilled. The ftrace handler is called as the very first instruction of the function. Does not it break the stack validity? Could we sleep inside the ftrace handler? Will the patched function be detected on the stack?

Or is my brain already too far in the fantasy world?

Quoted 8 lines

I think this isn't a possibility.

In today's code base, this can't happen because task patch states are only switched when sleeping or when exiting the kernel. The ftrace handler doesn't sleep directly.

If it were preempted, it couldn't be switched there either because we consider preempted stacks to be unreliable.

And IIRC ftrace handlers cannot sleep and are called with preemption disabled as of now. The code is a bit obscure, but see __ftrace_ops_list_func for example. This is "main" ftrace handler that calls all the registered ones in case FTRACE_OPS_FL_DYNAMIC is set (which is always true for handlers coming from modules) and CONFIG_PREEMPT is on. If it is off and there is only one handler registered for a function dynamic trampoline is used. See commit 12cce594fa8f ("ftrace/x86: Allow !CONFIG_PREEMPT dynamic ops to use allocated trampolines"). I think Steven had a plan to implement dynamic trampolines even for CONFIG_PREEMPT case but he still hasn't done it. It should use RCU_TASKS infrastructure.

The reason for all the mess is that ftrace needs to be sure that no task is in the handler when the handler/trampoline is freed.

So we should be safe for now even from this side.

Miroslav

maintainer
#106 lore raw

Re: [RFC PATCH v2 17/18] livepatch: change to a per-task consistency model

In reply to Josh Poimboeuf
Quoted 49 lines · Josh Poimboeuf

Change livepatch to use a basic per-task consistency model. This is the foundation which will eventually enable us to patch those ~10% of security patches which change function or data semantics. This is the biggest remaining piece needed to make livepatch more generally useful.

This code stems from the design proposal made by Vojtech [1] in November 2014. It's a hybrid of kGraft and kpatch: it uses kGraft's per-task consistency and syscall barrier switching combined with kpatch's stack trace switching. There are also a number of fallback options which make it quite flexible.

Patches are applied on a per-task basis, when the task is deemed safe to switch over. When a patch is enabled, livepatch enters into a transition state where tasks are converging to the patched state. Usually this transition state can complete in a few seconds. The same sequence occurs when a patch is disabled, except the tasks converge from the patched state to the unpatched state.

An interrupt handler inherits the patched state of the task it interrupts. The same is true for forked tasks: the child inherits the patched state of the parent.

Livepatch uses several complementary approaches to determine when it's safe to patch tasks:

1. The first and most effective approach is stack checking of sleeping tasks. If no affected functions are on the stack of a given task, the task is patched. In most cases this will patch most or all of the tasks on the first try. Otherwise it'll keep trying periodically. This option is only available if the architecture has reliable stacks (CONFIG_RELIABLE_STACKTRACE and CONFIG_STACK_VALIDATION).

2. The second approach, if needed, is kernel exit switching. A task is switched when it returns to user space from a system call, a user space IRQ, or a signal. It's useful in the following cases:

a) Patching I/O-bound user tasks which are sleeping on an affected function. In this case you have to send SIGSTOP and SIGCONT to force it to exit the kernel and be patched.
b) Patching CPU-bound user tasks. If the task is highly CPU-bound then it will get patched the next time it gets interrupted by an IRQ.
c) Applying patches for architectures which don't yet have CONFIG_RELIABLE_STACKTRACE. In this case you'll have to signal most of the tasks on the system. However this isn't a complete solution, because there's currently no way to patch kthreads without CONFIG_RELIABLE_STACKTRACE.

Quoted 2 lines

Note: since idle "swapper" tasks don't ever exit the kernel, they instead have a kpatch_patch_task() call in the idle loop which allows

s/kpatch_patch_task()/klp_patch_task()/

[...]

Quoted 67 lines
Documentation/livepatch/livepatch.txt +39 -15
@@ -72,7 +72,8 @@ example, they add a NULL pointer or a boundary check, fix a race by adding
 a missing memory barrier, or add some locking around a critical section.
 Most of these changes are self contained and the function presents itself
 the same way to the rest of the system. In this case, the functions might
-be updated independently one by one.
+be updated independently one by one.  (This can be done by setting the
+'immediate' flag in the klp_patch struct.)
 
 But there are more complex fixes. For example, a patch might change
 ordering of locking in multiple functions at the same time. Or a patch
@@ -86,20 +87,103 @@ or no data are stored in the modified structures at the moment.
 The theory about how to apply functions a safe way is rather complex.
 The aim is to define a so-called consistency model. It attempts to define
 conditions when the new implementation could be used so that the system
-stays consistent. The theory is not yet finished. See the discussion at
-http://thread.gmane.org/gmane.linux.kernel/1823033/focus=1828189
-
-The current consistency model is very simple. It guarantees that either
-the old or the new function is called. But various functions get redirected
-one by one without any synchronization.
-
-In other words, the current implementation _never_ modifies the behavior
-in the middle of the call. It is because it does _not_ rewrite the entire
-function in the memory. Instead, the function gets redirected at the
-very beginning. But this redirection is used immediately even when
-some other functions from the same patch have not been redirected yet.
-
-See also the section "Limitations" below.
+stays consistent.
+
+Livepatch has a consistency model which is a hybrid of kGraft and
+kpatch:  it uses kGraft's per-task consistency and syscall barrier
+switching combined with kpatch's stack trace switching.  There are also
+a number of fallback options which make it quite flexible.
+
+Patches are applied on a per-task basis, when the task is deemed safe to
+switch over.  When a patch is enabled, livepatch enters into a
+transition state where tasks are converging to the patched state.
+Usually this transition state can complete in a few seconds.  The same
+sequence occurs when a patch is disabled, except the tasks converge from
+the patched state to the unpatched state.
+
+An interrupt handler inherits the patched state of the task it
+interrupts.  The same is true for forked tasks: the child inherits the
+patched state of the parent.
+
+Livepatch uses several complementary approaches to determine when it's
+safe to patch tasks:
+
+1. The first and most effective approach is stack checking of sleeping
+   tasks.  If no affected functions are on the stack of a given task,
+   the task is patched.  In most cases this will patch most or all of
+   the tasks on the first try.  Otherwise it'll keep trying
+   periodically.  This option is only available if the architecture has
+   reliable stacks (CONFIG_RELIABLE_STACKTRACE and
+   CONFIG_STACK_VALIDATION).
+
+2. The second approach, if needed, is kernel exit switching.  A
+   task is switched when it returns to user space from a system call, a
+   user space IRQ, or a signal.  It's useful in the following cases:
+
+   a) Patching I/O-bound user tasks which are sleeping on an affected
+      function.  In this case you have to send SIGSTOP and SIGCONT to
+      force it to exit the kernel and be patched.
+   b) Patching CPU-bound user tasks.  If the task is highly CPU-bound

Documentation/livepatch/livepatch.txt or no data are stored in the modified structures at the moment. · line 136

Quoted 10 lines
Documentation/livepatch/livepatch.txt +10 -0
+      then it will get patched the next time it gets interrupted by an
+      IRQ.
+   c) Applying patches for architectures which don't yet have
+      CONFIG_RELIABLE_STACKTRACE.  In this case you'll have to signal
+      most of the tasks on the system.  However this isn't a complete
+      solution, because there's currently no way to patch kthreads
+      without CONFIG_RELIABLE_STACKTRACE.
+
+   Note: since idle "swapper" tasks don't ever exit the kernel, they
+   instead have a kpatch_patch_task() call in the idle loop which allows

s/kpatch_patch_task()/klp_patch_task()/

Otherwise all the code that touches livepatch looks good to me. Apart from the things mentioned in another emails.

Miroslav

#105 lore raw

Re: [RFC PATCH v2 17/18] livepatch: change to a per-task consistency model

In reply to Miroslav Benes
Quoted 57 lines · Miroslav Benes
Quoted 57 lines
kernel/livepatch/transition.c new file +57 -0
+void klp_init_transition(struct klp_patch *patch, int state)
+{
+	struct task_struct *g, *task;
+	unsigned int cpu;
+	struct klp_object *obj;
+	struct klp_func *func;
+	int initial_state = !state;
+
+	klp_transition_patch = patch;
+
+	/*
+	 * If the patch can be applied or reverted immediately, skip the
+	 * per-task transitions.
+	 */
+	if (patch->immediate)
+		return;
+
+	/*
+	 * Initialize all tasks to the initial patch state to prepare them for
+	 * switching to the target state.
+	 */
+	read_lock(&tasklist_lock);
+	for_each_process_thread(g, task)
+		task->patch_state = initial_state;
+	read_unlock(&tasklist_lock);
+
+	/*
+	 * Ditto for the idle "swapper" tasks.
+	 */
+	get_online_cpus();
+	for_each_online_cpu(cpu)
+		idle_task(cpu)->patch_state = initial_state;
+	put_online_cpus();
+
+	/*
+	 * Ensure klp_ftrace_handler() sees the task->patch_state updates
+	 * before the func->transition updates.  Otherwise it could read an
+	 * out-of-date task state and pick the wrong function.
+	 */
+	smp_wmb();
+
+	/*
+	 * Set the func transition states so klp_ftrace_handler() will know to
+	 * switch to the transition logic.
+	 *
+	 * When patching, the funcs aren't yet in the func_stack and will be
+	 * made visible to the ftrace handler shortly by the calls to
+	 * klp_patch_object().
+	 *
+	 * When unpatching, the funcs are already in the func_stack and so are
+	 * already visible to the ftrace handler.
+	 */
+	klp_for_each_object(patch, obj)
+		klp_for_each_func(obj, func)
+			func->transition = true;
+
+	/*
Quoted 8 lines

kernel/livepatch/transition.c line 128

Quoted 4 lines
kernel/livepatch/transition.c new file +4 -0
+	 * Set the global target patch state which tasks will switch to.  This
+	 * has no effect until the TIF_PATCH_PENDING flags get set later.
+	 */
+	klp_target_state = state;

I am afraid there is a problem for (patch->immediate == true) patches. klp_target_state is not set for those and the comment is not entirely true, because klp_target_state has an effect in several places.

Ah, you're right. I moved this assignment here for v2. It was originally done before the patch->immediate check. If I remember correctly, I moved it closer to the barrier for better readability (but I created a bug in the process).

Quoted 3 lines

I guess we need to set klp_target_state even for immediate patches. Should we also initialize it with KLP_UNDEFINED and set it to KLP_UNDEFINED in klp_complete_transition()?

Yes, to both.

Signature · 1 line
Josh
#100 lore raw

Re: klp_task_patch: was: [RFC PATCH v2 17/18] livepatch: change to a per-task consistency model

In reply to Petr Mladek
Quoted 90 lines · Petr Mladek
Quoted 77 lines · Josh Poimboeuf
Quoted 50 lines · Petr Mladek

I have missed that the two commands are called with preemption disabled. So, I had the following crazy scenario in mind:

CPU0 CPU1

klp_enable_patch()

  klp_target_state = KLP_PATCHED;

for_each_task()
set TIF_PENDING_PATCH

# task 123

if (klp_patch_pending(current)
klp_patch_task(current)

clear TIF_PENDING_PATCH

smp_rmb();

# switch to assembly of # klp_patch_task()

mov klp_target_state, %r12

# interrupt and schedule # another task

  klp_reverse_transition();
    klp_target_state = KLP_UNPATCHED;

    klt_try_to_complete_transition()

      task = 123;
      if (task->patch_state == klp_target_state;
         return 0;

    => task 123 is in target state and does
    not block conversion

klp_complete_transition()

  # disable previous patch on the stack
  klp_disable_patch();
Quoted 9 lines

klp_target_state = KLP_UNPATCHED;

# task 123 gets scheduled again lea %r12, task->patch_state

=> it happily stores an outdated state

Thanks for the clear explanation, this helps a lot.

Quoted 5 lines

This is why the two functions should get called with preemption disabled. We should document it at least. I imagine that we will use them later also in another context and nobody will remember this crazy scenario.

Quoted 8 lines

Well, even disabled preemption does not help. The process on CPU1 might be also interrupted by an NMI and do some long printk in it.

IMHO, the only safe approach is to call klp_patch_task() only for "current" on a safe place. Then this race is harmless. The switch happen on a safe place, so that it does not matter into which state the process is switched.

Quoted 5 lines

I'm not sure about this solution. When klp_complete_transition() is called, we need all tasks to be patched, for good. We don't want any of them to randomly switch to the wrong state at some later time in the middle of a future patch operation. How would changing klp_patch_task() to only use "current" prevent that?

You are right that it is pity but it really should be safe because it is not entirely random.

If the race happens and assign an outdated value, there are two situations:

Quoted 8 lines

1. It is assigned when there is not transition in the progress. Then it is OK because it will be ignored by the ftrace handler. The right state will be set before the next transition starts.

2. It is assigned when some other transition is in progress. Then it is OK as long as the function is called from "current". The "wrong" state will be used consistently. It will switch to the right state on another safe state.

Maybe it would be safe, though I'm not entirely convinced. Regardless I think we should avoid these situations entirely because they create windows for future bugs and races.

Quoted 20 lines
Quoted 20 lines
Quoted 7 lines

By other words, the task state might be updated only

+ by the task itself on a safe place
+ by other task when the updated on is sleeping on a safe place

This should be well documented and the API should help to avoid a misuse.

I think we could fix it to be safe for future callers who might not have preemption disabled with a couple of changes to klp_patch_task():
disabling preemption and testing/clearing the TIF_PATCH_PENDING flag before changing the patch state:

  void klp_patch_task(struct task_struct *task)
  {
  	preempt_disable();
  
  	if (test_and_clear_tsk_thread_flag(task, TIF_PATCH_PENDING))
  		task->patch_state = READ_ONCE(klp_target_state);
Quoted 6 lines
Quoted 2 lines
  	preempt_enable();
  }

It reduces the race window a bit but it is still there. For example, NMI still might add a huge delay between reading klp_target_state and assigning task->patch state.

Maybe you missed this paragraph from my last email:

| We would also need a synchronize_sched() after the patching is complete,
| either at the end of klp_try_complete_transition() or in
| klp_complete_transition().  That would make sure that all existing calls
| to klp_patch_task() are done.

So a huge NMI delay wouldn't be a problem here. The call to synchronize_sched() in klp_complete_transition() would sleep until the NMI handler returns and the critical section of klp_patch_task() finishes. So once a patch is complete, we know that it's really complete.

Quoted 11 lines

What about the following?

/*
* This function might assign an outdated value if the transaction
`* is reverted and finalized in parallel. But it is safe. If the value
* is assigned outside of a transaction, it is ignored and the next
* transaction will set the right one. Or if it gets assigned
* inside another transaction, it will repeat the cycle and
* set the right state.
*/
void klp_update_current_patch_state()

Quoted 4 lines
{
	while (test_and_clear_tsk_thread_flag(current, TIF_PATCH_PENDING))
		current->patch_state = READ_ONCE(klp_target_state);
}

I'm not sure how this would work. How would the thread flag get set again after it's been cleared?

Also I really don't like the idea of randomly updating a task's patch state after the transition has been completed.

Quoted 8 lines

Note that the disabled preemption helped only partially, so I think that it was not really needed.

Hmm, it means that the task->patch_state might be either KLP_PATCHED or KLP_UNPATCHED outside a transition. I wonder if the tristate really brings some advantages.

Quoted 4 lines

Alternatively, we might synchronize the operation with klp_mutex. The function is called in a slow path and in a safe context. Well, it might cause contention on the lock when many CPUs are trying to update their tasks.

I don't think a mutex would work because at least the ftrace handler (and maybe more) can't sleep. Maybe a spinlock could work but I think that would be overkill.

Signature · 1 line
Josh
#107 lore raw

Re: livepatch: change to a per-task consistency model

In reply to Josh Poimboeuf

+++ Josh Poimboeuf [28/04/16 15:44 -0500]:

[snip]

Quoted 58 lines
Documentation/livepatch/livepatch.txt +0 -0
index 6c43f6e..bee86d0 100644
@@ -72,7 +72,8 @@ example, they add a NULL pointer or a boundary check, fix a race by adding

a missing memory barrier, or add some locking around a critical section. Most of these changes are self contained and the function presents itself the same way to the rest of the system. In this case, the functions might

Documentation/livepatch/livepatch.txt +2 -1
-be updated independently one by one.
+be updated independently one by one.  (This can be done by setting the
+'immediate' flag in the klp_patch struct.)

But there are more complex fixes. For example, a patch might change ordering of locking in multiple functions at the same time. Or a patch

Documentation/livepatch/livepatch.txt +0 -0
@@ -86,20 +87,103 @@ or no data are stored in the modified structures at the moment.

The theory about how to apply functions a safe way is rather complex. The aim is to define a so-called consistency model. It attempts to define conditions when the new implementation could be used so that the system

Documentation/livepatch/livepatch.txt +26 -14
-stays consistent. The theory is not yet finished. See the discussion at
-http://thread.gmane.org/gmane.linux.kernel/1823033/focus=1828189
-
-The current consistency model is very simple. It guarantees that either
-the old or the new function is called. But various functions get redirected
-one by one without any synchronization.
-
-In other words, the current implementation _never_ modifies the behavior
-in the middle of the call. It is because it does _not_ rewrite the entire
-function in the memory. Instead, the function gets redirected at the
-very beginning. But this redirection is used immediately even when
-some other functions from the same patch have not been redirected yet.
-
-See also the section "Limitations" below.
+stays consistent.
+
+Livepatch has a consistency model which is a hybrid of kGraft and
+kpatch:  it uses kGraft's per-task consistency and syscall barrier
+switching combined with kpatch's stack trace switching.  There are also
+a number of fallback options which make it quite flexible.
+
+Patches are applied on a per-task basis, when the task is deemed safe to
+switch over.  When a patch is enabled, livepatch enters into a
+transition state where tasks are converging to the patched state.
+Usually this transition state can complete in a few seconds.  The same
+sequence occurs when a patch is disabled, except the tasks converge from
+the patched state to the unpatched state.
+
+An interrupt handler inherits the patched state of the task it
+interrupts.  The same is true for forked tasks: the child inherits the
+patched state of the parent.
+
+Livepatch uses several complementary approaches to determine when it's
+safe to patch tasks:
+
+1. The first and most effective approach is stack checking of sleeping
+   tasks.  If no affected functions are on the stack of a given task,
+   the task is patched.  In most cases this will patch most or all of
+   the tasks on the first try.  Otherwise it'll keep trying
+   periodically.  This option is only available if the architecture has

Documentation/livepatch/livepatch.txt or no data are stored in the modified structures at the moment. · line 125

Quoted 10 lines
Documentation/livepatch/livepatch.txt +10 -0
+   reliable stacks (CONFIG_RELIABLE_STACKTRACE and
+   CONFIG_STACK_VALIDATION).
+
+2. The second approach, if needed, is kernel exit switching.  A
+   task is switched when it returns to user space from a system call, a
+   user space IRQ, or a signal.  It's useful in the following cases:
+
+   a) Patching I/O-bound user tasks which are sleeping on an affected
+      function.  In this case you have to send SIGSTOP and SIGCONT to
+      force it to exit the kernel and be patched.

See below -

Quoted 51 lines
Documentation/livepatch/livepatch.txt +51 -0
+   b) Patching CPU-bound user tasks.  If the task is highly CPU-bound
+      then it will get patched the next time it gets interrupted by an
+      IRQ.
+   c) Applying patches for architectures which don't yet have
+      CONFIG_RELIABLE_STACKTRACE.  In this case you'll have to signal
+      most of the tasks on the system.  However this isn't a complete
+      solution, because there's currently no way to patch kthreads
+      without CONFIG_RELIABLE_STACKTRACE.
+
+   Note: since idle "swapper" tasks don't ever exit the kernel, they
+   instead have a kpatch_patch_task() call in the idle loop which allows
+   them to patched before the CPU enters the idle state.
+
+3. A third approach (not yet implemented) is planned for the case where
+   a kthread is sleeping on an affected function.  In that case we could
+   kick the kthread with a signal and then try to patch the task from
+   the to-be-patched function's livepatch ftrace handler when it
+   re-enters the function.  This will require
+   CONFIG_RELIABLE_STACKTRACE.
+
+All the above approaches may be skipped by setting the 'immediate' flag
+in the 'klp_patch' struct, which will patch all tasks immediately.  This
+can be useful if the patch doesn't change any function or data
+semantics.  Note that, even with this flag set, it's possible that some
+tasks may still be running with an old version of the function, until
+that function returns.
+
+There's also an 'immediate' flag in the 'klp_func' struct which allows
+you to specify that certain functions in the patch can be applied
+without per-task consistency.  This might be useful if you want to patch
+a common function like schedule(), and the function change doesn't need
+consistency but the rest of the patch does.
+
+For architectures which don't have CONFIG_RELIABLE_STACKTRACE, there
+are two options:
+
+a) the user can set the patch->immediate flag which causes all tasks to
+   be patched immediately.  This option should be used with care, only
+   when the patch doesn't change any function or data semantics; or
+
+b) use the kernel exit switching approach (this is the default).
+   Note the patching will never complete because there's no currently no
+   way to patch kthreads without CONFIG_RELIABLE_STACKTRACE.
+
+The /sys/kernel/livepatch/<patch>/transition file shows whether a patch
+is in transition.  Only a single patch (the topmost patch on the stack)
+can be in transition at a given time.  A patch can remain in transition
+indefinitely, if any of the tasks are stuck in the initial patch state.
+
+A transition can be reversed and effectively canceled by writing the
+opposite value to the /sys/kernel/livepatch/<patch>/enabled file while

Documentation/livepatch/livepatch.txt or no data are stored in the modified structures at the moment. · line 186

Quoted 10 lines
Documentation/livepatch/livepatch.txt +10 -0
+the transition is in progress.  Then all the tasks will attempt to
+converge back to the original patch state.
+
+There's also a /proc/<pid>/patch_state file which can be used to
+determine which tasks are blocking completion of a patching operation.
+If a patch is in transition, this file shows 0 to indicate the task is
+unpatched and 1 to indicate it's patched.  Otherwise, if no patch is in
+transition, it shows -1. Any tasks which are blocking the transition
+can be signaled with SIGSTOP and SIGCONT to force them to change their
+patched state.

What about tasks sleeping on affected functions in uninterruptible sleep (possibly indefinitely)? Since all signals are ignored, we wouldn't be able to patch those tasks in this way, right? Would that be an unsupported case? Might be useful to mention this in the documentation somewhere.

Jessica

#108 lore raw

Re: livepatch: change to a per-task consistency model

Quoted 4 lines · Jessica Yu

What about tasks sleeping on affected functions in uninterruptible sleep (possibly indefinitely)? Since all signals are ignored, we wouldn't be able to patch those tasks in this way, right? Would that be an unsupported case?

I don't think there is any better way out of this situation than documenting that the convergence of patching could in such cases could take quite a lot of time (well, we can pro-actively try to detect this situation before the patching actually start, and warn about the possible consequences).

But let's face it, this should be pretty uncommon, because (a) it's not realistic for the wait times to be really indefinite (b) the task is likely to be in TASK_KILLABLE rather than just plain TASK_UNINTERRUPTIBLE.

Signature · 2 lines
Jiri Kosina
SUSE Labs
maintainer
#101 lore raw

Re: klp_task_patch: was: [RFC PATCH v2 17/18] livepatch: change to a per-task consistency model

In reply to Josh Poimboeuf
Quoted 95 lines · Josh Poimboeuf
Quoted 94 lines · Petr Mladek
Quoted 77 lines · Josh Poimboeuf
Quoted 50 lines · Petr Mladek

I have missed that the two commands are called with preemption disabled. So, I had the following crazy scenario in mind:

CPU0 CPU1

klp_enable_patch()

  klp_target_state = KLP_PATCHED;

for_each_task()
set TIF_PENDING_PATCH

# task 123

if (klp_patch_pending(current)
klp_patch_task(current)

clear TIF_PENDING_PATCH

smp_rmb();

# switch to assembly of # klp_patch_task()

mov klp_target_state, %r12

# interrupt and schedule # another task

  klp_reverse_transition();
    klp_target_state = KLP_UNPATCHED;

    klt_try_to_complete_transition()

      task = 123;
      if (task->patch_state == klp_target_state;
         return 0;

    => task 123 is in target state and does
    not block conversion

klp_complete_transition()

  # disable previous patch on the stack
  klp_disable_patch();
Quoted 9 lines

klp_target_state = KLP_UNPATCHED;

# task 123 gets scheduled again lea %r12, task->patch_state

=> it happily stores an outdated state

Thanks for the clear explanation, this helps a lot.

Quoted 5 lines

This is why the two functions should get called with preemption disabled. We should document it at least. I imagine that we will use them later also in another context and nobody will remember this crazy scenario.

Quoted 8 lines

Well, even disabled preemption does not help. The process on CPU1 might be also interrupted by an NMI and do some long printk in it.

IMHO, the only safe approach is to call klp_patch_task() only for "current" on a safe place. Then this race is harmless. The switch happen on a safe place, so that it does not matter into which state the process is switched.

Quoted 5 lines

I'm not sure about this solution. When klp_complete_transition() is called, we need all tasks to be patched, for good. We don't want any of them to randomly switch to the wrong state at some later time in the middle of a future patch operation. How would changing klp_patch_task() to only use "current" prevent that?

You are right that it is pity but it really should be safe because it is not entirely random.

If the race happens and assign an outdated value, there are two situations:

1. It is assigned when there is not transition in the progress. Then it is OK because it will be ignored by the ftrace handler. The right state will be set before the next transition starts.

Quoted 8 lines
Quoted 4 lines

2. It is assigned when some other transition is in progress. Then it is OK as long as the function is called from "current". The "wrong" state will be used consistently. It will switch to the right state on another safe state.

Maybe it would be safe, though I'm not entirely convinced. Regardless I think we should avoid these situations entirely because they create windows for future bugs and races.

Yup, I would prefer a cleaner solution as well.

Quoted 34 lines
Quoted 20 lines
Quoted 20 lines
Quoted 7 lines

By other words, the task state might be updated only

+ by the task itself on a safe place
+ by other task when the updated on is sleeping on a safe place

This should be well documented and the API should help to avoid a misuse.

I think we could fix it to be safe for future callers who might not have preemption disabled with a couple of changes to klp_patch_task():
disabling preemption and testing/clearing the TIF_PATCH_PENDING flag before changing the patch state:

  void klp_patch_task(struct task_struct *task)
  {
  	preempt_disable();
  
  	if (test_and_clear_tsk_thread_flag(task, TIF_PATCH_PENDING))
  		task->patch_state = READ_ONCE(klp_target_state);
Quoted 6 lines
Quoted 2 lines
  	preempt_enable();
  }

It reduces the race window a bit but it is still there. For example, NMI still might add a huge delay between reading klp_target_state and assigning task->patch state.

Maybe you missed this paragraph from my last email:

| We would also need a synchronize_sched() after the patching is complete,
| either at the end of klp_try_complete_transition() or in
| klp_complete_transition().  That would make sure that all existing calls
| to klp_patch_task() are done.
Quoted 5 lines

So a huge NMI delay wouldn't be a problem here. The call to synchronize_sched() in klp_complete_transition() would sleep until the NMI handler returns and the critical section of klp_patch_task() finishes. So once a patch is complete, we know that it's really complete.

Yes, synchronize_sched() will help with the premeption disabled. I did not shake my head enough last time.

Quoted 16 lines
Quoted 11 lines

What about the following?

/*
* This function might assign an outdated value if the transaction
`* is reverted and finalized in parallel. But it is safe. If the value
* is assigned outside of a transaction, it is ignored and the next
* transaction will set the right one. Or if it gets assigned
* inside another transaction, it will repeat the cycle and
* set the right state.
*/
void klp_update_current_patch_state()

Quoted 4 lines
{
	while (test_and_clear_tsk_thread_flag(current, TIF_PATCH_PENDING))
		current->patch_state = READ_ONCE(klp_target_state);
}
Quoted 2 lines

I'm not sure how this would work. How would the thread flag get set again after it's been cleared?

See the race described in the previous mail. The problem is when the target_state and the TIF flags gets set after reading klp_target_state into a register and before storing the value into current->patch_state.

We do not need this if use the synchronize_sched() and fix up current->patch_state then.

Quoted 11 lines

Also I really don't like the idea of randomly updating a task's patch state after the transition has been completed.

Quoted 8 lines

Note that the disabled preemption helped only partially, so I think that it was not really needed.

Hmm, it means that the task->patch_state might be either KLP_PATCHED or KLP_UNPATCHED outside a transition. I wonder if the tristate really brings some advantages.

Quoted 8 lines
Quoted 4 lines

Alternatively, we might synchronize the operation with klp_mutex. The function is called in a slow path and in a safe context. Well, it might cause contention on the lock when many CPUs are trying to update their tasks.

I don't think a mutex would work because at least the ftrace handler (and maybe more) can't sleep. Maybe a spinlock could work but I think that would be overkill.

Sure, I had a spinlock in mind.

Best Regards,
Petr

#109 lore raw

Re: livepatch: change to a per-task consistency model

In reply to Jiri Kosina
Quoted 13 lines · Jiri Kosina
Quoted 4 lines · Jessica Yu

What about tasks sleeping on affected functions in uninterruptible sleep (possibly indefinitely)? Since all signals are ignored, we wouldn't be able to patch those tasks in this way, right? Would that be an unsupported case?

I don't think there is any better way out of this situation than documenting that the convergence of patching could in such cases could take quite a lot of time (well, we can pro-actively try to detect this situation before the patching actually start, and warn about the possible consequences).

Quoted 3 lines

But let's face it, this should be pretty uncommon, because (a) it's not realistic for the wait times to be really indefinite (b) the task is likely to be in TASK_KILLABLE rather than just plain TASK_UNINTERRUPTIBLE.

Yeah, I think this situation -- a task sleeping on an affected function in uninterruptible state for a long period of time -- would be exceedingly rare and not something we need to worry about for now.

Signature · 1 line
Josh
#110 lore raw

Re: livepatch: change to a per-task consistency model

Quoted 3 lines · Josh Poimboeuf

Yeah, I think this situation -- a task sleeping on an affected function in uninterruptible state for a long period of time -- would be exceedingly rare and not something we need to worry about for now.

Plus in case task'd be in TASK_UNINTERRUPTIBLE for more than 120s, hung task detector would trigger anyway.

Signature · 2 lines
Jiri Kosina
SUSE Labs
#35 lore raw

Re: [RFC PATCH v2 05/18] sched: add task flag for preempt IRQ tracking

In reply to Andy Lutomirski
Quoted 82 lines · Andy Lutomirski
Quoted 44 lines · Josh Poimboeuf
Quoted 28 lines · Andy Lutomirski
Quoted 19 lines · Josh Poimboeuf
Quoted 9 lines · Andy Lutomirski
Quoted 5 lines · Josh Poimboeuf
Quoted 2 lines

I suppose we could try to rejigger the code so that rbp points to pt_regs or similar.

I think we should avoid doing something like that because it would break gdb and all the other unwinders who don't know about it.

How so?

Quoted 7 lines

Currently, rbp in the entry code is meaningless. I'm suggesting that, when we do, for example, 'call \do_sym' in idtentry, we point rbp to the pt_regs. Currently it points to something stale (which the dump_stack code might be relying on. Hmm.) But it's probably also safe to assume that if you unwind to the 'call \do_sym', then pt_regs is the next thing on the stack, so just doing the section thing would work.

Quoted 5 lines

Yes, rbp is meaningless on the entry from user space. But if an in-kernel interrupt occurs (e.g. page fault, preemption) and you have nested entry, rbp keeps its old value, right? So the unwinder can walk past the nested entry frame and keep going until it gets to the original entry.

Yes.

Quoted 8 lines

It would be nice if we could do better, though, and actually notice the pt_regs and identify the entry. For example, I'd love to see "page fault, RIP=xyz" printed in the middle of a stack dump on a crash.

Also, I think that just following rbp links will lose the actual function that took the page fault (or whatever function pt_regs->ip actually points to).

Hm. I think we could fix all that in a more standard way. Whenever a new pt_regs frame gets saved on entry, we could also create a new stack frame which points to a fake kernel_entry() function. That would tell the unwinder there's a pt_regs frame without otherwise breaking frame pointers across the frame.

Quoted 4 lines

Then I guess we wouldn't need my other solution of putting the idt entries in a special section.

How does that sound?

Let me try to understand.

The normal call sequence is call; push %rbp; mov %rsp, %rbp. So rbp points to (prev rbp, prev rip) on the stack, and you can follow the chain back. Right now, on a user access page fault or similar, we have rbp (probably) pointing to the interrupted frame, and the interrupted rip isn't saved anywhere that a naive unwinder can find it. (It's in pt_regs, but the rbp chain skips right over that.)

We could change the entry code so that an interrupt / idtentry does:

push pt_regs
push kernel_entry
push %rbp
mov %rsp, %rbp
call handler
pop %rbp
addq $8, %rsp

or similar. That would make it appear that the actual C handler was caused by a dummy function "kernel_entry". Now the unwinder would get to kernel_entry, but it *still* wouldn't find its way to the calling frame, which only solves part of the problem. We could at least teach the unwinder how kernel_entry works and let it decode pt_regs to continue unwinding. This would be nice, and I think it could work.

I think I like this, except that, if it used a separate section, it could potentially be faster, as, for each actual entry type, the offset from the C handler frame to pt_regs is a foregone conclusion. But this is pretty simple and performance is already abysmal in most handlers.

Quoted 5 lines

There's an added benefit to using a separate section, though: we could also annotate the calls with what type of entry they were so the unwinder could print it out nicely.

I could be convinced either way.

Ok, I took a stab at this. See the patch below.

In addition to annotating interrupt/exception pt_regs frames, I also annotated all the syscall pt_regs, for consistency.

As you mentioned, it will affect performance a bit, but I think it will be insignificant.

I think I like this approach better than putting the interrupt/idtentry's in a special section, because this is much more precise. Especially now that I'm annotating pt_regs syscalls.

Also I think with a few minor changes we could implement your idea of annotating the calls with what type of entry they are. But I don't think that's really needed, because the name of the interrupt/idtentry is already on the stack trace.

Before:

  [<ffffffff8143c243>] dump_stack+0x85/0xc2
  [<ffffffff81073596>] __do_page_fault+0x576/0x5a0
  [<ffffffff8107369c>] trace_do_page_fault+0x5c/0x2e0
  [<ffffffff8106d83c>] do_async_page_fault+0x2c/0xa0
  [<ffffffff81887058>] async_page_fault+0x28/0x30
  [<ffffffff81451560>] ? copy_page_to_iter+0x70/0x440
  [<ffffffff811ebeac>] ? pagecache_get_page+0x2c/0x290
  [<ffffffff811edaeb>] generic_file_read_iter+0x26b/0x770
  [<ffffffff81285e32>] __vfs_read+0xe2/0x140
  [<ffffffff81286378>] vfs_read+0x98/0x140
  [<ffffffff812878c8>] SyS_read+0x58/0xc0
  [<ffffffff81884dbc>] entry_SYSCALL_64_fastpath+0x1f/0xbd

After:

  [<ffffffff8143c243>] dump_stack+0x85/0xc2
  [<ffffffff81073596>] __do_page_fault+0x576/0x5a0
  [<ffffffff8107369c>] trace_do_page_fault+0x5c/0x2e0
  [<ffffffff8106d83c>] do_async_page_fault+0x2c/0xa0
  [<ffffffff81887422>] async_page_fault+0x32/0x40
  [<ffffffff81887861>] pt_regs+0x1/0x10
  [<ffffffff81451560>] ? copy_page_to_iter+0x70/0x440
  [<ffffffff811ebeac>] ? pagecache_get_page+0x2c/0x290
  [<ffffffff811edaeb>] generic_file_read_iter+0x26b/0x770
  [<ffffffff81285e32>] __vfs_read+0xe2/0x140
  [<ffffffff81286378>] vfs_read+0x98/0x140
  [<ffffffff812878c8>] SyS_read+0x58/0xc0
  [<ffffffff81884dc6>] entry_SYSCALL_64_fastpath+0x29/0xdb
  [<ffffffff81887861>] pt_regs+0x1/0x10

Note this example is with today's unwinder. It could be made smarter to get the RIP from the pt_regs so the '?' could be removed from copy_page_to_iter().

Thoughts?

arch/x86/entry/calling.h +26 -0
index 9a9e588..f54886a 100644
@@ -201,6 +201,32 @@ For 32-bit we have the following conventions - kernel is built with
 	.byte 0xf1
 	.endm
 
+	/*
+	 * Create a stack frame for the saved pt_regs.  This allows frame
+	 * pointer based unwinders to find pt_regs on the stack.
+	 */
+	.macro CREATE_PT_REGS_FRAME regs=%rsp
+#ifdef CONFIG_FRAME_POINTER
+	pushq	\regs
+	pushq	$pt_regs+1
+	pushq	%rbp
+	movq	%rsp, %rbp
+#endif
+	.endm
+
+	.macro REMOVE_PT_REGS_FRAME
+#ifdef CONFIG_FRAME_POINTER
+	popq	%rbp
+	addq	$0x10, %rsp
+#endif
+	.endm
+
+	.macro CALL_HANDLER handler regs=%rsp
+	CREATE_PT_REGS_FRAME \regs
+	call	\handler
+	REMOVE_PT_REGS_FRAME
+	.endm
+
 #endif /* CONFIG_X86_64 */
 
 /*
arch/x86/entry/entry_64.S +27 -16

149 lines not on this page · raw message

#36 lore raw

Re: [RFC PATCH v2 05/18] sched: add task flag for preempt IRQ tracking

Quoted 138 lines · Josh Poimboeuf
Quoted 82 lines · Andy Lutomirski
Quoted 44 lines · Josh Poimboeuf
Quoted 28 lines · Andy Lutomirski
Quoted 19 lines · Josh Poimboeuf
Quoted 9 lines · Andy Lutomirski
Quoted 5 lines · Josh Poimboeuf
Quoted 2 lines

I suppose we could try to rejigger the code so that rbp points to pt_regs or similar.

I think we should avoid doing something like that because it would break gdb and all the other unwinders who don't know about it.

How so?

Quoted 7 lines

Currently, rbp in the entry code is meaningless. I'm suggesting that, when we do, for example, 'call \do_sym' in idtentry, we point rbp to the pt_regs. Currently it points to something stale (which the dump_stack code might be relying on. Hmm.) But it's probably also safe to assume that if you unwind to the 'call \do_sym', then pt_regs is the next thing on the stack, so just doing the section thing would work.

Quoted 5 lines

Yes, rbp is meaningless on the entry from user space. But if an in-kernel interrupt occurs (e.g. page fault, preemption) and you have nested entry, rbp keeps its old value, right? So the unwinder can walk past the nested entry frame and keep going until it gets to the original entry.

Yes.

Quoted 8 lines

It would be nice if we could do better, though, and actually notice the pt_regs and identify the entry. For example, I'd love to see "page fault, RIP=xyz" printed in the middle of a stack dump on a crash.

Also, I think that just following rbp links will lose the actual function that took the page fault (or whatever function pt_regs->ip actually points to).

Hm. I think we could fix all that in a more standard way. Whenever a new pt_regs frame gets saved on entry, we could also create a new stack frame which points to a fake kernel_entry() function. That would tell the unwinder there's a pt_regs frame without otherwise breaking frame pointers across the frame.

Quoted 4 lines

Then I guess we wouldn't need my other solution of putting the idt entries in a special section.

How does that sound?

Let me try to understand.

The normal call sequence is call; push %rbp; mov %rsp, %rbp. So rbp points to (prev rbp, prev rip) on the stack, and you can follow the chain back. Right now, on a user access page fault or similar, we have rbp (probably) pointing to the interrupted frame, and the interrupted rip isn't saved anywhere that a naive unwinder can find it. (It's in pt_regs, but the rbp chain skips right over that.)

We could change the entry code so that an interrupt / idtentry does:

push pt_regs
push kernel_entry
push %rbp
mov %rsp, %rbp
call handler
pop %rbp
addq $8, %rsp

or similar. That would make it appear that the actual C handler was caused by a dummy function "kernel_entry". Now the unwinder would get to kernel_entry, but it *still* wouldn't find its way to the calling frame, which only solves part of the problem. We could at least teach the unwinder how kernel_entry works and let it decode pt_regs to continue unwinding. This would be nice, and I think it could work.

I think I like this, except that, if it used a separate section, it could potentially be faster, as, for each actual entry type, the offset from the C handler frame to pt_regs is a foregone conclusion. But this is pretty simple and performance is already abysmal in most handlers.

Quoted 5 lines

There's an added benefit to using a separate section, though: we could also annotate the calls with what type of entry they were so the unwinder could print it out nicely.

I could be convinced either way.

Ok, I took a stab at this. See the patch below.

In addition to annotating interrupt/exception pt_regs frames, I also annotated all the syscall pt_regs, for consistency.

As you mentioned, it will affect performance a bit, but I think it will be insignificant.

I think I like this approach better than putting the interrupt/idtentry's in a special section, because this is much more precise. Especially now that I'm annotating pt_regs syscalls.

Also I think with a few minor changes we could implement your idea of annotating the calls with what type of entry they are. But I don't think that's really needed, because the name of the interrupt/idtentry is already on the stack trace.

Before:

  [<ffffffff8143c243>] dump_stack+0x85/0xc2
  [<ffffffff81073596>] __do_page_fault+0x576/0x5a0
  [<ffffffff8107369c>] trace_do_page_fault+0x5c/0x2e0
  [<ffffffff8106d83c>] do_async_page_fault+0x2c/0xa0
  [<ffffffff81887058>] async_page_fault+0x28/0x30
  [<ffffffff81451560>] ? copy_page_to_iter+0x70/0x440
  [<ffffffff811ebeac>] ? pagecache_get_page+0x2c/0x290
  [<ffffffff811edaeb>] generic_file_read_iter+0x26b/0x770
  [<ffffffff81285e32>] __vfs_read+0xe2/0x140
  [<ffffffff81286378>] vfs_read+0x98/0x140
  [<ffffffff812878c8>] SyS_read+0x58/0xc0
  [<ffffffff81884dbc>] entry_SYSCALL_64_fastpath+0x1f/0xbd

After:

  [<ffffffff8143c243>] dump_stack+0x85/0xc2
  [<ffffffff81073596>] __do_page_fault+0x576/0x5a0
  [<ffffffff8107369c>] trace_do_page_fault+0x5c/0x2e0
  [<ffffffff8106d83c>] do_async_page_fault+0x2c/0xa0
  [<ffffffff81887422>] async_page_fault+0x32/0x40
  [<ffffffff81887861>] pt_regs+0x1/0x10
  [<ffffffff81451560>] ? copy_page_to_iter+0x70/0x440
  [<ffffffff811ebeac>] ? pagecache_get_page+0x2c/0x290
  [<ffffffff811edaeb>] generic_file_read_iter+0x26b/0x770
  [<ffffffff81285e32>] __vfs_read+0xe2/0x140
  [<ffffffff81286378>] vfs_read+0x98/0x140
  [<ffffffff812878c8>] SyS_read+0x58/0xc0
  [<ffffffff81884dc6>] entry_SYSCALL_64_fastpath+0x29/0xdb
  [<ffffffff81887861>] pt_regs+0x1/0x10
Quoted 5 lines

Note this example is with today's unwinder. It could be made smarter to get the RIP from the pt_regs so the '?' could be removed from copy_page_to_iter().

Thoughts?

I think we should do that. The silly sample patch I sent you (or at least that I think I sent you) did that, and it worked nicely.

Quoted 11 lines
arch/x86/entry/calling.h +2 -0
index 9a9e588..f54886a 100644
@@ -201,6 +201,32 @@ For 32-bit we have the following conventions - kernel is built with
        .byte 0xf1
        .endm
 
+       /*
+        * Create a stack frame for the saved pt_regs.  This allows frame

arch/x86/entry/calling.h For 32-bit we have the following conventions - kernel is built with · line 215

Quoted 10 lines
arch/x86/entry/calling.h +10 -0
+        * pointer based unwinders to find pt_regs on the stack.
+        */
+       .macro CREATE_PT_REGS_FRAME regs=%rsp
+#ifdef CONFIG_FRAME_POINTER
+       pushq   \regs
+       pushq   $pt_regs+1
+       pushq   %rbp
+       movq    %rsp, %rbp
+#endif
+       .endm

I don't love this part. It's going to hurt performance, and, given that we need to change the unwinder anyway to make it useful, let's just emit a table somewhere in .rodata and use it directly.

Quoted 11 lines
arch/x86/entry/entry_64.S +1 -0
@@ -199,6 +199,7 @@ entry_SYSCALL_64_fastpath:
        ja      1f                              /* return -ENOSYS (already in pt_regs->ax) */
        movq    %r10, %rcx
 
+       CREATE_PT_REGS_FRAME
        /*
         * This call instruction is handled specially in stub_ptregs_64.
         * It might end up jumping to the slow path.  If it jumps, RAX

arch/x86/entry/entry_64.S entry_SYSCALL_64_fastpath: · line 214

Quoted 7 lines
arch/x86/entry/entry_64.S +2 -0
@@ -207,6 +208,8 @@ entry_SYSCALL_64_fastpath:
        call    *sys_call_table(, %rax, 8)
 .Lentry_SYSCALL_64_after_fastpath_call:
 
+       REMOVE_PT_REGS_FRAME
+
        movq    %rax, RAX(%rsp)
 1:

This one is particular is quite hot, so I'd much rather avoid it.

#37 lore raw

Re: [RFC PATCH v2 05/18] sched: add task flag for preempt IRQ tracking

Quoted 9 lines · Andy Lutomirski
Quoted 5 lines · Josh Poimboeuf

Note this example is with today's unwinder. It could be made smarter to get the RIP from the pt_regs so the '?' could be removed from copy_page_to_iter().

Thoughts?

I think we should do that. The silly sample patch I sent you (or at least that I think I sent you) did that, and it worked nicely.

Yeah, we can certainly do something similar to make the unwinder smarter. It should be very simple with this approach: if it finds the pt_regs() function on the stack, the (struct pt_regs *) pointer will be right after it.

Quoted 8 lines
Quoted 8 lines
arch/x86/entry/calling.h +0 -0
index 9a9e588..f54886a 100644
@@ -201,6 +201,32 @@ For 32-bit we have the following conventions - kernel is built with
        .byte 0xf1
        .endm
 
Quoted 16 lines

arch/x86/entry/calling.h For 32-bit we have the following conventions - kernel is built with · line 215

Quoted 12 lines
arch/x86/entry/calling.h +12 -0
+       /*
+        * Create a stack frame for the saved pt_regs.  This allows frame
+        * pointer based unwinders to find pt_regs on the stack.
+        */
+       .macro CREATE_PT_REGS_FRAME regs=%rsp
+#ifdef CONFIG_FRAME_POINTER
+       pushq   \regs
+       pushq   $pt_regs+1
+       pushq   %rbp
+       movq    %rsp, %rbp
+#endif
+       .endm

I don't love this part. It's going to hurt performance, and, given that we need to change the unwinder anyway to make it useful, let's just emit a table somewhere in .rodata and use it directly.

I'm not sure about the idea of a table. I get the feeling it would add more complexity to both the entry code and the unwinder. How would you specify the pt_regs location when it's on a different stack? (See the interrupt macro: non-nested interrupts will place pt_regs on the task stack before switching to the irq stack.)

If you're worried about performance, I can remove the syscall annotations. They're optional anyway, since the pt_regs is always at the same place on the stack for syscalls.

I think three extra pushes wouldn't be a performance issue for interrupts/exceptions. And they'll go away when we finally bury CONFIG_FRAME_POINTER.

Here's the same patch without syscall annotations:

arch/x86/entry/calling.h +26 -0
index 9a9e588..f54886a 100644
@@ -201,6 +201,32 @@ For 32-bit we have the following conventions - kernel is built with
 	.byte 0xf1
 	.endm
 
+	/*
+	 * Create a stack frame for the saved pt_regs.  This allows frame
+	 * pointer based unwinders to find pt_regs on the stack.
+	 */
+	.macro CREATE_PT_REGS_FRAME regs=%rsp
+#ifdef CONFIG_FRAME_POINTER
+	pushq	\regs
+	pushq	$pt_regs+1
+	pushq	%rbp
+	movq	%rsp, %rbp
+#endif
+	.endm
+
+	.macro REMOVE_PT_REGS_FRAME
+#ifdef CONFIG_FRAME_POINTER
+	popq	%rbp
+	addq	$0x10, %rsp
+#endif
+	.endm
+
+	.macro CALL_HANDLER handler regs=%rsp
+	CREATE_PT_REGS_FRAME \regs
+	call	\handler
+	REMOVE_PT_REGS_FRAME
+	.endm
+
 #endif /* CONFIG_X86_64 */
 
 /*
arch/x86/entry/entry_64.S +17 -11
index 9ee0da1..93bc8f0 100644
@@ -468,7 +468,7 @@ END(irq_entries_start)
 	/* We entered an interrupt context - irqs are off: */
 	TRACE_IRQS_OFF
 
-	call	\func	/* rdi points to pt_regs */
+	CALL_HANDLER \func regs=%rdi
 	.endm
 
 	/*
@@ -495,7 +495,7 @@ ret_from_intr:
 	/* Interrupt came from user space */
 GLOBAL(retint_user)
 	mov	%rsp,%rdi
-	call	prepare_exit_to_usermode
+	CALL_HANDLER prepare_exit_to_usermode
 	TRACE_IRQS_IRETQ
 	SWAPGS
 	jmp	restore_regs_and_iret
@@ -509,7 +509,7 @@ retint_kernel:
 	jnc	1f
 0:	cmpl	$0, PER_CPU_VAR(__preempt_count)
 	jnz	1f
-	call	preempt_schedule_irq
+	CALL_HANDLER preempt_schedule_irq
 	jmp	0b
 1:
 #endif
@@ -688,8 +688,6 @@ ENTRY(\sym)
 	.endif
 	.endif
 
-	movq	%rsp, %rdi			/* pt_regs pointer */
-
 	.if \has_error_code
 	movq	ORIG_RAX(%rsp), %rsi		/* get error code */
 	movq	$-1, ORIG_RAX(%rsp)		/* no syscall to restart */
@@ -701,7 +699,8 @@ ENTRY(\sym)
 	subq	$EXCEPTION_STKSZ, CPU_TSS_IST(\shift_ist)
 	.endif
 
-	call	\do_sym
+	movq	%rsp, %rdi			/* pt_regs pointer */
+	CALL_HANDLER \do_sym
 
 	.if \shift_ist != -1
 	addq	$EXCEPTION_STKSZ, CPU_TSS_IST(\shift_ist)
@@ -728,8 +727,6 @@ ENTRY(\sym)
 	call	sync_regs
 	movq	%rax, %rsp			/* switch stack */
 
-	movq	%rsp, %rdi			/* pt_regs pointer */
-
 	.if \has_error_code
 	movq	ORIG_RAX(%rsp), %rsi		/* get error code */
 	movq	$-1, ORIG_RAX(%rsp)		/* no syscall to restart */
@@ -737,7 +734,8 @@ ENTRY(\sym)
 	xorl	%esi, %esi			/* no error code */
 	.endif
 
-	call	\do_sym
+	movq	%rsp, %rdi			/* pt_regs pointer */
+	CALL_HANDLER \do_sym
 
 	jmp	error_exit			/* %ebx: no swapgs flag */
 	.endif
@@ -1174,7 +1172,7 @@ ENTRY(nmi)
 
 	movq	%rsp, %rdi
 	movq	$-1, %rsi
-	call	do_nmi
+	CALL_HANDLER do_nmi
 
 	/*
 	 * Return back to user mode.  We must *not* do the normal exit
@@ -1387,7 +1385,7 @@ end_repeat_nmi:
 	/* paranoidentry do_nmi, 0; without TRACE_IRQS_OFF */
 	movq	%rsp, %rdi
 	movq	$-1, %rsi
-	call	do_nmi
+	CALL_HANDLER do_nmi
 
 	testl	%ebx, %ebx			/* swapgs needed? */
 	jnz	nmi_restore
@@ -1423,3 +1421,11 @@ ENTRY(ignore_sysret)
 	mov	$-ENOSYS, %eax
 	sysret
 END(ignore_sysret)
+
+/* fake function which allows stack unwinders to detect pt_regs frames */
+#ifdef CONFIG_FRAME_POINTER
+ENTRY(pt_regs)
+	nop
+	nop
+ENDPROC(pt_regs)
+#endif /* CONFIG_FRAME_POINTER */
#38 lore raw

Re: [RFC PATCH v2 05/18] sched: add task flag for preempt IRQ tracking

Quoted 8 lines · Josh Poimboeuf
Quoted 7 lines · Andy Lutomirski
Quoted 5 lines · Josh Poimboeuf

Note this example is with today's unwinder. It could be made smarter to get the RIP from the pt_regs so the '?' could be removed from copy_page_to_iter().

Thoughts?

Quoted 7 lines
Quoted 2 lines

I think we should do that. The silly sample patch I sent you (or at least that I think I sent you) did that, and it worked nicely.

Yeah, we can certainly do something similar to make the unwinder smarter. It should be very simple with this approach: if it finds the pt_regs() function on the stack, the (struct pt_regs *) pointer will be right after it.

That seems barely easier than checking if it finds a function in .entry that's marked on the stack, and the latter has no runtime cost.

Quoted 26 lines
Quoted 9 lines
Quoted 9 lines
arch/x86/entry/calling.h +0 -0
index 9a9e588..f54886a 100644
@@ -201,6 +201,32 @@ For 32-bit we have the following conventions - kernel is built with
        .byte 0xf1
        .endm
 
Quoted 16 lines

arch/x86/entry/calling.h For 32-bit we have the following conventions - kernel is built with · line 215

Quoted 12 lines
arch/x86/entry/calling.h +12 -0
+       /*
+        * Create a stack frame for the saved pt_regs.  This allows frame
+        * pointer based unwinders to find pt_regs on the stack.
+        */
+       .macro CREATE_PT_REGS_FRAME regs=%rsp
+#ifdef CONFIG_FRAME_POINTER
+       pushq   \regs
+       pushq   $pt_regs+1
+       pushq   %rbp
+       movq    %rsp, %rbp
+#endif
+       .endm

I don't love this part. It's going to hurt performance, and, given that we need to change the unwinder anyway to make it useful, let's just emit a table somewhere in .rodata and use it directly.

Quoted 5 lines

I'm not sure about the idea of a table. I get the feeling it would add more complexity to both the entry code and the unwinder. How would you specify the pt_regs location when it's on a different stack? (See the interrupt macro: non-nested interrupts will place pt_regs on the task stack before switching to the irq stack.)

Hmm. I need to think about the interrupt stack case a bit. Although the actual top of the interrupt stack has a nearly fixed format, and I have old patches to clean it up and make it actually be fixed. I'll try to dust those off and resend them soon.

Quoted 8 lines

If you're worried about performance, I can remove the syscall annotations. They're optional anyway, since the pt_regs is always at the same place on the stack for syscalls.

I think three extra pushes wouldn't be a performance issue for interrupts/exceptions. And they'll go away when we finally bury CONFIG_FRAME_POINTER.

I bet we'll always need to support CONFIG_FRAME_POINTER for some embedded systems.

I'll play with this a bit.

--Andy

#39 lore raw

Re: [RFC PATCH v2 05/18] sched: add task flag for preempt IRQ tracking

Quoted 12 lines · Andy Lutomirski
Quoted 11 lines · Josh Poimboeuf
Quoted 9 lines · Andy Lutomirski
Quoted 5 lines · Josh Poimboeuf

Note this example is with today's unwinder. It could be made smarter to get the RIP from the pt_regs so the '?' could be removed from copy_page_to_iter().

Thoughts?

I think we should do that. The silly sample patch I sent you (or at least that I think I sent you) did that, and it worked nicely.

Quoted 7 lines
Quoted 4 lines

Yeah, we can certainly do something similar to make the unwinder smarter. It should be very simple with this approach: if it finds the pt_regs() function on the stack, the (struct pt_regs *) pointer will be right after it.

That seems barely easier than checking if it finds a function in .entry that's marked on the stack,

Don't forget you'd also have to look up the function's pt_regs offset in the table.

Quoted 1 line

and the latter has no runtime cost.

Well, I had been assuming the three extra pushes and one extra pop for interrupts would be negligible, but I'm definitely no expert there. Any idea how I can measure it?

Quoted 10 lines
Quoted 5 lines

I'm not sure about the idea of a table. I get the feeling it would add more complexity to both the entry code and the unwinder. How would you specify the pt_regs location when it's on a different stack? (See the interrupt macro: non-nested interrupts will place pt_regs on the task stack before switching to the irq stack.)

Hmm. I need to think about the interrupt stack case a bit. Although the actual top of the interrupt stack has a nearly fixed format, and I have old patches to clean it up and make it actually be fixed. I'll try to dust those off and resend them soon.

How would that solve the problem? Would pt_regs be moved or copied to the irq stack?

Quoted 10 lines
Quoted 7 lines

If you're worried about performance, I can remove the syscall annotations. They're optional anyway, since the pt_regs is always at the same place on the stack for syscalls.

I think three extra pushes wouldn't be a performance issue for interrupts/exceptions. And they'll go away when we finally bury CONFIG_FRAME_POINTER.

I bet we'll always need to support CONFIG_FRAME_POINTER for some embedded systems.

Yeah, probably.

Quoted 1 line

I'll play with this a bit.

Thanks, looking forward to seeing what you come up with.

Signature · 1 line
Josh
#40 lore raw

Re: [RFC PATCH v2 05/18] sched: add task flag for preempt IRQ tracking

Quoted 21 lines · Josh Poimboeuf
Quoted 12 lines · Andy Lutomirski
Quoted 11 lines · Josh Poimboeuf
Quoted 9 lines · Andy Lutomirski
Quoted 5 lines · Josh Poimboeuf

Note this example is with today's unwinder. It could be made smarter to get the RIP from the pt_regs so the '?' could be removed from copy_page_to_iter().

Thoughts?

I think we should do that. The silly sample patch I sent you (or at least that I think I sent you) did that, and it worked nicely.

Quoted 7 lines
Quoted 4 lines

Yeah, we can certainly do something similar to make the unwinder smarter. It should be very simple with this approach: if it finds the pt_regs() function on the stack, the (struct pt_regs *) pointer will be right after it.

That seems barely easier than checking if it finds a function in .entry that's marked on the stack,

Quoted 8 lines

Don't forget you'd also have to look up the function's pt_regs offset in the table.

Quoted 1 line

and the latter has no runtime cost.

Well, I had been assuming the three extra pushes and one extra pop for interrupts would be negligible, but I'm definitely no expert there. Any idea how I can measure it?

I think it would be negligible, at least for interrupts, since interrupts are already extremely expensive. But I don't love adding assembly code that makes them even slower. The real thing I dislike about this approach is that it's not a normal stack frame, so you need code in the unwinder to unwind through it correctly, which makes me think that you're not saving much complexity by adding the pushes. But maybe I'm wrong.

Quoted 7 lines
Quoted 7 lines
Quoted 6 lines

I'm not sure about the idea of a table. I get the feeling it would add more complexity to both the entry code and the unwinder. How would you specify the pt_regs location when it's on a different stack? (See the interrupt macro: non-nested interrupts will place pt_regs on the task stack before switching to the irq stack.)

Quoted 7 lines
Quoted 4 lines

Hmm. I need to think about the interrupt stack case a bit. Although the actual top of the interrupt stack has a nearly fixed format, and I have old patches to clean it up and make it actually be fixed. I'll try to dust those off and resend them soon.

How would that solve the problem? Would pt_regs be moved or copied to the irq stack?

Hmm.

Maybe the right way would be to unwind off the IRQ stack in two steps. Step one would be to figure out that you're on the IRQ stack and pop your way off it. Step two would be to find pt_regs.

But that could be rather nasty to implement. Maybe what we actually want to do is this:

First, apply this thing:

https://git.kernel.org/cgit/linux/kernel/git/luto/linux.git/commit/?h=x86/entry_ist&id=2231ec7e0bcc1a2bc94a17081511ab54cc6badd1

that gives us a common format for the IRQ stack.

Second, use my idea of making a table of offsets to pt_regs, so we'd have, roughly:

ENTER_IRQ_STACK old_rsp=%r11
call __do_softirq
ANNOTATE_IRQSTACK_PTREGS_CALL offset=0
LEAVE_IRQ_STACK

the idea here is that offset=0 means that there is no offset beyond that implied by ENTER_IRQ_STACK. What actually gets written to the table is offset 8, because ENTER_IRQ_STACK itself does one push.

So far, this has no runtime overhead at all.

Then, in the unwinder, the logic becomes:

If the return address is annotated in the ptregs entry table, look up the offset. If the offset is in bounds, then use it. If the offset is out of bounds and we're on the IRQ stack, then unwind the ENTER_IRQ_STACK as well.

Does that seem reasonable? I can try to implement it and see what it looks like.

--Andy

Quoted 20 lines
Quoted 11 lines
Quoted 8 lines

If you're worried about performance, I can remove the syscall annotations. They're optional anyway, since the pt_regs is always at the same place on the stack for syscalls.

I think three extra pushes wouldn't be a performance issue for interrupts/exceptions. And they'll go away when we finally bury CONFIG_FRAME_POINTER.

I bet we'll always need to support CONFIG_FRAME_POINTER for some embedded systems.

Yeah, probably.

Quoted 1 line

I'll play with this a bit.

Thanks, looking forward to seeing what you come up with.

--
Josh

Signature · 2 lines
Andy Lutomirski
AMA Capital Management, LLC
#41 lore raw

Re: [RFC PATCH v2 05/18] sched: add task flag for preempt IRQ tracking

Quoted 27 lines · Andy Lutomirski
Quoted 26 lines · Josh Poimboeuf
Quoted 12 lines · Andy Lutomirski
Quoted 11 lines · Josh Poimboeuf
Quoted 9 lines · Andy Lutomirski
Quoted 5 lines · Josh Poimboeuf

Note this example is with today's unwinder. It could be made smarter to get the RIP from the pt_regs so the '?' could be removed from copy_page_to_iter().

Thoughts?

I think we should do that. The silly sample patch I sent you (or at least that I think I sent you) did that, and it worked nicely.

Quoted 7 lines
Quoted 4 lines

Yeah, we can certainly do something similar to make the unwinder smarter. It should be very simple with this approach: if it finds the pt_regs() function on the stack, the (struct pt_regs *) pointer will be right after it.

That seems barely easier than checking if it finds a function in .entry that's marked on the stack,

Don't forget you'd also have to look up the function's pt_regs offset in the table.

Quoted 1 line

and the latter has no runtime cost.

Quoted 7 lines
Quoted 3 lines

Well, I had been assuming the three extra pushes and one extra pop for interrupts would be negligible, but I'm definitely no expert there. Any idea how I can measure it?

I think it would be negligible, at least for interrupts, since interrupts are already extremely expensive. But I don't love adding assembly code that makes them even slower.

I just don't find that very convincing :-) If the performance impact is negligible, we should ignore it. We should instead be focusing on finding the simplest solution.

Quoted 4 lines

The real thing I dislike about this approach is that it's not a normal stack frame, so you need code in the unwinder to unwind through it correctly, which makes me think that you're not saving much complexity by adding the pushes. But maybe I'm wrong.

Hm, I view it differently. Sure the stack frame is a bit unusual, but as far as unwinders go, it _is_ normal. So even a stock unwinder can show the user that a pt_regs() -- or interrupt_frame() or whatever we call it -- function was called. Just knowing that an interrupt occurred could be useful information, even without the contents of RIP.

Quoted 45 lines
Quoted 6 lines
Quoted 6 lines
Quoted 5 lines

I'm not sure about the idea of a table. I get the feeling it would add more complexity to both the entry code and the unwinder. How would you specify the pt_regs location when it's on a different stack? (See the interrupt macro: non-nested interrupts will place pt_regs on the task stack before switching to the irq stack.)

Quoted 7 lines
Quoted 4 lines

Hmm. I need to think about the interrupt stack case a bit. Although the actual top of the interrupt stack has a nearly fixed format, and I have old patches to clean it up and make it actually be fixed. I'll try to dust those off and resend them soon.

How would that solve the problem? Would pt_regs be moved or copied to the irq stack?

Hmm.

Maybe the right way would be to unwind off the IRQ stack in two steps. Step one would be to figure out that you're on the IRQ stack and pop your way off it. Step two would be to find pt_regs.

But that could be rather nasty to implement. Maybe what we actually want to do is this:

First, apply this thing:

https://git.kernel.org/cgit/linux/kernel/git/luto/linux.git/commit/?h=x86/entry_ist&id=2231ec7e0bcc1a2bc94a17081511ab54cc6badd1

that gives us a common format for the IRQ stack.

Second, use my idea of making a table of offsets to pt_regs, so we'd have, roughly:

ENTER_IRQ_STACK old_rsp=%r11
call __do_softirq
ANNOTATE_IRQSTACK_PTREGS_CALL offset=0
LEAVE_IRQ_STACK

the idea here is that offset=0 means that there is no offset beyond that implied by ENTER_IRQ_STACK. What actually gets written to the table is offset 8, because ENTER_IRQ_STACK itself does one push.

So far, this has no runtime overhead at all.

Then, in the unwinder, the logic becomes:

Quoted 7 lines

If the return address is annotated in the ptregs entry table, look up the offset. If the offset is in bounds, then use it. If the offset is out of bounds and we're on the IRQ stack, then unwind the ENTER_IRQ_STACK as well.

Does that seem reasonable? I can try to implement it and see what it looks like.

To be honest I'm not crazy about it. The ANNOTATE_IRQSTACK_PTREGS_CALL macro needs knowledge about the implementation of ENTER_IRQ_STACK and how many pushes it does. I think that makes the entry code more complex and harder to understand than my patch.

Also the unwinders would need to be quite a bit more complex, and would need to know more of the intimate details of the irq stack. That's probably a less important consideration than entry code complexity, but it's still significant when you consider the fact that the kernel has many unwinders, both in-kernel and out-of-kernel.

Signature · 1 line
Josh
#111 lore raw

RE: livepatch: change to a per-task consistency model

In reply to Jiri Kosina

RnJvbTogSmlyaSBLb3NpbmENCj4gU2VudDogMTggTWF5IDIwMTYgMjE6MjMNCj4gT24gV2VkLCAx OCBNYXkgMjAxNiwgSm9zaCBQb2ltYm9ldWYgd3JvdGU6DQo+IA0KPiA+IFllYWgsIEkgdGhpbmsg dGhpcyBzaXR1YXRpb24gLS0gYSB0YXNrIHNsZWVwaW5nIG9uIGFuIGFmZmVjdGVkIGZ1bmN0aW9u DQo+ID4gaW4gdW5pbnRlcnJ1cHRpYmxlIHN0YXRlIGZvciBhIGxvbmcgcGVyaW9kIG9mIHRpbWUg LS0gd291bGQgYmUNCj4gPiBleGNlZWRpbmdseSByYXJlIGFuZCBub3Qgc29tZXRoaW5nIHdlIG5l ZWQgdG8gd29ycnkgYWJvdXQgZm9yIG5vdy4NCj4gDQo+IFBsdXMgaW4gY2FzZSB0YXNrJ2QgYmUg aW4gVEFTS19VTklOVEVSUlVQVElCTEUgZm9yIG1vcmUgdGhhbiAxMjBzLCBodW5nDQo+IHRhc2sg ZGV0ZWN0b3Igd291bGQgdHJpZ2dlciBhbnl3YXkuDQoNClJlbGF0ZWQsIHBsZWFzZSBjYW4gd2Ug aGF2ZSBhIGZsYWcgZm9yIHRoZSBzbGVlcCBhbmQvb3IgcHJvY2VzcyBzbyB0aGF0DQphbiB1bmlu dGVycnVwdGlibGUgc2xlZXAgZG9lc24ndCB0cmlnZ2VyIHRoZSAnaHVuZyB0YXNrJyBkZXRlY3Rv cg0KYW5kIGFsc28gc3RvcHMgdGhlIHByb2Nlc3MgY291bnRpbmcgdG93YXJkcyB0aGUgJ2xvYWQg YXZlcmFnZScuDQoNCkluIHBhcnRpY3VsYXIgc29tZSBrZXJuZWwgdGhyZWFkcyBhcmUgbm90IHNp Z25hbGFibGUsIGFuZCBkbyBub3QNCndhbnQgdG8gYmUgd29rZW4gYnkgc2lnbmFscyAodGhleSBl eGl0IG9uIGEgc3BlY2lmaWMgcmVxdWVzdCkuDQoNCglEYXZpZA0KDQo=

#112 lore raw

RE: livepatch: change to a per-task consistency model

Quoted 2 lines · David Laight

Related, please can we have a flag for the sleep and/or process so that an uninterruptible sleep doesn't trigger the 'hung task' detector

TASK_KILLABLE

Quoted 1 line

and also stops the process counting towards the 'load average'.

TASK_NOLOAD

Signature · 2 lines
Jiri Kosina
SUSE Labs
#44 lore raw

Re: [RFC PATCH v2 05/18] sched: add task flag for preempt IRQ tracking

In reply to Josh Poimboeuf
Quoted 138 lines · Josh Poimboeuf
Quoted 82 lines · Andy Lutomirski
Quoted 44 lines · Josh Poimboeuf
Quoted 28 lines · Andy Lutomirski
Quoted 19 lines · Josh Poimboeuf
Quoted 9 lines · Andy Lutomirski
Quoted 5 lines · Josh Poimboeuf
Quoted 2 lines

I suppose we could try to rejigger the code so that rbp points to pt_regs or similar.

I think we should avoid doing something like that because it would break gdb and all the other unwinders who don't know about it.

How so?

Quoted 7 lines

Currently, rbp in the entry code is meaningless. I'm suggesting that, when we do, for example, 'call \do_sym' in idtentry, we point rbp to the pt_regs. Currently it points to something stale (which the dump_stack code might be relying on. Hmm.) But it's probably also safe to assume that if you unwind to the 'call \do_sym', then pt_regs is the next thing on the stack, so just doing the section thing would work.

Quoted 5 lines

Yes, rbp is meaningless on the entry from user space. But if an in-kernel interrupt occurs (e.g. page fault, preemption) and you have nested entry, rbp keeps its old value, right? So the unwinder can walk past the nested entry frame and keep going until it gets to the original entry.

Yes.

Quoted 8 lines

It would be nice if we could do better, though, and actually notice the pt_regs and identify the entry. For example, I'd love to see "page fault, RIP=xyz" printed in the middle of a stack dump on a crash.

Also, I think that just following rbp links will lose the actual function that took the page fault (or whatever function pt_regs->ip actually points to).

Hm. I think we could fix all that in a more standard way. Whenever a new pt_regs frame gets saved on entry, we could also create a new stack frame which points to a fake kernel_entry() function. That would tell the unwinder there's a pt_regs frame without otherwise breaking frame pointers across the frame.

Quoted 4 lines

Then I guess we wouldn't need my other solution of putting the idt entries in a special section.

How does that sound?

Let me try to understand.

The normal call sequence is call; push %rbp; mov %rsp, %rbp. So rbp points to (prev rbp, prev rip) on the stack, and you can follow the chain back. Right now, on a user access page fault or similar, we have rbp (probably) pointing to the interrupted frame, and the interrupted rip isn't saved anywhere that a naive unwinder can find it. (It's in pt_regs, but the rbp chain skips right over that.)

We could change the entry code so that an interrupt / idtentry does:

push pt_regs
push kernel_entry
push %rbp
mov %rsp, %rbp
call handler
pop %rbp
addq $8, %rsp

or similar. That would make it appear that the actual C handler was caused by a dummy function "kernel_entry". Now the unwinder would get to kernel_entry, but it *still* wouldn't find its way to the calling frame, which only solves part of the problem. We could at least teach the unwinder how kernel_entry works and let it decode pt_regs to continue unwinding. This would be nice, and I think it could work.

I think I like this, except that, if it used a separate section, it could potentially be faster, as, for each actual entry type, the offset from the C handler frame to pt_regs is a foregone conclusion. But this is pretty simple and performance is already abysmal in most handlers.

Quoted 5 lines

There's an added benefit to using a separate section, though: we could also annotate the calls with what type of entry they were so the unwinder could print it out nicely.

I could be convinced either way.

Ok, I took a stab at this. See the patch below.

In addition to annotating interrupt/exception pt_regs frames, I also annotated all the syscall pt_regs, for consistency.

As you mentioned, it will affect performance a bit, but I think it will be insignificant.

I think I like this approach better than putting the interrupt/idtentry's in a special section, because this is much more precise. Especially now that I'm annotating pt_regs syscalls.

Also I think with a few minor changes we could implement your idea of annotating the calls with what type of entry they are. But I don't think that's really needed, because the name of the interrupt/idtentry is already on the stack trace.

Before:

  [<ffffffff8143c243>] dump_stack+0x85/0xc2
  [<ffffffff81073596>] __do_page_fault+0x576/0x5a0
  [<ffffffff8107369c>] trace_do_page_fault+0x5c/0x2e0
  [<ffffffff8106d83c>] do_async_page_fault+0x2c/0xa0
  [<ffffffff81887058>] async_page_fault+0x28/0x30
  [<ffffffff81451560>] ? copy_page_to_iter+0x70/0x440
  [<ffffffff811ebeac>] ? pagecache_get_page+0x2c/0x290
  [<ffffffff811edaeb>] generic_file_read_iter+0x26b/0x770
  [<ffffffff81285e32>] __vfs_read+0xe2/0x140
  [<ffffffff81286378>] vfs_read+0x98/0x140
  [<ffffffff812878c8>] SyS_read+0x58/0xc0
  [<ffffffff81884dbc>] entry_SYSCALL_64_fastpath+0x1f/0xbd

After:

  [<ffffffff8143c243>] dump_stack+0x85/0xc2
  [<ffffffff81073596>] __do_page_fault+0x576/0x5a0
  [<ffffffff8107369c>] trace_do_page_fault+0x5c/0x2e0
  [<ffffffff8106d83c>] do_async_page_fault+0x2c/0xa0
  [<ffffffff81887422>] async_page_fault+0x32/0x40
  [<ffffffff81887861>] pt_regs+0x1/0x10
  [<ffffffff81451560>] ? copy_page_to_iter+0x70/0x440
  [<ffffffff811ebeac>] ? pagecache_get_page+0x2c/0x290
  [<ffffffff811edaeb>] generic_file_read_iter+0x26b/0x770
  [<ffffffff81285e32>] __vfs_read+0xe2/0x140
  [<ffffffff81286378>] vfs_read+0x98/0x140
  [<ffffffff812878c8>] SyS_read+0x58/0xc0
  [<ffffffff81884dc6>] entry_SYSCALL_64_fastpath+0x29/0xdb
  [<ffffffff81887861>] pt_regs+0x1/0x10
Quoted 5 lines

Note this example is with today's unwinder. It could be made smarter to get the RIP from the pt_regs so the '?' could be removed from copy_page_to_iter().

Thoughts?

Maybe I'm coming around to liking this idea.

In an ideal world (DWARF support, high-quality unwinder, nice pretty printer, etc), unwinding across a kernel exception would look like:

- some_func
- some_other_func
- do_page_fault
- page_fault

After page_fault, the next unwind step takes us to the faulting RIP (faulting_func) and reports that all GPRs are known. It should probably learn this fact from DWARF if DWARF is available, instead of directly decoding pt_regs, due to a few funny cases in which pt_regs may be incomplete. A nice pretty printer could now print all the regs.

- faulting_func
- etc.

For this to work, we don't actually need the unwinder to explicitly know where pt_regs is.

Food for thought, though: if user code does SYSENTER with TF set, you'll end up with partial pt_regs. There's nothing the kernel can do about it. DWARF will handle it without any fanfare, but I don't know if it's going to cause trouble for you pre-DWARF.

I'm also not sure it makes sense to apply this before the unwinder that can consume it is ready. Maybe, if it would be consistent with your plans, it would make sense to rewrite the unwinder first, then apply this and teach live patching to use the new unwinder, and *then* add DWARF support?

arch/x86/entry/calling.h For 32-bit we have the following conventions - kernel is built with · line 211

Quoted 9 lines
arch/x86/entry/calling.h +8 -0
+       /*
+        * Create a stack frame for the saved pt_regs.  This allows frame
+        * pointer based unwinders to find pt_regs on the stack.
+        */
+       .macro CREATE_PT_REGS_FRAME regs=%rsp
+#ifdef CONFIG_FRAME_POINTER
+       pushq   \regs
+       pushq   $pt_regs+1

Why the +1?

arch/x86/entry/calling.h For 32-bit we have the following conventions - kernel is built with · line 215

Quoted 4 lines
arch/x86/entry/calling.h +4 -0
+       pushq   %rbp
+       movq    %rsp, %rbp
+#endif
+       .endm

I keep wanting this to be only two pushes and to fudge rbp to make it work, but I don't see a good way. But let's call it CREATE_NESTED_ENTRY_FRAME or something, and let's rename pt_regs to nested_frame or similar.

arch/x86/entry/calling.h For 32-bit we have the following conventions - kernel is built with · line 228

Quoted 6 lines
arch/x86/entry/calling.h +6 -0
+
+       .macro CALL_HANDLER handler regs=%rsp
+       CREATE_PT_REGS_FRAME \regs
+       call    \handler
+       REMOVE_PT_REGS_FRAME
+       .endm

I think I'd rather open-code this everywhere. It'll make it clearer what's going on.

Quoted 9 lines
arch/x86/entry/entry_64.S +1 -0
@@ -199,6 +199,7 @@ entry_SYSCALL_64_fastpath:
        ja      1f                              /* return -ENOSYS (already in pt_regs->ax) */
        movq    %r10, %rcx
 
+       CREATE_PT_REGS_FRAME
        /*
         * This call instruction is handled specially in stub_ptregs_64.
         * It might end up jumping to the slow path.  If it jumps, RAX

arch/x86/entry/entry_64.S entry_SYSCALL_64_fastpath: · line 212

Quoted 5 lines
arch/x86/entry/entry_64.S +2 -0
@@ -207,6 +208,8 @@ entry_SYSCALL_64_fastpath:
        call    *sys_call_table(, %rax, 8)
 .Lentry_SYSCALL_64_after_fastpath_call:
 
+       REMOVE_PT_REGS_FRAME
+

As discussed, let's get rid of this bit.

arch/x86/entry/entry_64.S entry_SYSCALL_64_fastpath: · line 244

Quoted 9 lines
arch/x86/entry/entry_64.S +1 -1
        movq    %rax, RAX(%rsp)
 1:
 
@@ -238,14 +241,14 @@ entry_SYSCALL_64_fastpath:
        ENABLE_INTERRUPTS(CLBR_NONE)
        SAVE_EXTRA_REGS
        movq    %rsp, %rdi
-       call    syscall_return_slowpath /* returns with IRQs disabled */
+       CALL_HANDLER syscall_return_slowpath    /* returns with IRQs disabled */

and this.

Quoted 12 lines
arch/x86/entry/entry_64.S +1 -1
        jmp     return_from_SYSCALL_64
 
 entry_SYSCALL64_slow_path:
        /* IRQs are off. */
        SAVE_EXTRA_REGS
        movq    %rsp, %rdi
-       call    do_syscall_64           /* returns with IRQs disabled */
+       CALL_HANDLER do_syscall_64      /* returns with IRQs disabled */
 
 return_from_SYSCALL_64:
        RESTORE_EXTRA_REGS

arch/x86/entry/entry_64.S ENTRY(stub_ptregs_64) · line 350

Quoted 4 lines
arch/x86/entry/entry_64.S +1 -0
@@ -344,6 +347,7 @@ ENTRY(stub_ptregs_64)
        DISABLE_INTERRUPTS(CLBR_NONE)
        TRACE_IRQS_OFF
        popq    %rax
+       REMOVE_PT_REGS_FRAME

This will be less mysterious if you open-code the macros. Also, I think you have to, some return_from_SYSCALL_64 needs to be directly after the actual call instruction. (But if you get rid of the hunks above, I think this goes away too, so this may be moot.)

arch/x86/entry/entry_64.S END(ptregs_\func) · line 380

Quoted 8 lines
arch/x86/entry/entry_64.S +1 -1
 1:
@@ -372,7 +376,7 @@ END(ptregs_\func)
 ENTRY(ret_from_fork)
        LOCK ; btr $TIF_FORK, TI_flags(%r8)
 
-       call    schedule_tail                   /* rdi: 'prev' task parameter */
+       CALL_HANDLER schedule_tail              /* rdi: 'prev' task parameter */
 

If you end up making the unwinder smart enough to notice that rsp is just below pt_regs, then this can go away. It's harmless, though.

arch/x86/entry/entry_64.S ENTRY(ret_from_fork) · line 394

Quoted 10 lines
arch/x86/entry/entry_64.S +2 -1
        testb   $3, CS(%rsp)                    /* from kernel_thread? */
        jnz     1f
@@ -385,8 +389,9 @@ ENTRY(ret_from_fork)
         * parameter to be passed in RBP.  The called function is permitted
         * to call do_execve and thereby jump to user mode.
         */
+       movq    RBX(%rsp), %rbx
        movq    RBP(%rsp), %rdi
-       call    *RBX(%rsp)
+       CALL_HANDLER *%rbx

Does using a register like this actually save any code size? Admittedly, it's a bit cleaner.

arch/x86/entry/entry_64.S ENTRY(ignore_sysret) · line 1436

Quoted 8 lines
arch/x86/entry/entry_64.S +8 -0
+
+/* fake function which allows stack unwinders to detect pt_regs frames */
+#ifdef CONFIG_FRAME_POINTER
+ENTRY(pt_regs)
+       nop
+       nop
+ENDPROC(pt_regs)
+#endif /* CONFIG_FRAME_POINTER */

Why is this two bytes long? Is there some reason it has to be more than one byte?

--Andy

#42 lore raw

Re: [RFC PATCH v2 05/18] sched: add task flag for preempt IRQ tracking

In reply to Andy Lutomirski
Quoted 6 lines · Andy Lutomirski

I think it would be negligible, at least for interrupts, since interrupts are already extremely expensive. But I don't love adding assembly code that makes them even slower. The real thing I dislike about this approach is that it's not a normal stack frame, so you need code in the unwinder to unwind through it correctly, which makes me think that you're not saving much complexity by adding the pushes.

I fail to see what is so special about the stack frame; it's in fact pretty normal.

It has added semantic value for "those who know", but the others will (pretty much correctly) consider it to be a stackframe from a function call, and be done with it.

What am I missing?

Thanks,

Signature · 2 lines
Jiri Kosina
SUSE Labs
#43 lore raw

Re: [RFC PATCH v2 05/18] sched: add task flag for preempt IRQ tracking

Quoted 9 lines · Jiri Kosina
Quoted 6 lines · Andy Lutomirski

I think it would be negligible, at least for interrupts, since interrupts are already extremely expensive. But I don't love adding assembly code that makes them even slower. The real thing I dislike about this approach is that it's not a normal stack frame, so you need code in the unwinder to unwind through it correctly, which makes me think that you're not saving much complexity by adding the pushes.

Quoted 8 lines

I fail to see what is so special about the stack frame; it's in fact pretty normal.

It has added semantic value for "those who know", but the others will (pretty much correctly) consider it to be a stackframe from a function call, and be done with it.

What am I missing?

In Josh's code, the stack looks like:

...
interrupted frame
pt_regs
pointer to pt_regs
address of pt_regs dummy function
rbp
handler frame

A naive unwinder won't unwind this correctly, as there's no link back to the interrupted frame's RIP. If the layout changed to:

...
interrupted frame
pt_regs
interrupted RIP
rbp
handler frame

then I think it would unwind correctly, but the pt_regs would be invisible, which is IMO a bit unfortunate. It could also be (I think):

...
interrupted frame
pt_regs
interrupted rbp
interrupted RIP
pointer to pt_regs
address of pt_regs dummy function
pointer to "interrupted RIP" stack slot
handler frame

but now this is *five* pushes for the dummy frame, which I think is getting a bit out of hand.

--Andy

#45 lore raw

Re: [RFC PATCH v2 05/18] sched: add task flag for preempt IRQ tracking

In reply to Andy Lutomirski
Quoted 139 lines · Andy Lutomirski
Quoted 138 lines · Josh Poimboeuf
Quoted 82 lines · Andy Lutomirski
Quoted 44 lines · Josh Poimboeuf
Quoted 28 lines · Andy Lutomirski
Quoted 19 lines · Josh Poimboeuf
Quoted 9 lines · Andy Lutomirski
Quoted 5 lines · Josh Poimboeuf
Quoted 2 lines

I suppose we could try to rejigger the code so that rbp points to pt_regs or similar.

I think we should avoid doing something like that because it would break gdb and all the other unwinders who don't know about it.

How so?

Quoted 7 lines

Currently, rbp in the entry code is meaningless. I'm suggesting that, when we do, for example, 'call \do_sym' in idtentry, we point rbp to the pt_regs. Currently it points to something stale (which the dump_stack code might be relying on. Hmm.) But it's probably also safe to assume that if you unwind to the 'call \do_sym', then pt_regs is the next thing on the stack, so just doing the section thing would work.

Quoted 5 lines

Yes, rbp is meaningless on the entry from user space. But if an in-kernel interrupt occurs (e.g. page fault, preemption) and you have nested entry, rbp keeps its old value, right? So the unwinder can walk past the nested entry frame and keep going until it gets to the original entry.

Yes.

Quoted 8 lines

It would be nice if we could do better, though, and actually notice the pt_regs and identify the entry. For example, I'd love to see "page fault, RIP=xyz" printed in the middle of a stack dump on a crash.

Also, I think that just following rbp links will lose the actual function that took the page fault (or whatever function pt_regs->ip actually points to).

Hm. I think we could fix all that in a more standard way. Whenever a new pt_regs frame gets saved on entry, we could also create a new stack frame which points to a fake kernel_entry() function. That would tell the unwinder there's a pt_regs frame without otherwise breaking frame pointers across the frame.

Quoted 4 lines

Then I guess we wouldn't need my other solution of putting the idt entries in a special section.

How does that sound?

Let me try to understand.

The normal call sequence is call; push %rbp; mov %rsp, %rbp. So rbp points to (prev rbp, prev rip) on the stack, and you can follow the chain back. Right now, on a user access page fault or similar, we have rbp (probably) pointing to the interrupted frame, and the interrupted rip isn't saved anywhere that a naive unwinder can find it. (It's in pt_regs, but the rbp chain skips right over that.)

We could change the entry code so that an interrupt / idtentry does:

push pt_regs
push kernel_entry
push %rbp
mov %rsp, %rbp
call handler
pop %rbp
addq $8, %rsp

or similar. That would make it appear that the actual C handler was caused by a dummy function "kernel_entry". Now the unwinder would get to kernel_entry, but it *still* wouldn't find its way to the calling frame, which only solves part of the problem. We could at least teach the unwinder how kernel_entry works and let it decode pt_regs to continue unwinding. This would be nice, and I think it could work.

I think I like this, except that, if it used a separate section, it could potentially be faster, as, for each actual entry type, the offset from the C handler frame to pt_regs is a foregone conclusion. But this is pretty simple and performance is already abysmal in most handlers.

Quoted 5 lines

There's an added benefit to using a separate section, though: we could also annotate the calls with what type of entry they were so the unwinder could print it out nicely.

I could be convinced either way.

Ok, I took a stab at this. See the patch below.

In addition to annotating interrupt/exception pt_regs frames, I also annotated all the syscall pt_regs, for consistency.

As you mentioned, it will affect performance a bit, but I think it will be insignificant.

I think I like this approach better than putting the interrupt/idtentry's in a special section, because this is much more precise. Especially now that I'm annotating pt_regs syscalls.

Also I think with a few minor changes we could implement your idea of annotating the calls with what type of entry they are. But I don't think that's really needed, because the name of the interrupt/idtentry is already on the stack trace.

Before:

  [<ffffffff8143c243>] dump_stack+0x85/0xc2
  [<ffffffff81073596>] __do_page_fault+0x576/0x5a0
  [<ffffffff8107369c>] trace_do_page_fault+0x5c/0x2e0
  [<ffffffff8106d83c>] do_async_page_fault+0x2c/0xa0
  [<ffffffff81887058>] async_page_fault+0x28/0x30
  [<ffffffff81451560>] ? copy_page_to_iter+0x70/0x440
  [<ffffffff811ebeac>] ? pagecache_get_page+0x2c/0x290
  [<ffffffff811edaeb>] generic_file_read_iter+0x26b/0x770
  [<ffffffff81285e32>] __vfs_read+0xe2/0x140
  [<ffffffff81286378>] vfs_read+0x98/0x140
  [<ffffffff812878c8>] SyS_read+0x58/0xc0
  [<ffffffff81884dbc>] entry_SYSCALL_64_fastpath+0x1f/0xbd

After:

  [<ffffffff8143c243>] dump_stack+0x85/0xc2
  [<ffffffff81073596>] __do_page_fault+0x576/0x5a0
  [<ffffffff8107369c>] trace_do_page_fault+0x5c/0x2e0
  [<ffffffff8106d83c>] do_async_page_fault+0x2c/0xa0
  [<ffffffff81887422>] async_page_fault+0x32/0x40
  [<ffffffff81887861>] pt_regs+0x1/0x10
  [<ffffffff81451560>] ? copy_page_to_iter+0x70/0x440
  [<ffffffff811ebeac>] ? pagecache_get_page+0x2c/0x290
  [<ffffffff811edaeb>] generic_file_read_iter+0x26b/0x770
  [<ffffffff81285e32>] __vfs_read+0xe2/0x140
  [<ffffffff81286378>] vfs_read+0x98/0x140
  [<ffffffff812878c8>] SyS_read+0x58/0xc0
  [<ffffffff81884dc6>] entry_SYSCALL_64_fastpath+0x29/0xdb
  [<ffffffff81887861>] pt_regs+0x1/0x10
Quoted 7 lines
Quoted 5 lines

Note this example is with today's unwinder. It could be made smarter to get the RIP from the pt_regs so the '?' could be removed from copy_page_to_iter().

Thoughts?

Maybe I'm coming around to liking this idea.

Ok, good :-)

Quoted 15 lines

In an ideal world (DWARF support, high-quality unwinder, nice pretty printer, etc), unwinding across a kernel exception would look like:

- some_func
- some_other_func
- do_page_fault
- page_fault

After page_fault, the next unwind step takes us to the faulting RIP (faulting_func) and reports that all GPRs are known. It should probably learn this fact from DWARF if DWARF is available, instead of directly decoding pt_regs, due to a few funny cases in which pt_regs may be incomplete. A nice pretty printer could now print all the regs.

Quoted 5 lines

- faulting_func
- etc.

For this to work, we don't actually need the unwinder to explicitly know where pt_regs is.

That's true (but only for DWARF).

Quoted 4 lines

Food for thought, though: if user code does SYSENTER with TF set, you'll end up with partial pt_regs. There's nothing the kernel can do about it. DWARF will handle it without any fanfare, but I don't know if it's going to cause trouble for you pre-DWARF.

In this case it should see the stack pointer is past the pt_regs offset, so it would just report it as an empty stack.

Quoted 5 lines

I'm also not sure it makes sense to apply this before the unwinder that can consume it is ready. Maybe, if it would be consistent with your plans, it would make sense to rewrite the unwinder first, then apply this and teach live patching to use the new unwinder, and *then* add DWARF support?

For the purposes of livepatch, the reliable unwinder needs to detect whether an interrupt/exception pt_regs frame exists on a sleeping task (or current). This patch would allow us to do that.

So my preferred order of doing things would be:

1) Brian Gerst's switch_to() cleanup and any related unwinder fixes
2) this patch for annotating pt_regs stack frames
3) reliable unwinder, similar to what I already posted, except it relies on this patch instead of PF_PREEMPT_IRQ, and knows how to deal with the new inactive task frame format of #1
4) livepatch consistency model which uses the reliable unwinder
5) rewrite unwinder, and port all users to the new interface
6) add DWARF unwinder

1-4 are pretty much already written, whereas 5 and 6 will take considerably more work.

Quoted 10 lines

arch/x86/entry/calling.h For 32-bit we have the following conventions - kernel is built with · line 211

Quoted 8 lines
arch/x86/entry/calling.h +8 -0
+       /*
+        * Create a stack frame for the saved pt_regs.  This allows frame
+        * pointer based unwinders to find pt_regs on the stack.
+        */
+       .macro CREATE_PT_REGS_FRAME regs=%rsp
+#ifdef CONFIG_FRAME_POINTER
+       pushq   \regs
+       pushq   $pt_regs+1

Why the +1?

Some unwinders like gdb are smart enough to report the function which contains the instruction *before* the return address. Without the +1, they would show the wrong function.

Quoted 9 lines

arch/x86/entry/calling.h For 32-bit we have the following conventions - kernel is built with · line 215

Quoted 4 lines
arch/x86/entry/calling.h +4 -0
+       pushq   %rbp
+       movq    %rsp, %rbp
+#endif
+       .endm

I keep wanting this to be only two pushes and to fudge rbp to make it work, but I don't see a good way. But let's call it CREATE_NESTED_ENTRY_FRAME or something, and let's rename pt_regs to nested_frame or similar.

Or, if we aren't going to annotate syscall pt_regs, we could give it a more specific name. CREATE_INTERRUPT_FRAME and interrupt_frame()?

Quoted 9 lines

arch/x86/entry/calling.h For 32-bit we have the following conventions - kernel is built with · line 228

Quoted 6 lines
arch/x86/entry/calling.h +6 -0
+
+       .macro CALL_HANDLER handler regs=%rsp
+       CREATE_PT_REGS_FRAME \regs
+       call    \handler
+       REMOVE_PT_REGS_FRAME
+       .endm

I think I'd rather open-code this everywhere. It'll make it clearer what's going on.

Ok.

Quoted 12 lines
Quoted 12 lines
arch/x86/entry/entry_64.S +1 -0
@@ -199,6 +199,7 @@ entry_SYSCALL_64_fastpath:
        ja      1f                              /* return -ENOSYS (already in pt_regs->ax) */
        movq    %r10, %rcx
 
+       CREATE_PT_REGS_FRAME
        /*
         * This call instruction is handled specially in stub_ptregs_64.
         * It might end up jumping to the slow path.  If it jumps, RAX
@@ -207,6 +208,8 @@ entry_SYSCALL_64_fastpath:
        call    *sys_call_table(, %rax, 8)
 .Lentry_SYSCALL_64_after_fastpath_call:
 
Quoted 4 lines

Review entry_SYSCALL_64_fastpath: · line 212

Quoted 2 lines
(quoted diff) +2 -0
+       REMOVE_PT_REGS_FRAME
+

As discussed, let's get rid of this bit.

Yeah, it's fine with me to get rid of all the syscall stuff.

Quoted 4 lines
Quoted 4 lines
arch/x86/entry/entry_64.S +0 -0
        movq    %rax, RAX(%rsp)
 1:
 
Quoted 8 lines

arch/x86/entry/entry_64.S entry_SYSCALL_64_fastpath: · line 244

Quoted 6 lines
arch/x86/entry/entry_64.S +1 -1
@@ -238,14 +241,14 @@ entry_SYSCALL_64_fastpath:
        ENABLE_INTERRUPTS(CLBR_NONE)
        SAVE_EXTRA_REGS
        movq    %rsp, %rdi
-       call    syscall_return_slowpath /* returns with IRQs disabled */
+       CALL_HANDLER syscall_return_slowpath    /* returns with IRQs disabled */

and this.

This will be gone...

Quoted 10 lines
Quoted 10 lines
arch/x86/entry/entry_64.S +1 -1
        jmp     return_from_SYSCALL_64
 
 entry_SYSCALL64_slow_path:
        /* IRQs are off. */
        SAVE_EXTRA_REGS
        movq    %rsp, %rdi
-       call    do_syscall_64           /* returns with IRQs disabled */
+       CALL_HANDLER do_syscall_64      /* returns with IRQs disabled */
 
Quoted 12 lines

arch/x86/entry/entry_64.S ENTRY(stub_ptregs_64) · line 350

Quoted 7 lines
arch/x86/entry/entry_64.S +1 -0
 return_from_SYSCALL_64:
        RESTORE_EXTRA_REGS
@@ -344,6 +347,7 @@ ENTRY(stub_ptregs_64)
        DISABLE_INTERRUPTS(CLBR_NONE)
        TRACE_IRQS_OFF
        popq    %rax
+       REMOVE_PT_REGS_FRAME

This will be less mysterious if you open-code the macros. Also, I think you have to, some return_from_SYSCALL_64 needs to be directly after the actual call instruction. (But if you get rid of the hunks above, I think this goes away too, so this may be moot.)

and this...

Quoted 11 lines

arch/x86/entry/entry_64.S END(ptregs_\func) · line 380

Quoted 8 lines
arch/x86/entry/entry_64.S +1 -1
 1:
@@ -372,7 +376,7 @@ END(ptregs_\func)
 ENTRY(ret_from_fork)
        LOCK ; btr $TIF_FORK, TI_flags(%r8)
 
-       call    schedule_tail                   /* rdi: 'prev' task parameter */
+       CALL_HANDLER schedule_tail              /* rdi: 'prev' task parameter */
 

If you end up making the unwinder smart enough to notice that rsp is just below pt_regs, then this can go away. It's harmless, though.

and this...

Quoted 5 lines
Quoted 5 lines
arch/x86/entry/entry_64.S +0 -0
        testb   $3, CS(%rsp)                    /* from kernel_thread? */
        jnz     1f
@@ -385,8 +389,9 @@ ENTRY(ret_from_fork)
         * parameter to be passed in RBP.  The called function is permitted
         * to call do_execve and thereby jump to user mode.
Quoted 8 lines

arch/x86/entry/entry_64.S ENTRY(ret_from_fork) · line 394

Quoted 5 lines
arch/x86/entry/entry_64.S +2 -1
         */
+       movq    RBX(%rsp), %rbx
        movq    RBP(%rsp), %rdi
-       call    *RBX(%rsp)
+       CALL_HANDLER *%rbx

Does using a register like this actually save any code size? Admittedly, it's a bit cleaner.

and this.

(FWIW, I used a register because the assembler macro didn't seem to support passing "*RBX(%rsp)" as an argument.)

Quoted 11 lines

arch/x86/entry/entry_64.S ENTRY(ignore_sysret) · line 1436

Quoted 8 lines
arch/x86/entry/entry_64.S +8 -0
+
+/* fake function which allows stack unwinders to detect pt_regs frames */
+#ifdef CONFIG_FRAME_POINTER
+ENTRY(pt_regs)
+       nop
+       nop
+ENDPROC(pt_regs)
+#endif /* CONFIG_FRAME_POINTER */

Why is this two bytes long? Is there some reason it has to be more than one byte?

Similar to above, this is related to the need to support various unwinders. Whether the unwinder displays the ret_addr or the instruction preceding it, either way the instruction needs to be inside the function for the function to be reported.

Signature · 1 line
Josh
#46 lore raw

Re: [RFC PATCH v2 05/18] sched: add task flag for preempt IRQ tracking

Quoted 27 lines · Josh Poimboeuf
Quoted 1 line

Maybe I'm coming around to liking this idea.

Ok, good :-)

Quoted 15 lines

In an ideal world (DWARF support, high-quality unwinder, nice pretty printer, etc), unwinding across a kernel exception would look like:

- some_func
- some_other_func
- do_page_fault
- page_fault

After page_fault, the next unwind step takes us to the faulting RIP (faulting_func) and reports that all GPRs are known. It should probably learn this fact from DWARF if DWARF is available, instead of directly decoding pt_regs, due to a few funny cases in which pt_regs may be incomplete. A nice pretty printer could now print all the regs.

Quoted 5 lines

- faulting_func
- etc.

For this to work, we don't actually need the unwinder to explicitly know where pt_regs is.

That's true (but only for DWARF).

Quoted 7 lines
Quoted 4 lines

Food for thought, though: if user code does SYSENTER with TF set, you'll end up with partial pt_regs. There's nothing the kernel can do about it. DWARF will handle it without any fanfare, but I don't know if it's going to cause trouble for you pre-DWARF.

In this case it should see the stack pointer is past the pt_regs offset, so it would just report it as an empty stack.

OK

Quoted 22 lines
Quoted 6 lines

I'm also not sure it makes sense to apply this before the unwinder that can consume it is ready. Maybe, if it would be consistent with your plans, it would make sense to rewrite the unwinder first, then apply this and teach live patching to use the new unwinder, and *then* add DWARF support?

For the purposes of livepatch, the reliable unwinder needs to detect whether an interrupt/exception pt_regs frame exists on a sleeping task (or current). This patch would allow us to do that.

So my preferred order of doing things would be:

1) Brian Gerst's switch_to() cleanup and any related unwinder fixes
2) this patch for annotating pt_regs stack frames
3) reliable unwinder, similar to what I already posted, except it relies on this patch instead of PF_PREEMPT_IRQ, and knows how to deal with the new inactive task frame format of #1
4) livepatch consistency model which uses the reliable unwinder
5) rewrite unwinder, and port all users to the new interface
6) add DWARF unwinder

Quoted 2 lines

1-4 are pretty much already written, whereas 5 and 6 will take considerably more work.

Fair enough.

Quoted 10 lines
Quoted 10 lines
Quoted 9 lines
arch/x86/entry/calling.h +8 -0
+       /*
+        * Create a stack frame for the saved pt_regs.  This allows frame
+        * pointer based unwinders to find pt_regs on the stack.
+        */
+       .macro CREATE_PT_REGS_FRAME regs=%rsp
+#ifdef CONFIG_FRAME_POINTER
+       pushq   \regs
+       pushq   $pt_regs+1
Quoted 5 lines
Quoted 1 line

Why the +1?

Some unwinders like gdb are smart enough to report the function which contains the instruction *before* the return address. Without the +1, they would show the wrong function.

Lovely. Want to add a comment?

Quoted 6 lines
Quoted 6 lines
Quoted 5 lines
arch/x86/entry/calling.h +4 -0
+       pushq   %rbp
+       movq    %rsp, %rbp
+#endif
+       .endm
Quoted 7 lines
Quoted 4 lines

I keep wanting this to be only two pushes and to fudge rbp to make it work, but I don't see a good way. But let's call it CREATE_NESTED_ENTRY_FRAME or something, and let's rename pt_regs to nested_frame or similar.

Or, if we aren't going to annotate syscall pt_regs, we could give it a more specific name. CREATE_INTERRUPT_FRAME and interrupt_frame()?

CREATE_INTERRUPT_FRAME is confusing because it applies to idtentry, too. CREATE_PT_REGS_FRAME is probably fine.

Quoted 12 lines
Quoted 11 lines

arch/x86/entry/entry_64.S ENTRY(ignore_sysret) · line 1436

Quoted 8 lines
arch/x86/entry/entry_64.S +8 -0
+
+/* fake function which allows stack unwinders to detect pt_regs frames */
+#ifdef CONFIG_FRAME_POINTER
+ENTRY(pt_regs)
+       nop
+       nop
+ENDPROC(pt_regs)
+#endif /* CONFIG_FRAME_POINTER */

Why is this two bytes long? Is there some reason it has to be more than one byte?

Quoted 4 lines

Similar to above, this is related to the need to support various unwinders. Whether the unwinder displays the ret_addr or the instruction preceding it, either way the instruction needs to be inside the function for the function to be reported.

OK.

--Andy

#113 lore raw

RE: livepatch: change to a per-task consistency model

In reply to Jiri Kosina

From: Jiri Kosina=20

Quoted 5 lines

Sent: 23 May 2016 19:45

Quoted 2 lines

Related, please can we have a flag for the sleep and/or process so that an uninterruptible sleep doesn't trigger the 'hung task' detector

=20
TASK_KILLABLE

Not sure that does what I want.
It appears to allow some 'kill' actions to wake the process. I'm sure I've looked at the 'hung task' code since 2007.

Quoted 3 lines
Quoted 1 line

and also stops the process counting towards the 'load average'.

=20
TASK_NOLOAD

Ah, that was added in May 2015.
Not surprising I didn't know about it.

I'll leave the code doing:
set_current_state(signal_pending(current) ? TASK_UNINTERRUPTIBLE ? TASK_I=
NTERRUPTIBLE);
for a while longer.

David

#114 lore raw

RE: livepatch: change to a per-task consistency model

Quoted 8 lines · David Laight
Quoted 4 lines
Quoted 2 lines

Related, please can we have a flag for the sleep and/or process so that an uninterruptible sleep doesn't trigger the 'hung task' detector

TASK_KILLABLE

Not sure that does what I want.
It appears to allow some 'kill' actions to wake the process. I'm sure I've looked at the 'hung task' code since 2007.

The trick is the

	if (t->state == TASK_UNINTERRUPTIBLE)

test in check_hung_uninterruptible_tasks(). That makes sure that TASK_KILLABLE tasks (e.g. waiting on NFS I/O, but not limited only to that; feel free to set it whereever you need) are skipped. Please note that TASK_KILLABLE is actually a _mask_ that includes TASK_UNINTERRUPTIBLE as well; therefore the '==' test skips such tasks.

Signature · 2 lines
Jiri Kosina
SUSE Labs
maintainer
#115 lore raw

Re: [RFC PATCH v2 17/18] livepatch: change to a per-task consistency model

In reply to Josh Poimboeuf
Quoted 10 lines · Josh Poimboeuf

Change livepatch to use a basic per-task consistency model. This is the foundation which will eventually enable us to patch those ~10% of security patches which change function or data semantics. This is the biggest remaining piece needed to make livepatch more generally useful.

kernel/livepatch/transition.c new file +0 -0
new file mode 100644
index 0000000..92819bb

kernel/livepatch/transition.c line 328

Quoted 9 lines
kernel/livepatch/transition.c new file +9 -0
+/*
+ * Try to safely switch a task to the target patch state.  If it's currently
+ * running, or it's sleeping on a to-be-patched or to-be-unpatched function, or
+ * if the stack is unreliable, return false.
+ */
+static bool klp_try_switch_task(struct task_struct *task)
+{
+	struct rq *rq;
+	unsigned long flags;

This should be of type "struct rq_flags". Otherwise, I get compilation warnings:

kernel/livepatch/transition.c: In function ‘klp_try_switch_task’:
kernel/livepatch/transition.c:349:2: warning: passing argument 2 of ‘task_rq_lock’ from incompatible pointer type [enabled by default]
rq = task_rq_lock(task, &flags);
^
In file included from kernel/livepatch/transition.c:24:0:
kernel/livepatch/../sched/sched.h:1468:12: note: expected ‘struct rq_flags *’ but argument is of type ‘long unsigned int *’
struct rq *task_rq_lock(struct task_struct *p, struct rq_flags *rf)

            ^

kernel/livepatch/transition.c:367:2: warning: passing argument 3 of ‘task_rq_unlock’ from incompatible pointer type [enabled by default]
task_rq_unlock(rq, task, &flags);
^
In file included from kernel/livepatch/transition.c:24:0:
kernel/livepatch/../sched/sched.h:1480:1: note: expected ‘struct rq_flags *’ but argument is of type ‘long unsigned int *’
task_rq_unlock(struct rq *rq, struct task_struct *p, struct rq_flags *rf)

And even runtime warnings from lockdep:

[  212.847548] WARNING: CPU: 1 PID: 3847 at kernel/locking/lockdep.c:3532 lock_release+0x431/0x480
[  212.847549] releasing a pinned lock
[  212.847550] Modules linked in: livepatch_sample(E+)
[  212.847555] CPU: 1 PID: 3847 Comm: modprobe Tainted: G            E K 4.7.0-rc1-next-20160602-4-default+ #336
[  212.847556] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Bochs 01/01/2011
[  212.847558]  0000000000000000 ffff880139823aa0 ffffffff814388dc ffff880139823af0
[  212.847562]  0000000000000000 ffff880139823ae0 ffffffff8106fad1 00000dcc82b11390
[  212.847565]  ffff88013fc978d8 ffffffff810eea1e ffff8800ba0ed6d0 0000000000000003
[  212.847569] Call Trace:
[  212.847572]  [<ffffffff814388dc>] dump_stack+0x85/0xc9
[  212.847575]  [<ffffffff8106fad1>] __warn+0xd1/0xf0
[  212.847578]  [<ffffffff810eea1e>] ? klp_try_switch_task.part.3+0x5e/0x2b0
[  212.847580]  [<ffffffff8106fb3f>] warn_slowpath_fmt+0x4f/0x60
[  212.847582]  [<ffffffff810cc151>] lock_release+0x431/0x480
[  212.847585]  [<ffffffff8101e258>] ? dump_trace+0x118/0x310
[  212.847588]  [<ffffffff8195d07c>] ? entry_SYSCALL_64_fastpath+0x1f/0xbd
[  212.847590]  [<ffffffff8195c8bf>] _raw_spin_unlock+0x1f/0x30
[  212.847600]  [<ffffffff810eea1e>] klp_try_switch_task.part.3+0x5e/0x2b0
[  212.847603]  [<ffffffff810ef0e4>] klp_try_complete_transition+0x84/0x190
[  212.847605]  [<ffffffff810ed370>] __klp_enable_patch+0xb0/0x130
[  212.847607]  [<ffffffff810ed445>] klp_enable_patch+0x55/0x80
[  212.847610]  [<ffffffffa0000030>] ? livepatch_cmdline_proc_show+0x30/0x30 [livepatch_sample]
[  212.847613]  [<ffffffffa0000061>] livepatch_init+0x31/0x70 [livepatch_sample]
[  212.847615]  [<ffffffffa0000030>] ? livepatch_cmdline_proc_show+0x30/0x30 [livepatch_sample]
[  212.847617]  [<ffffffff8100041d>] do_one_initcall+0x3d/0x160
[  212.847629]  [<ffffffff81196c9b>] ? do_init_module+0x27/0x1e4
[  212.847632]  [<ffffffff810e7172>] ? rcu_read_lock_sched_held+0x62/0x70
[  212.847634]  [<ffffffff811fdea2>] ? kmem_cache_alloc_trace+0x282/0x340
[  212.847636]  [<ffffffff81196cd4>] do_init_module+0x60/0x1e4
[  212.847638]  [<ffffffff81111fd2>] load_module+0x1482/0x1d40
[  212.847640]  [<ffffffff8110ea10>] ? __symbol_put+0x40/0x40
[  212.847643]  [<ffffffff81112aa9>] SYSC_finit_module+0xa9/0xd0
[  212.847645]  [<ffffffff81112aee>] SyS_finit_module+0xe/0x10
[  212.847647]  [<ffffffff8195d07c>] entry_SYSCALL_64_fastpath+0x1f/0xbd
[  212.847649] ---[ end trace e4e9f09d45443049 ]---
Quoted 14 lines
kernel/livepatch/transition.c new file +14 -0
+	int ret;
+	bool success = false;
+
+	/* check if this task has already switched over */
+	if (task->patch_state == klp_target_state)
+		return true;
+
+	/*
+	 * For arches which don't have reliable stack traces, we have to rely
+	 * on other methods (e.g., switching tasks at the syscall barrier).
+	 */
+	if (!IS_ENABLED(CONFIG_RELIABLE_STACKTRACE))
+		return false;
+

kernel/livepatch/transition.c line 352

Quoted 10 lines
kernel/livepatch/transition.c new file +10 -0
+	/*
+	 * Now try to check the stack for any to-be-patched or to-be-unpatched
+	 * functions.  If all goes well, switch the task to the target patch
+	 * state.
+	 */
+	rq = task_rq_lock(task, &flags);
+
+	if (task_running(rq, task) && task != current) {
+		pr_debug("%s: pid %d (%s) is running\n", __func__, task->pid,
+			 task->comm);

Also I think about using printk_deferred() inside the rq_lock but it is not strictly needed. Also we use only pr_debug() here which is a NOP when not enabled.

Best Regards,
Petr

#116 lore raw

Re: [RFC PATCH v2 17/18] livepatch: change to a per-task consistency model

Quoted 69 lines · Petr Mladek
Quoted 10 lines · Josh Poimboeuf

Change livepatch to use a basic per-task consistency model. This is the foundation which will eventually enable us to patch those ~10% of security patches which change function or data semantics. This is the biggest remaining piece needed to make livepatch more generally useful.

kernel/livepatch/transition.c new file +0 -0
new file mode 100644
index 0000000..92819bb

kernel/livepatch/transition.c line 328

Quoted 9 lines
kernel/livepatch/transition.c new file +9 -0
+/*
+ * Try to safely switch a task to the target patch state.  If it's currently
+ * running, or it's sleeping on a to-be-patched or to-be-unpatched function, or
+ * if the stack is unreliable, return false.
+ */
+static bool klp_try_switch_task(struct task_struct *task)
+{
+	struct rq *rq;
+	unsigned long flags;

This should be of type "struct rq_flags". Otherwise, I get compilation warnings:

kernel/livepatch/transition.c: In function ‘klp_try_switch_task’:
kernel/livepatch/transition.c:349:2: warning: passing argument 2 of ‘task_rq_lock’ from incompatible pointer type [enabled by default]
rq = task_rq_lock(task, &flags);
^
In file included from kernel/livepatch/transition.c:24:0:
kernel/livepatch/../sched/sched.h:1468:12: note: expected ‘struct rq_flags *’ but argument is of type ‘long unsigned int *’
struct rq *task_rq_lock(struct task_struct *p, struct rq_flags *rf)

            ^

kernel/livepatch/transition.c:367:2: warning: passing argument 3 of ‘task_rq_unlock’ from incompatible pointer type [enabled by default]
task_rq_unlock(rq, task, &flags);
^
In file included from kernel/livepatch/transition.c:24:0:
kernel/livepatch/../sched/sched.h:1480:1: note: expected ‘struct rq_flags *’ but argument is of type ‘long unsigned int *’
task_rq_unlock(struct rq *rq, struct task_struct *p, struct rq_flags *rf)

And even runtime warnings from lockdep:

[  212.847548] WARNING: CPU: 1 PID: 3847 at kernel/locking/lockdep.c:3532 lock_release+0x431/0x480
[  212.847549] releasing a pinned lock
[  212.847550] Modules linked in: livepatch_sample(E+)
[  212.847555] CPU: 1 PID: 3847 Comm: modprobe Tainted: G            E K 4.7.0-rc1-next-20160602-4-default+ #336
[  212.847556] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Bochs 01/01/2011
[  212.847558]  0000000000000000 ffff880139823aa0 ffffffff814388dc ffff880139823af0
[  212.847562]  0000000000000000 ffff880139823ae0 ffffffff8106fad1 00000dcc82b11390
[  212.847565]  ffff88013fc978d8 ffffffff810eea1e ffff8800ba0ed6d0 0000000000000003
[  212.847569] Call Trace:
[  212.847572]  [<ffffffff814388dc>] dump_stack+0x85/0xc9
[  212.847575]  [<ffffffff8106fad1>] __warn+0xd1/0xf0
[  212.847578]  [<ffffffff810eea1e>] ? klp_try_switch_task.part.3+0x5e/0x2b0
[  212.847580]  [<ffffffff8106fb3f>] warn_slowpath_fmt+0x4f/0x60
[  212.847582]  [<ffffffff810cc151>] lock_release+0x431/0x480
[  212.847585]  [<ffffffff8101e258>] ? dump_trace+0x118/0x310
[  212.847588]  [<ffffffff8195d07c>] ? entry_SYSCALL_64_fastpath+0x1f/0xbd
[  212.847590]  [<ffffffff8195c8bf>] _raw_spin_unlock+0x1f/0x30
[  212.847600]  [<ffffffff810eea1e>] klp_try_switch_task.part.3+0x5e/0x2b0
[  212.847603]  [<ffffffff810ef0e4>] klp_try_complete_transition+0x84/0x190
[  212.847605]  [<ffffffff810ed370>] __klp_enable_patch+0xb0/0x130
[  212.847607]  [<ffffffff810ed445>] klp_enable_patch+0x55/0x80
[  212.847610]  [<ffffffffa0000030>] ? livepatch_cmdline_proc_show+0x30/0x30 [livepatch_sample]
[  212.847613]  [<ffffffffa0000061>] livepatch_init+0x31/0x70 [livepatch_sample]
[  212.847615]  [<ffffffffa0000030>] ? livepatch_cmdline_proc_show+0x30/0x30 [livepatch_sample]
[  212.847617]  [<ffffffff8100041d>] do_one_initcall+0x3d/0x160
[  212.847629]  [<ffffffff81196c9b>] ? do_init_module+0x27/0x1e4
[  212.847632]  [<ffffffff810e7172>] ? rcu_read_lock_sched_held+0x62/0x70
Quoted 8 lines
[  212.847634]  [<ffffffff811fdea2>] ? kmem_cache_alloc_trace+0x282/0x340
[  212.847636]  [<ffffffff81196cd4>] do_init_module+0x60/0x1e4
[  212.847638]  [<ffffffff81111fd2>] load_module+0x1482/0x1d40
[  212.847640]  [<ffffffff8110ea10>] ? __symbol_put+0x40/0x40
[  212.847643]  [<ffffffff81112aa9>] SYSC_finit_module+0xa9/0xd0
[  212.847645]  [<ffffffff81112aee>] SyS_finit_module+0xe/0x10
[  212.847647]  [<ffffffff8195d07c>] entry_SYSCALL_64_fastpath+0x1f/0xbd
[  212.847649] ---[ end trace e4e9f09d45443049 ]---

Thanks, I also saw this when rebasing onto a newer linux-next.

Quoted 20 lines
Quoted 20 lines
kernel/livepatch/transition.c new file +20 -0
+	int ret;
+	bool success = false;
+
+	/* check if this task has already switched over */
+	if (task->patch_state == klp_target_state)
+		return true;
+
+	/*
+	 * For arches which don't have reliable stack traces, we have to rely
+	 * on other methods (e.g., switching tasks at the syscall barrier).
+	 */
+	if (!IS_ENABLED(CONFIG_RELIABLE_STACKTRACE))
+		return false;
+
+	/*
+	 * Now try to check the stack for any to-be-patched or to-be-unpatched
+	 * functions.  If all goes well, switch the task to the target patch
+	 * state.
+	 */
+	rq = task_rq_lock(task, &flags);
Quoted 8 lines

kernel/livepatch/transition.c line 352

Quoted 4 lines
kernel/livepatch/transition.c new file +4 -0
+
+	if (task_running(rq, task) && task != current) {
+		pr_debug("%s: pid %d (%s) is running\n", __func__, task->pid,
+			 task->comm);

Also I think about using printk_deferred() inside the rq_lock but it is not strictly needed. Also we use only pr_debug() here which is a NOP when not enabled.

Good catch. It's probably best to avoid it anyway. klp_check_stack() also has some pr_debug() calls. I may restructure the code a bit to release the lock before doing any of the pr_debug()'s.

Signature · 1 line
Josh
#47 lore raw

Re: [RFC PATCH v2 05/18] sched: add task flag for preempt IRQ tracking

In reply to Andy Lutomirski
Quoted 112 lines · Andy Lutomirski
Quoted 27 lines · Josh Poimboeuf
Quoted 1 line

Maybe I'm coming around to liking this idea.

Ok, good :-)

Quoted 15 lines

In an ideal world (DWARF support, high-quality unwinder, nice pretty printer, etc), unwinding across a kernel exception would look like:

- some_func
- some_other_func
- do_page_fault
- page_fault

After page_fault, the next unwind step takes us to the faulting RIP (faulting_func) and reports that all GPRs are known. It should probably learn this fact from DWARF if DWARF is available, instead of directly decoding pt_regs, due to a few funny cases in which pt_regs may be incomplete. A nice pretty printer could now print all the regs.

Quoted 5 lines

- faulting_func
- etc.

For this to work, we don't actually need the unwinder to explicitly know where pt_regs is.

That's true (but only for DWARF).

Quoted 7 lines
Quoted 4 lines

Food for thought, though: if user code does SYSENTER with TF set, you'll end up with partial pt_regs. There's nothing the kernel can do about it. DWARF will handle it without any fanfare, but I don't know if it's going to cause trouble for you pre-DWARF.

In this case it should see the stack pointer is past the pt_regs offset, so it would just report it as an empty stack.

OK

Quoted 22 lines
Quoted 6 lines

I'm also not sure it makes sense to apply this before the unwinder that can consume it is ready. Maybe, if it would be consistent with your plans, it would make sense to rewrite the unwinder first, then apply this and teach live patching to use the new unwinder, and *then* add DWARF support?

For the purposes of livepatch, the reliable unwinder needs to detect whether an interrupt/exception pt_regs frame exists on a sleeping task (or current). This patch would allow us to do that.

So my preferred order of doing things would be:

1) Brian Gerst's switch_to() cleanup and any related unwinder fixes
2) this patch for annotating pt_regs stack frames
3) reliable unwinder, similar to what I already posted, except it relies on this patch instead of PF_PREEMPT_IRQ, and knows how to deal with the new inactive task frame format of #1
4) livepatch consistency model which uses the reliable unwinder
5) rewrite unwinder, and port all users to the new interface
6) add DWARF unwinder

Quoted 2 lines

1-4 are pretty much already written, whereas 5 and 6 will take considerably more work.

Fair enough.

Quoted 10 lines
Quoted 10 lines
Quoted 9 lines
(quoted diff) +8 -0
+       /*
+        * Create a stack frame for the saved pt_regs.  This allows frame
+        * pointer based unwinders to find pt_regs on the stack.
+        */
+       .macro CREATE_PT_REGS_FRAME regs=%rsp
+#ifdef CONFIG_FRAME_POINTER
+       pushq   \regs
+       pushq   $pt_regs+1
Quoted 5 lines
Quoted 1 line

Why the +1?

Some unwinders like gdb are smart enough to report the function which contains the instruction *before* the return address. Without the +1, they would show the wrong function.

Lovely. Want to add a comment?

Quoted 6 lines
Quoted 6 lines
Quoted 5 lines
(quoted diff) +4 -0
+       pushq   %rbp
+       movq    %rsp, %rbp
+#endif
+       .endm
Quoted 7 lines
Quoted 4 lines

I keep wanting this to be only two pushes and to fudge rbp to make it work, but I don't see a good way. But let's call it CREATE_NESTED_ENTRY_FRAME or something, and let's rename pt_regs to nested_frame or similar.

Or, if we aren't going to annotate syscall pt_regs, we could give it a more specific name. CREATE_INTERRUPT_FRAME and interrupt_frame()?

CREATE_INTERRUPT_FRAME is confusing because it applies to idtentry, too. CREATE_PT_REGS_FRAME is probably fine.

Quoted 12 lines
Quoted 11 lines

Review

Quoted 8 lines
(quoted diff) +8 -0
+
+/* fake function which allows stack unwinders to detect pt_regs frames */
+#ifdef CONFIG_FRAME_POINTER
+ENTRY(pt_regs)
+       nop
+       nop
+ENDPROC(pt_regs)
+#endif /* CONFIG_FRAME_POINTER */

Why is this two bytes long? Is there some reason it has to be more than one byte?

Quoted 6 lines
Quoted 4 lines

Similar to above, this is related to the need to support various unwinders. Whether the unwinder displays the ret_addr or the instruction preceding it, either way the instruction needs to be inside the function for the function to be reported.

OK.

Andy,

So I got a chance to look at this some more. I'm thinking that to make this feature more consistently useful, we shouldn't only annotate pt_regs frames for calls to handlers; other calls should be annotated as
well: preempt_schedule_irq, CALL_enter_from_user_mode, prepare_exit_to_usermode, SWAPGS, TRACE_IRQS_OFF, DISABLE_INTERRUPTS, etc. That way, the unwinder will always be able to find pt_regs from an interrupt/exception, even if starting from one of these other calls.

But then, things get ugly. You have to either setup and tear down the frame for every possible call, or do a higher-level setup/teardown across multiple calls, which invalidates several assumptions in the entry code about the location of pt_regs on the stack.

Also problematic is that several of the macros (like TRACE_IRQS_IRETQ) make assumptions about the location of pt_regs. And they're used by both syscall and interrupt code. So if we didn't create a frame pointer header for syscalls, we'd basically need two versions of the macros: one for irqs/exceptions and one for syscalls.

So I think the cleanest way to handle this is to always allocate two extra registers on the stack in ALLOC_PT_GPREGS_ON_STACK. Then all entry code can assume that pt_regs is at a constant location, and all the above problems go away. Another benefit is that we'd only need two saves instead of three -- the pointer to pt_regs is no longer needed since pt_regs is always immediately after the frame header.

I worked up a patch to implement this -- see below. It writes the frame pointer in all entry paths, including syscalls. This helps keep the code simple.

The downside is a small performance penalty: with getppid()-in-a-loop on my laptop, the average syscall went from 52ns to 53ns, which is about a 2% slowdown. But I doubt it would be measurable in a real-world workload.

It looks like about half the slowdown is due to the extra stack allocation (which presumably adds a little d-cache pressure on the stack memory) and the other half is due to the stack writes.

I could remove the writes from the syscall path but it would only save about half a ns, and it would make the code less robust. Plus it's nice to have the consistency of having *all* pt_regs frames annotated.

Thoughts?

----

arch/x86/entry/calling.h +91 -36
index 9a9e588..0f6ccfc 100644
@@ -88,30 +88,69 @@ For 32-bit we have the following conventions - kernel is built with
 #define RSP		19*8
 #define SS		20*8
 
-#define SIZEOF_PTREGS	21*8
 
-	.macro ALLOC_PT_GPREGS_ON_STACK addskip=0
-	addq	$-(15*8+\addskip), %rsp
+#ifdef CONFIG_FRAME_POINTER
+
+#define PT_REGS_OFFSET 2*8
+
+	/*
+	 * Create an entry stack frame pointer header which corresponds to the
+	 * saved pt_regs.  This allows frame pointer based unwinders to find
+	 * pt_regs on the stack.  The frame pointer and the return address of a
+	 * fake function are stored immediately before the pt_regs.
+	 */
+	.macro SAVE_ENTRY_FRAME_POINTER
+	movq	%rbp,			0*8(%rsp)
+	movq	$entry_frame_ret,	1*8(%rsp)
+	movq	%rsp, %rbp
+	.endm
+
+	.macro RESTORE_ENTRY_FRAME_POINTER
+	movq	(%rsp), %rbp
+	.endm
+
+	.macro ALLOC_AND_SAVE_ENTRY_FRAME_POINTER
+	subq	$PT_REGS_OFFSET, %rsp
+	SAVE_ENTRY_FRAME_POINTER
+	.endm
+
+#else /* !CONFIG_FRAME_POINTER */
+
+#define PT_REGS_OFFSET 0
+#define SAVE_ENTRY_FRAME_POINTER
+#define RESTORE_ENTRY_FRAME_POINTER
+#define ALLOC_AND_SAVE_ENTRY_FRAME_POINTER
+
+#endif /* CONFIG_FRAME_POINTER */
+
+#define PT_REGS_SIZE		21*8
+#define ENTRY_FRAME_SIZE	(PT_REGS_OFFSET+PT_REGS_SIZE)
+
+#define TI_FLAGS(rsp) ASM_THREAD_INFO(TI_flags, rsp, ENTRY_FRAME_SIZE)
+#define PT_REGS(reg) reg+PT_REGS_OFFSET(%rsp)
+
+	.macro ALLOC_ENTRY_FRAME addskip=0
+	addq	$-(15*8+PT_REGS_OFFSET+\addskip), %rsp
 	.endm
 
 	.macro SAVE_C_REGS_HELPER offset=0 rax=1 rcx=1 r8910=1 r11=1
 	.if \r11
-	movq %r11, 6*8+\offset(%rsp)
+	movq %r11, 6*8+PT_REGS_OFFSET+\offset(%rsp)
 	.endif
 	.if \r8910
-	movq %r10, 7*8+\offset(%rsp)
-	movq %r9,  8*8+\offset(%rsp)
-	movq %r8,  9*8+\offset(%rsp)
+	movq %r10, 7*8+PT_REGS_OFFSET+\offset(%rsp)
+	movq %r9,  8*8+PT_REGS_OFFSET+\offset(%rsp)
+	movq %r8,  9*8+PT_REGS_OFFSET+\offset(%rsp)
 	.endif
 	.if \rax
-	movq %rax, 10*8+\offset(%rsp)
+	movq %rax, 10*8+PT_REGS_OFFSET+\offset(%rsp)
 	.endif
 	.if \rcx
-	movq %rcx, 11*8+\offset(%rsp)
+	movq %rcx, 11*8+PT_REGS_OFFSET+\offset(%rsp)
 	.endif
-	movq %rdx, 12*8+\offset(%rsp)
-	movq %rsi, 13*8+\offset(%rsp)
-	movq %rdi, 14*8+\offset(%rsp)
+	movq %rdx, 12*8+PT_REGS_OFFSET+\offset(%rsp)
+	movq %rsi, 13*8+PT_REGS_OFFSET+\offset(%rsp)
+	movq %rdi, 14*8+PT_REGS_OFFSET+\offset(%rsp)
 	.endm
 	.macro SAVE_C_REGS offset=0
 	SAVE_C_REGS_HELPER \offset, 1, 1, 1, 1
@@ -128,23 +167,39 @@ For 32-bit we have the following conventions - kernel is built with
 	.macro SAVE_C_REGS_EXCEPT_RAX_RCX_R11
 	SAVE_C_REGS_HELPER 0, 0, 0, 1, 0
 	.endm
+	.macro SAVE_C_REGS_EXCEPT_RAX
+	SAVE_C_REGS_HELPER rax=0
+	.endm
+
+	.macro SAVE_EXTRA_REGS offset=0 rbx=1
+	movq %r15, 0*8+PT_REGS_OFFSET+\offset(%rsp)
+	movq %r14, 1*8+PT_REGS_OFFSET+\offset(%rsp)
+	movq %r13, 2*8+PT_REGS_OFFSET+\offset(%rsp)
+	movq %r12, 3*8+PT_REGS_OFFSET+\offset(%rsp)
+#ifdef CONFIG_FRAME_POINTER
+	/* copy rbp value from entry frame header */
+	movq \offset(%rsp), %rbp
+	movq %rbp, 4*8+PT_REGS_OFFSET+\offset(%rsp)
+	leaq \offset(%rsp), %rbp
+#else
+	movq %rbp, 4*8+PT_REGS_OFFSET+\offset(%rsp)
+#endif
+	.if \rbx
+	movq %rbx, 5*8+PT_REGS_OFFSET+\offset(%rsp)
+	.endif
+	.endm
 
-	.macro SAVE_EXTRA_REGS offset=0
-	movq %r15, 0*8+\offset(%rsp)
-	movq %r14, 1*8+\offset(%rsp)
-	movq %r13, 2*8+\offset(%rsp)
-	movq %r12, 3*8+\offset(%rsp)
-	movq %rbp, 4*8+\offset(%rsp)
-	movq %rbx, 5*8+\offset(%rsp)
+	.macro SAVE_EXTRA_REGS_EXCEPT_RBX
+	SAVE_EXTRA_REGS rbx=0
 	.endm
 
 	.macro RESTORE_EXTRA_REGS offset=0
-	movq 0*8+\offset(%rsp), %r15
-	movq 1*8+\offset(%rsp), %r14
-	movq 2*8+\offset(%rsp), %r13
-	movq 3*8+\offset(%rsp), %r12
-	movq 4*8+\offset(%rsp), %rbp
-	movq 5*8+\offset(%rsp), %rbx
+	movq 0*8+PT_REGS_OFFSET+\offset(%rsp), %r15
+	movq 1*8+PT_REGS_OFFSET+\offset(%rsp), %r14
+	movq 2*8+PT_REGS_OFFSET+\offset(%rsp), %r13
+	movq 3*8+PT_REGS_OFFSET+\offset(%rsp), %r12
+	movq 4*8+PT_REGS_OFFSET+\offset(%rsp), %rbp
+	movq 5*8+PT_REGS_OFFSET+\offset(%rsp), %rbx
 	.endm
 
 	.macro ZERO_EXTRA_REGS
@@ -158,24 +213,24 @@ For 32-bit we have the following conventions - kernel is built with
 
 	.macro RESTORE_C_REGS_HELPER rstor_rax=1, rstor_rcx=1, rstor_r11=1, rstor_r8910=1, rstor_rdx=1
 	.if \rstor_r11
-	movq 6*8(%rsp), %r11
+	movq 6*8+PT_REGS_OFFSET(%rsp), %r11
 	.endif
 	.if \rstor_r8910
-	movq 7*8(%rsp), %r10
-	movq 8*8(%rsp), %r9
-	movq 9*8(%rsp), %r8
+	movq 7*8+PT_REGS_OFFSET(%rsp), %r10
+	movq 8*8+PT_REGS_OFFSET(%rsp), %r9
+	movq 9*8+PT_REGS_OFFSET(%rsp), %r8
 	.endif
 	.if \rstor_rax
-	movq 10*8(%rsp), %rax
+	movq 10*8+PT_REGS_OFFSET(%rsp), %rax
 	.endif
 	.if \rstor_rcx
-	movq 11*8(%rsp), %rcx
+	movq 11*8+PT_REGS_OFFSET(%rsp), %rcx
 	.endif
 	.if \rstor_rdx
-	movq 12*8(%rsp), %rdx
+	movq 12*8+PT_REGS_OFFSET(%rsp), %rdx
 	.endif
-	movq 13*8(%rsp), %rsi
-	movq 14*8(%rsp), %rdi
+	movq 13*8+PT_REGS_OFFSET(%rsp), %rsi
+	movq 14*8+PT_REGS_OFFSET(%rsp), %rdi
 	.endm
 	.macro RESTORE_C_REGS
 	RESTORE_C_REGS_HELPER 1,1,1,1,1
@@ -193,8 +248,8 @@ For 32-bit we have the following conventions - kernel is built with
 	RESTORE_C_REGS_HELPER 1,0,0,1,1
 	.endm
 
-	.macro REMOVE_PT_GPREGS_FROM_STACK addskip=0
-	subq $-(15*8+\addskip), %rsp
+	.macro FREE_ENTRY_FRAME addskip=0
+	subq $-(15*8+PT_REGS_OFFSET+\addskip), %rsp
 	.endm
 
 	.macro icebp
arch/x86/entry/entry_64.S +84 -72

386 lines not on this page · raw message

arch/x86/entry/entry_64_compat.S +11 -16
index e1721da..31b4f63c 100644
@@ -309,21 +309,16 @@ ENTRY(entry_INT80_compat)
 
 	/* Construct struct pt_regs on stack (iret frame is already on stack) */
 	pushq	%rax			/* pt_regs->orig_ax */
-	pushq	%rdi			/* pt_regs->di */
-	pushq	%rsi			/* pt_regs->si */
-	pushq	%rdx			/* pt_regs->dx */
-	pushq	%rcx			/* pt_regs->cx */
-	pushq	$-ENOSYS		/* pt_regs->ax */
-	pushq   $0			/* pt_regs->r8  = 0 */
-	pushq   $0			/* pt_regs->r9  = 0 */
-	pushq   $0			/* pt_regs->r10 = 0 */
-	pushq   $0			/* pt_regs->r11 = 0 */
-	pushq   %rbx                    /* pt_regs->rbx */
-	pushq   %rbp                    /* pt_regs->rbp */
-	pushq   %r12                    /* pt_regs->r12 */
-	pushq   %r13                    /* pt_regs->r13 */
-	pushq   %r14                    /* pt_regs->r14 */
-	pushq   %r15                    /* pt_regs->r15 */
+	ALLOC_ENTRY_FRAME
+	SAVE_ENTRY_FRAME_POINTER
+	movq	$-ENOSYS, %rax
+	xorq	%r8, %r8
+	xorq	%r9, %r9
+	xorq	%r10, %r10
+	xorq	%r11, %r11
+	SAVE_C_REGS
+	SAVE_EXTRA_REGS
+
 	cld
 
 	/*
@@ -332,7 +327,7 @@ ENTRY(entry_INT80_compat)
 	 */
 	TRACE_IRQS_OFF
 
-	movq	%rsp, %rdi
+	leaq	PT_REGS_OFFSET(%rsp), %rdi
 	call	do_int80_syscall_32
 .Lsyscall_32_done:
 
arch/x86/include/asm/traps.h +0 -1
index c3496619..bba7ece 100644
@@ -70,7 +70,6 @@ dotraplinkage void do_segment_not_present(struct pt_regs *, long);
 dotraplinkage void do_stack_segment(struct pt_regs *, long);
 #ifdef CONFIG_X86_64
 dotraplinkage void do_double_fault(struct pt_regs *, long);
-asmlinkage struct pt_regs *sync_regs(struct pt_regs *);
 #endif
 dotraplinkage void do_general_protection(struct pt_regs *, long);
 dotraplinkage void do_page_fault(struct pt_regs *, unsigned long);
arch/x86/kernel/head_64.S +12 -19
index 5df831e..f3c7922 100644
@@ -362,24 +362,15 @@ early_idt_handler_common:
 	incl early_recursion_flag(%rip)
 
 	/* The vector number is currently in the pt_regs->di slot. */
-	pushq %rsi				/* pt_regs->si */
-	movq 8(%rsp), %rsi			/* RSI = vector number */
-	movq %rdi, 8(%rsp)			/* pt_regs->di = RDI */
-	pushq %rdx				/* pt_regs->dx */
-	pushq %rcx				/* pt_regs->cx */
-	pushq %rax				/* pt_regs->ax */
-	pushq %r8				/* pt_regs->r8 */
-	pushq %r9				/* pt_regs->r9 */
-	pushq %r10				/* pt_regs->r10 */
-	pushq %r11				/* pt_regs->r11 */
-	pushq %rbx				/* pt_regs->bx */
-	pushq %rbp				/* pt_regs->bp */
-	pushq %r12				/* pt_regs->r12 */
-	pushq %r13				/* pt_regs->r13 */
-	pushq %r14				/* pt_regs->r14 */
-	pushq %r15				/* pt_regs->r15 */
-
-	cmpq $14,%rsi		/* Page fault? */
+	ALLOC_ENTRY_FRAME addskip=-8
+	SAVE_ENTRY_FRAME_POINTER
+	movq %rbx, PT_REGS(RBX)	/* save rbx */
+	movq PT_REGS(RDI), %rbx	/* rbx = vector number */
+
+	SAVE_C_REGS
+	SAVE_EXTRA_REGS_EXCEPT_RBX
+
+	cmpq $14, %rbx		/* page fault? */
 	jnz 10f
 	GET_CR2_INTO(%rdi)	/* Can clobber any volatile register if pv */
 	call early_make_pgtable
@@ -387,7 +378,9 @@ early_idt_handler_common:
 	jz 20f			/* All good */
 
 10:
-	movq %rsp,%rdi		/* RDI = pt_regs; RSI is already trapnr */
+	movq %rsp, %rdi
+	addq $PT_REGS_OFFSET, %rdi /* rdi = pt_regs */
+	movq %rbx, %rsi		   /* rsi = vector number */
 	call early_fixup_exception
 
 20:
arch/x86/kernel/traps.c +22 -10
index 00f03d8..6954e74 100644
@@ -514,22 +514,34 @@ exit:
 NOKPROBE_SYMBOL(do_int3);
 
 #ifdef CONFIG_X86_64
+
+struct entry_frame {
+#ifdef CONFIG_FRAME_POINTER
+	void *fp;
+	void *ret_addr;
+#endif
+	struct pt_regs regs;
+};
+
 /*
  * Help handler running on IST stack to switch off the IST stack if the
  * interrupted code was in user mode. The actual stack switch is done in
  * entry_64.S
  */
-asmlinkage __visible notrace struct pt_regs *sync_regs(struct pt_regs *eregs)
+asmlinkage __visible notrace
+struct entry_frame *sync_entry_frame(struct entry_frame *old)
 {
-	struct pt_regs *regs = task_pt_regs(current);
-	*regs = *eregs;
-	return regs;
+	struct entry_frame *new = container_of(task_pt_regs(current),
+					       struct entry_frame, regs);
+
+	*new = *old;
+	return new;
 }
-NOKPROBE_SYMBOL(sync_regs);
+NOKPROBE_SYMBOL(sync_entry_frame);
 
 struct bad_iret_stack {
 	void *error_entry_ret;
-	struct pt_regs regs;
+	struct entry_frame frame;
 };
 
 asmlinkage __visible notrace
@@ -544,15 +556,15 @@ struct bad_iret_stack *fixup_bad_iret(struct bad_iret_stack *s)
 	 */
 	struct bad_iret_stack *new_stack =
 		container_of(task_pt_regs(current),
-			     struct bad_iret_stack, regs);
+			     struct bad_iret_stack, frame.regs);
 
 	/* Copy the IRET target to the new stack. */
-	memmove(&new_stack->regs.ip, (void *)s->regs.sp, 5*8);
+	memmove(&new_stack->frame.regs.ip, (void *)s->frame.regs.sp, 5*8);
 
 	/* Copy the remainder of the stack from the current stack. */
-	memmove(new_stack, s, offsetof(struct bad_iret_stack, regs.ip));
+	memmove(new_stack, s, offsetof(struct bad_iret_stack, frame.regs.ip));
 
-	BUG_ON(!user_mode(&new_stack->regs));
+	BUG_ON(!user_mode(&new_stack->frame.regs));
 	return new_stack;
 }
 NOKPROBE_SYMBOL(fixup_bad_iret);
#48 lore raw

Re: [RFC PATCH v2 05/18] sched: add task flag for preempt IRQ tracking

Quoted 157 lines · Josh Poimboeuf
Quoted 112 lines · Andy Lutomirski
Quoted 27 lines · Josh Poimboeuf
Quoted 1 line

Maybe I'm coming around to liking this idea.

Ok, good :-)

Quoted 15 lines

In an ideal world (DWARF support, high-quality unwinder, nice pretty printer, etc), unwinding across a kernel exception would look like:

- some_func
- some_other_func
- do_page_fault
- page_fault

After page_fault, the next unwind step takes us to the faulting RIP (faulting_func) and reports that all GPRs are known. It should probably learn this fact from DWARF if DWARF is available, instead of directly decoding pt_regs, due to a few funny cases in which pt_regs may be incomplete. A nice pretty printer could now print all the regs.

Quoted 5 lines

- faulting_func
- etc.

For this to work, we don't actually need the unwinder to explicitly know where pt_regs is.

That's true (but only for DWARF).

Quoted 7 lines
Quoted 4 lines

Food for thought, though: if user code does SYSENTER with TF set, you'll end up with partial pt_regs. There's nothing the kernel can do about it. DWARF will handle it without any fanfare, but I don't know if it's going to cause trouble for you pre-DWARF.

In this case it should see the stack pointer is past the pt_regs offset, so it would just report it as an empty stack.

OK

Quoted 22 lines
Quoted 6 lines

I'm also not sure it makes sense to apply this before the unwinder that can consume it is ready. Maybe, if it would be consistent with your plans, it would make sense to rewrite the unwinder first, then apply this and teach live patching to use the new unwinder, and *then* add DWARF support?

For the purposes of livepatch, the reliable unwinder needs to detect whether an interrupt/exception pt_regs frame exists on a sleeping task (or current). This patch would allow us to do that.

So my preferred order of doing things would be:

1) Brian Gerst's switch_to() cleanup and any related unwinder fixes
2) this patch for annotating pt_regs stack frames
3) reliable unwinder, similar to what I already posted, except it relies on this patch instead of PF_PREEMPT_IRQ, and knows how to deal with the new inactive task frame format of #1
4) livepatch consistency model which uses the reliable unwinder
5) rewrite unwinder, and port all users to the new interface
6) add DWARF unwinder

Quoted 2 lines

1-4 are pretty much already written, whereas 5 and 6 will take considerably more work.

Fair enough.

Quoted 10 lines
Quoted 10 lines
Quoted 9 lines
(quoted diff) +8 -0
+       /*
+        * Create a stack frame for the saved pt_regs.  This allows frame
+        * pointer based unwinders to find pt_regs on the stack.
+        */
+       .macro CREATE_PT_REGS_FRAME regs=%rsp
+#ifdef CONFIG_FRAME_POINTER
+       pushq   \regs
+       pushq   $pt_regs+1
Quoted 5 lines
Quoted 1 line

Why the +1?

Some unwinders like gdb are smart enough to report the function which contains the instruction *before* the return address. Without the +1, they would show the wrong function.

Lovely. Want to add a comment?

Quoted 6 lines
Quoted 6 lines
Quoted 5 lines
arch/x86/entry/calling.h +4 -0
+       pushq   %rbp
+       movq    %rsp, %rbp
+#endif
+       .endm
Quoted 7 lines
Quoted 4 lines

I keep wanting this to be only two pushes and to fudge rbp to make it work, but I don't see a good way. But let's call it CREATE_NESTED_ENTRY_FRAME or something, and let's rename pt_regs to nested_frame or similar.

Or, if we aren't going to annotate syscall pt_regs, we could give it a more specific name. CREATE_INTERRUPT_FRAME and interrupt_frame()?

CREATE_INTERRUPT_FRAME is confusing because it applies to idtentry, too. CREATE_PT_REGS_FRAME is probably fine.

Quoted 12 lines
Quoted 11 lines

arch/x86/entry/entry_64.S ENTRY(ignore_sysret) · line 1469

Quoted 8 lines
arch/x86/entry/entry_64.S +8 -0
+
+/* fake function which allows stack unwinders to detect pt_regs frames */
+#ifdef CONFIG_FRAME_POINTER
+ENTRY(pt_regs)
+       nop
+       nop
+ENDPROC(pt_regs)
+#endif /* CONFIG_FRAME_POINTER */

Why is this two bytes long? Is there some reason it has to be more than one byte?

Quoted 6 lines
Quoted 4 lines

Similar to above, this is related to the need to support various unwinders. Whether the unwinder displays the ret_addr or the instruction preceding it, either way the instruction needs to be inside the function for the function to be reported.

OK.

Andy,

So I got a chance to look at this some more. I'm thinking that to make this feature more consistently useful, we shouldn't only annotate pt_regs frames for calls to handlers; other calls should be annotated as
well: preempt_schedule_irq, CALL_enter_from_user_mode, prepare_exit_to_usermode, SWAPGS, TRACE_IRQS_OFF, DISABLE_INTERRUPTS, etc. That way, the unwinder will always be able to find pt_regs from an interrupt/exception, even if starting from one of these other calls.

But then, things get ugly. You have to either setup and tear down the frame for every possible call, or do a higher-level setup/teardown across multiple calls, which invalidates several assumptions in the entry code about the location of pt_regs on the stack.

Also problematic is that several of the macros (like TRACE_IRQS_IRETQ) make assumptions about the location of pt_regs. And they're used by both syscall and interrupt code. So if we didn't create a frame pointer header for syscalls, we'd basically need two versions of the macros: one for irqs/exceptions and one for syscalls.

So I think the cleanest way to handle this is to always allocate two extra registers on the stack in ALLOC_PT_GPREGS_ON_STACK. Then all entry code can assume that pt_regs is at a constant location, and all the above problems go away. Another benefit is that we'd only need two saves instead of three -- the pointer to pt_regs is no longer needed since pt_regs is always immediately after the frame header.

I worked up a patch to implement this -- see below. It writes the frame pointer in all entry paths, including syscalls. This helps keep the code simple.

The downside is a small performance penalty: with getppid()-in-a-loop on my laptop, the average syscall went from 52ns to 53ns, which is about a 2% slowdown. But I doubt it would be measurable in a real-world workload.

Quoted 7 lines

It looks like about half the slowdown is due to the extra stack allocation (which presumably adds a little d-cache pressure on the stack memory) and the other half is due to the stack writes.

I could remove the writes from the syscall path but it would only save about half a ns, and it would make the code less robust. Plus it's nice to have the consistency of having *all* pt_regs frames annotated.

This is a bit messy, and I'm not really sure that the entry code should be have to operate under constraints like this. Also, convincing myself this works for NMI sounds unpleasant.

Maybe we should go back to my idea of just listing the call sites in a table.

#49 lore raw

Re: [RFC PATCH v2 05/18] sched: add task flag for preempt IRQ tracking

Quoted 43 lines · Andy Lutomirski
Quoted 35 lines

So I got a chance to look at this some more. I'm thinking that to make this feature more consistently useful, we shouldn't only annotate pt_regs frames for calls to handlers; other calls should be annotated as
well: preempt_schedule_irq, CALL_enter_from_user_mode, prepare_exit_to_usermode, SWAPGS, TRACE_IRQS_OFF, DISABLE_INTERRUPTS, etc. That way, the unwinder will always be able to find pt_regs from an interrupt/exception, even if starting from one of these other calls.

But then, things get ugly. You have to either setup and tear down the frame for every possible call, or do a higher-level setup/teardown across multiple calls, which invalidates several assumptions in the entry code about the location of pt_regs on the stack.

Also problematic is that several of the macros (like TRACE_IRQS_IRETQ) make assumptions about the location of pt_regs. And they're used by both syscall and interrupt code. So if we didn't create a frame pointer header for syscalls, we'd basically need two versions of the macros: one for irqs/exceptions and one for syscalls.

So I think the cleanest way to handle this is to always allocate two extra registers on the stack in ALLOC_PT_GPREGS_ON_STACK. Then all entry code can assume that pt_regs is at a constant location, and all the above problems go away. Another benefit is that we'd only need two saves instead of three -- the pointer to pt_regs is no longer needed since pt_regs is always immediately after the frame header.

I worked up a patch to implement this -- see below. It writes the frame pointer in all entry paths, including syscalls. This helps keep the code simple.

The downside is a small performance penalty: with getppid()-in-a-loop on my laptop, the average syscall went from 52ns to 53ns, which is about a 2% slowdown. But I doubt it would be measurable in a real-world workload.

Quoted 7 lines

It looks like about half the slowdown is due to the extra stack allocation (which presumably adds a little d-cache pressure on the stack memory) and the other half is due to the stack writes.

I could remove the writes from the syscall path but it would only save about half a ns, and it would make the code less robust. Plus it's nice to have the consistency of having *all* pt_regs frames annotated.

Quoted 5 lines

This is a bit messy, and I'm not really sure that the entry code should be have to operate under constraints like this. Also, convincing myself this works for NMI sounds unpleasant.

Maybe we should go back to my idea of just listing the call sites in a table.

So are you suggesting something like:

.macro ENTRY_CALL func pt_regs_offset=0
call \func
1: .pushsection .entry_calls, "a"

	.long 1b - .
	.long \pt_regs_offset
	.popsection
	.endm

and then change every call in the entry code to ENTRY_CALL?

Signature · 1 line
Josh
#50 lore raw

Re: [RFC PATCH v2 05/18] sched: add task flag for preempt IRQ tracking

Quoted 55 lines · Josh Poimboeuf
Quoted 43 lines · Andy Lutomirski
Quoted 35 lines

So I got a chance to look at this some more. I'm thinking that to make this feature more consistently useful, we shouldn't only annotate pt_regs frames for calls to handlers; other calls should be annotated as
well: preempt_schedule_irq, CALL_enter_from_user_mode, prepare_exit_to_usermode, SWAPGS, TRACE_IRQS_OFF, DISABLE_INTERRUPTS, etc. That way, the unwinder will always be able to find pt_regs from an interrupt/exception, even if starting from one of these other calls.

But then, things get ugly. You have to either setup and tear down the frame for every possible call, or do a higher-level setup/teardown across multiple calls, which invalidates several assumptions in the entry code about the location of pt_regs on the stack.

Also problematic is that several of the macros (like TRACE_IRQS_IRETQ) make assumptions about the location of pt_regs. And they're used by both syscall and interrupt code. So if we didn't create a frame pointer header for syscalls, we'd basically need two versions of the macros: one for irqs/exceptions and one for syscalls.

So I think the cleanest way to handle this is to always allocate two extra registers on the stack in ALLOC_PT_GPREGS_ON_STACK. Then all entry code can assume that pt_regs is at a constant location, and all the above problems go away. Another benefit is that we'd only need two saves instead of three -- the pointer to pt_regs is no longer needed since pt_regs is always immediately after the frame header.

I worked up a patch to implement this -- see below. It writes the frame pointer in all entry paths, including syscalls. This helps keep the code simple.

The downside is a small performance penalty: with getppid()-in-a-loop on my laptop, the average syscall went from 52ns to 53ns, which is about a 2% slowdown. But I doubt it would be measurable in a real-world workload.

Quoted 7 lines

It looks like about half the slowdown is due to the extra stack allocation (which presumably adds a little d-cache pressure on the stack memory) and the other half is due to the stack writes.

I could remove the writes from the syscall path but it would only save about half a ns, and it would make the code less robust. Plus it's nice to have the consistency of having *all* pt_regs frames annotated.

Quoted 5 lines

This is a bit messy, and I'm not really sure that the entry code should be have to operate under constraints like this. Also, convincing myself this works for NMI sounds unpleasant.

Maybe we should go back to my idea of just listing the call sites in a table.

So are you suggesting something like:

.macro ENTRY_CALL func pt_regs_offset=0
call \func
1: .pushsection .entry_calls, "a"

Quoted 6 lines
        .long 1b - .
        .long \pt_regs_offset
        .popsection
        .endm

and then change every call in the entry code to ENTRY_CALL?

Yes, exactly, modulo whether the section name is good. hpa is probably the authority on that.

--Andy

#51 lore raw

Re: [RFC PATCH v2 05/18] sched: add task flag for preempt IRQ tracking

Quoted 61 lines · Andy Lutomirski
Quoted 60 lines · Josh Poimboeuf
Quoted 43 lines · Andy Lutomirski
Quoted 35 lines

So I got a chance to look at this some more. I'm thinking that to make this feature more consistently useful, we shouldn't only annotate pt_regs frames for calls to handlers; other calls should be annotated as
well: preempt_schedule_irq, CALL_enter_from_user_mode, prepare_exit_to_usermode, SWAPGS, TRACE_IRQS_OFF, DISABLE_INTERRUPTS, etc. That way, the unwinder will always be able to find pt_regs from an interrupt/exception, even if starting from one of these other calls.

But then, things get ugly. You have to either setup and tear down the frame for every possible call, or do a higher-level setup/teardown across multiple calls, which invalidates several assumptions in the entry code about the location of pt_regs on the stack.

Also problematic is that several of the macros (like TRACE_IRQS_IRETQ) make assumptions about the location of pt_regs. And they're used by both syscall and interrupt code. So if we didn't create a frame pointer header for syscalls, we'd basically need two versions of the macros: one for irqs/exceptions and one for syscalls.

So I think the cleanest way to handle this is to always allocate two extra registers on the stack in ALLOC_PT_GPREGS_ON_STACK. Then all entry code can assume that pt_regs is at a constant location, and all the above problems go away. Another benefit is that we'd only need two saves instead of three -- the pointer to pt_regs is no longer needed since pt_regs is always immediately after the frame header.

I worked up a patch to implement this -- see below. It writes the frame pointer in all entry paths, including syscalls. This helps keep the code simple.

The downside is a small performance penalty: with getppid()-in-a-loop on my laptop, the average syscall went from 52ns to 53ns, which is about a 2% slowdown. But I doubt it would be measurable in a real-world workload.

Quoted 7 lines

It looks like about half the slowdown is due to the extra stack allocation (which presumably adds a little d-cache pressure on the stack memory) and the other half is due to the stack writes.

I could remove the writes from the syscall path but it would only save about half a ns, and it would make the code less robust. Plus it's nice to have the consistency of having *all* pt_regs frames annotated.

Quoted 5 lines

This is a bit messy, and I'm not really sure that the entry code should be have to operate under constraints like this. Also, convincing myself this works for NMI sounds unpleasant.

Maybe we should go back to my idea of just listing the call sites in a table.

So are you suggesting something like:

.macro ENTRY_CALL func pt_regs_offset=0
call \func
1: .pushsection .entry_calls, "a"

        .long 1b - .
        .long \pt_regs_offset
        .popsection
        .endm
Quoted 4 lines
Quoted 1 line

and then change every call in the entry code to ENTRY_CALL?

Yes, exactly, modulo whether the section name is good. hpa is probably the authority on that.

Well, as you probably know, I don't really like peppering ENTRY_CALL everywhere. :-/

Also I wonder how we could annotate the hypercalls, for example DISABLE_INTERRUPTS actually wraps the call in a push/pop pair.

Signature · 1 line
Josh
#52 lore raw

Re: [RFC PATCH v2 05/18] sched: add task flag for preempt IRQ tracking

Quoted 12 lines · Josh Poimboeuf
Quoted 11 lines · Andy Lutomirski
Quoted 11 lines

So are you suggesting something like:

.macro ENTRY_CALL func pt_regs_offset=0
call \func
1: .pushsection .entry_calls, "a"

        .long 1b - .
        .long \pt_regs_offset
        .popsection
        .endm
Quoted 7 lines
Quoted 4 lines
Quoted 1 line

and then change every call in the entry code to ENTRY_CALL?

Yes, exactly, modulo whether the section name is good. hpa is probably the authority on that.

Well, as you probably know, I don't really like peppering ENTRY_CALL everywhere. :-/

Me neither. But at least it's less constraining on the already-fairly-hairy code.

Quoted 3 lines

Also I wonder how we could annotate the hypercalls, for example DISABLE_INTERRUPTS actually wraps the call in a push/pop pair.

Oh, yuck. But forcing all the DISABLE_INTERRUPTS and ENABLE_INTERRUPTS invocations to be in frame pointer regions isn't so great either.

DWARF solves this problem completely and IMO fairly cleanly. Maybe we should add your task flag and then consider removing it again when DWARF happens.

#58 lore raw

Re: [RFC PATCH v2 05/18] sched: add task flag for preempt IRQ tracking

In reply to Josh Poimboeuf
Quoted 10 lines · Josh Poimboeuf

Andy,

So I got a chance to look at this some more. I'm thinking that to make this feature more consistently useful, we shouldn't only annotate pt_regs frames for calls to handlers; other calls should be annotated as
well: preempt_schedule_irq, CALL_enter_from_user_mode, prepare_exit_to_usermode, SWAPGS, TRACE_IRQS_OFF, DISABLE_INTERRUPTS, etc. That way, the unwinder will always be able to find pt_regs from an interrupt/exception, even if starting from one of these other calls.

Quoted 5 lines

But then, things get ugly. You have to either setup and tear down the frame for every possible call, or do a higher-level setup/teardown across multiple calls, which invalidates several assumptions in the entry code about the location of pt_regs on the stack.

Here's yet another harebrained idea. Maybe it works better than my previous harebrained ideas :)

Your patch is already creating a somewhat nonstandard stack frame:

+       movq    %rbp,                   0*8(%rsp)
+       movq    $entry_frame_ret,       1*8(%rsp)
+       movq    %rsp, %rbp

It's kind of a normal stack frame, but rbp points at something odd, and to unwind it fully correctly, the unwinder needs to know about it.

What if we made it even more special, along the lines of:

leaq offset_to_ptregs(%rsp), %rbp
xorq $-1, %rbp

IOW, don't write anything to the stack at all, and just put a special value into RBP that says "the next frame is pt_regs at such-and-such address". Do this once on entry and make sure to restore RBP (from pt_regs) on exit. Now the unwinder can notice that RBP has the high bits clear *and* that the negation of it points to the stack, and it can figure out what's going on.

What do you think? Am I nuts or could this work?

It had better not have much risk of breaking things worse than they currently are, given that current kernel allow user code to stick any value it likes into the very last element of the RBP chain.

--Andy

#59 lore raw

Re: [RFC PATCH v2 05/18] sched: add task flag for preempt IRQ tracking

Quoted 41 lines · Andy Lutomirski
Quoted 10 lines · Josh Poimboeuf

Andy,

So I got a chance to look at this some more. I'm thinking that to make this feature more consistently useful, we shouldn't only annotate pt_regs frames for calls to handlers; other calls should be annotated as
well: preempt_schedule_irq, CALL_enter_from_user_mode, prepare_exit_to_usermode, SWAPGS, TRACE_IRQS_OFF, DISABLE_INTERRUPTS, etc. That way, the unwinder will always be able to find pt_regs from an interrupt/exception, even if starting from one of these other calls.

Quoted 5 lines

But then, things get ugly. You have to either setup and tear down the frame for every possible call, or do a higher-level setup/teardown across multiple calls, which invalidates several assumptions in the entry code about the location of pt_regs on the stack.

Here's yet another harebrained idea. Maybe it works better than my previous harebrained ideas :)

Your patch is already creating a somewhat nonstandard stack frame:

arch/x86/entry/calling.h +3 -0
+       movq    %rbp,                   0*8(%rsp)
+       movq    $entry_frame_ret,       1*8(%rsp)
+       movq    %rsp, %rbp

It's kind of a normal stack frame, but rbp points at something odd, and to unwind it fully correctly, the unwinder needs to know about it.

What if we made it even more special, along the lines of:

leaq offset_to_ptregs(%rsp), %rbp
xorq $-1, %rbp

IOW, don't write anything to the stack at all, and just put a special value into RBP that says "the next frame is pt_regs at such-and-such address". Do this once on entry and make sure to restore RBP (from pt_regs) on exit. Now the unwinder can notice that RBP has the high bits clear *and* that the negation of it points to the stack, and it can figure out what's going on.

Quoted 5 lines

What do you think? Am I nuts or could this work?

It had better not have much risk of breaking things worse than they currently are, given that current kernel allow user code to stick any value it likes into the very last element of the RBP chain.

I think it's a good idea, and it could work... BUT it would break external unwinders like gdb for the in-kernel entry case.

For interrupts and exceptions in kernel mode, rbp *is* valid. Sure, it doesn't tell you the interrupted function, but it does tell you its caller. A generic frame pointer unwinder skips the interrupted function, but at least it keeps going. If we encoded rbp on entry, that would break.

Signature · 1 line
Josh
#53 lore raw

Re: [RFC PATCH v2 05/18] sched: add task flag for preempt IRQ tracking

In reply to Andy Lutomirski
Quoted 25 lines · Andy Lutomirski
Quoted 12 lines · Josh Poimboeuf
Quoted 11 lines · Andy Lutomirski
Quoted 11 lines

So are you suggesting something like:

.macro ENTRY_CALL func pt_regs_offset=0
call \func
1: .pushsection .entry_calls, "a"

        .long 1b - .
        .long \pt_regs_offset
        .popsection
        .endm
Quoted 7 lines
Quoted 4 lines
Quoted 1 line

and then change every call in the entry code to ENTRY_CALL?

Yes, exactly, modulo whether the section name is good. hpa is probably the authority on that.

Well, as you probably know, I don't really like peppering ENTRY_CALL everywhere. :-/

Me neither. But at least it's less constraining on the already-fairly-hairy code.

Quoted 1 line
Quoted 6 lines
Quoted 2 lines

Also I wonder how we could annotate the hypercalls, for example DISABLE_INTERRUPTS actually wraps the call in a push/pop pair.

Oh, yuck. But forcing all the DISABLE_INTERRUPTS and ENABLE_INTERRUPTS invocations to be in frame pointer regions isn't so great either.

Hm, I don't follow this statement. Why not? The more frame pointer coverage, the better, especially if it doesn't add any additional overhead.

Quoted 3 lines

DWARF solves this problem completely and IMO fairly cleanly. Maybe we should add your task flag and then consider removing it again when DWARF happens.

I tend to doubt we'd be able to remove it later. As you said before, many embedded platforms probably won't be able to switch to DWARF, and they'll want to do live patching too.

So which is the least-bad option? To summarize:

1) task flag(s) for preemption and page faults

2) turn pt_regs into a stack frame

3) annotate all calls from entry code in a table

4) encode rbp on entry

They all have their issues, though I'm partial to #2.

Any more hare-brained ideas? :-)

Signature · 1 line
Josh
#54 lore raw

Re: [RFC PATCH v2 05/18] sched: add task flag for preempt IRQ tracking

Quoted 29 lines · Josh Poimboeuf
Quoted 28 lines · Andy Lutomirski
Quoted 12 lines · Josh Poimboeuf
Quoted 11 lines · Andy Lutomirski
Quoted 11 lines

So are you suggesting something like:

.macro ENTRY_CALL func pt_regs_offset=0
call \func
1: .pushsection .entry_calls, "a"

        .long 1b - .
        .long \pt_regs_offset
        .popsection
        .endm
Quoted 7 lines
Quoted 4 lines
Quoted 1 line

and then change every call in the entry code to ENTRY_CALL?

Yes, exactly, modulo whether the section name is good. hpa is probably the authority on that.

Well, as you probably know, I don't really like peppering ENTRY_CALL everywhere. :-/

Me neither. But at least it's less constraining on the already-fairly-hairy code.

Quoted 3 lines

Also I wonder how we could annotate the hypercalls, for example DISABLE_INTERRUPTS actually wraps the call in a push/pop pair.

Quoted 7 lines
Quoted 3 lines

Oh, yuck. But forcing all the DISABLE_INTERRUPTS and ENABLE_INTERRUPTS invocations to be in frame pointer regions isn't so great either.

Hm, I don't follow this statement. Why not? The more frame pointer coverage, the better, especially if it doesn't add any additional overhead.

Less flexibility, and it's IMO annoying to make the Xen case have extra constraints. It also makes it very awkward or impossible to take advantage of the sti interrupt window, although admittedly that doesn't work on Xen either, so maybe that's moot.

Quoted 15 lines
Quoted 4 lines

DWARF solves this problem completely and IMO fairly cleanly. Maybe we should add your task flag and then consider removing it again when DWARF happens.

I tend to doubt we'd be able to remove it later. As you said before, many embedded platforms probably won't be able to switch to DWARF, and they'll want to do live patching too.

So which is the least-bad option? To summarize:

1) task flag(s) for preemption and page faults

2) turn pt_regs into a stack frame

Quoted 7 lines

3) annotate all calls from entry code in a table

4) encode rbp on entry

They all have their issues, though I'm partial to #2.

Any more hare-brained ideas? :-)

I'll try to take a closer look at #2 and see just how much I dislike all the stack frame munging. Also, in principle, it's only the sleeping calls and the calls that make it into real (non-entry) kernel code that really want to be unwindable through this mechanism.

FWIW, I don't care that much about preserving gdb's partial ability to unwind through pt_regs, especially because gdb really ought to be able to use DWARF, too.

--Andy

#55 lore raw

Re: [RFC PATCH v2 05/18] sched: add task flag for preempt IRQ tracking

Quoted 8 lines · Andy Lutomirski
Quoted 8 lines

So which is the least-bad option? To summarize:

1) task flag(s) for preemption and page faults

2) turn pt_regs into a stack frame

3) annotate all calls from entry code in a table

Quoted 8 lines
Quoted 5 lines

4) encode rbp on entry

They all have their issues, though I'm partial to #2.

Any more hare-brained ideas? :-)

I'll try to take a closer look at #2 and see just how much I dislike all the stack frame munging.

Ok.

Quoted 3 lines

Also, in principle, it's only the
sleeping calls and the calls that make it into real (non-entry) kernel code that really want to be unwindable through this mechanism.

Yeah, that's true. We could modify options 2 or 3 to be less absolute. Though I think that makes them more prone to future breakage.

Quoted 3 lines

FWIW, I don't care that much about preserving gdb's partial ability to unwind through pt_regs, especially because gdb really ought to be able to use DWARF, too.

Hm, that's a good point. I really don't know if there are any other external tools out there that would care. Maybe we could try option 4 and then see if anybody complains.

Signature · 1 line
Josh
#56 lore raw

Re: [RFC PATCH v2 05/18] sched: add task flag for preempt IRQ tracking

Quoted 27 lines · Josh Poimboeuf
Quoted 8 lines · Andy Lutomirski
Quoted 8 lines

So which is the least-bad option? To summarize:

1) task flag(s) for preemption and page faults

2) turn pt_regs into a stack frame

3) annotate all calls from entry code in a table

Quoted 8 lines
Quoted 5 lines

4) encode rbp on entry

They all have their issues, though I'm partial to #2.

Any more hare-brained ideas? :-)

I'll try to take a closer look at #2 and see just how much I dislike all the stack frame munging.

Ok.

Quoted 3 lines

Also, in principle, it's only the
sleeping calls and the calls that make it into real (non-entry) kernel code that really want to be unwindable through this mechanism.

Yeah, that's true. We could modify options 2 or 3 to be less absolute. Though I think that makes them more prone to future breakage.

Quoted 7 lines
Quoted 3 lines

FWIW, I don't care that much about preserving gdb's partial ability to unwind through pt_regs, especially because gdb really ought to be able to use DWARF, too.

Hm, that's a good point. I really don't know if there are any other external tools out there that would care. Maybe we could try option 4 and then see if anybody complains.

I'm starting to think hare-brained option 4 is the way to go. Any external tooling should really be relying on DWARF anyway.

Here's a sneak preview. If this general approach looks ok to you, I'll go ahead and port all the in-tree unwinders and post a proper patch.

Instead of using xor -1 on the pt_regs pointer, I just cleared the high-order bit. That makes the unwinding experience much more pleasant for a human stack walker, and also ensures that anybody trying to dereference it gets slapped with an oops, at least in the 48-bit address space era.

arch/x86/entry/calling.h +17 -0
index 9a9e588..bf397426 100644
@@ -201,6 +201,23 @@ For 32-bit we have the following conventions - kernel is built with
 	.byte 0xf1
 	.endm
 
+	/*
+	 * This is a sneaky trick to help the unwinder find pt_regs on the
+	 * stack.  The frame pointer is replaced with an encoded pointer to
+	 * pt_regs.  The encoding is just a clearing of the highest-order bit,
+	 * which makes it an invalid address and is also a signal to the
+	 * unwinder that it's a pt_regs pointer in disguise.
+	 *
+	 * NOTE: This must be called *after* SAVE_EXTRA_REGS because it
+	 * corrupts rbp.
+	 */
+	.macro ENCODE_FRAME_POINTER ptregs_offset=0
+#ifdef CONFIG_FRAME_POINTER
+	leaq \ptregs_offset(%rsp), %rbp
+	btr $63, %rbp
+#endif
+	.endm
+
 #endif /* CONFIG_X86_64 */
 
 /*
arch/x86/entry/entry_64.S +6 -1
index 9ee0da1..eb79652 100644
@@ -431,6 +431,7 @@ END(irq_entries_start)
 	ALLOC_PT_GPREGS_ON_STACK
 	SAVE_C_REGS
 	SAVE_EXTRA_REGS
+	ENCODE_FRAME_POINTER
 
 	testb	$3, CS(%rsp)
 	jz	1f
@@ -893,6 +894,7 @@ ENTRY(xen_failsafe_callback)
 	ALLOC_PT_GPREGS_ON_STACK
 	SAVE_C_REGS
 	SAVE_EXTRA_REGS
+	ENCODE_FRAME_POINTER
 	jmp	error_exit
 END(xen_failsafe_callback)
 
@@ -936,6 +938,7 @@ ENTRY(paranoid_entry)
 	cld
 	SAVE_C_REGS 8
 	SAVE_EXTRA_REGS 8
+	ENCODE_FRAME_POINTER 8
 	movl	$1, %ebx
 	movl	$MSR_GS_BASE, %ecx
 	rdmsr
@@ -983,6 +986,7 @@ ENTRY(error_entry)
 	cld
 	SAVE_C_REGS 8
 	SAVE_EXTRA_REGS 8
+	ENCODE_FRAME_POINTER 8
 	xorl	%ebx, %ebx
 	testb	$3, CS+8(%rsp)
 	jz	.Lerror_kernelspace
@@ -1165,6 +1169,7 @@ ENTRY(nmi)
 	pushq	%r13		/* pt_regs->r13 */
 	pushq	%r14		/* pt_regs->r14 */
 	pushq	%r15		/* pt_regs->r15 */
+	ENCODE_FRAME_POINTER
 
 	/*
 	 * At this point we no longer need to worry about stack damage
@@ -1182,7 +1187,7 @@ ENTRY(nmi)
 	 * do_nmi doesn't modify pt_regs.
 	 */
 	SWAPGS
-	jmp	restore_c_regs_and_iret
+	jmp	restore_regs_and_iret
 
 .Lnmi_from_kernel:
 	/*
#57 lore raw

Re: [RFC PATCH v2 05/18] sched: add task flag for preempt IRQ tracking

Quoted 63 lines · Josh Poimboeuf
Quoted 27 lines · Josh Poimboeuf
Quoted 8 lines · Andy Lutomirski
Quoted 8 lines

So which is the least-bad option? To summarize:

1) task flag(s) for preemption and page faults

2) turn pt_regs into a stack frame

3) annotate all calls from entry code in a table

Quoted 8 lines
Quoted 5 lines

4) encode rbp on entry

They all have their issues, though I'm partial to #2.

Any more hare-brained ideas? :-)

I'll try to take a closer look at #2 and see just how much I dislike all the stack frame munging.

Ok.

Quoted 3 lines

Also, in principle, it's only the
sleeping calls and the calls that make it into real (non-entry) kernel code that really want to be unwindable through this mechanism.

Yeah, that's true. We could modify options 2 or 3 to be less absolute. Though I think that makes them more prone to future breakage.

Quoted 7 lines
Quoted 3 lines

FWIW, I don't care that much about preserving gdb's partial ability to unwind through pt_regs, especially because gdb really ought to be able to use DWARF, too.

Hm, that's a good point. I really don't know if there are any other external tools out there that would care. Maybe we could try option 4 and then see if anybody complains.

I'm starting to think hare-brained option 4 is the way to go. Any external tooling should really be relying on DWARF anyway.

Here's a sneak preview. If this general approach looks ok to you, I'll go ahead and port all the in-tree unwinders and post a proper patch.

Instead of using xor -1 on the pt_regs pointer, I just cleared the high-order bit. That makes the unwinding experience much more pleasant for a human stack walker, and also ensures that anybody trying to dereference it gets slapped with an oops, at least in the 48-bit address space era.

arch/x86/entry/calling.h +7 -0
index 9a9e588..bf397426 100644
@@ -201,6 +201,23 @@ For 32-bit we have the following conventions - kernel is built with
        .byte 0xf1
        .endm
 
+       /*
+        * This is a sneaky trick to help the unwinder find pt_regs on the
+        * stack.  The frame pointer is replaced with an encoded pointer to
+        * pt_regs.  The encoding is just a clearing of the highest-order bit,
+        * which makes it an invalid address and is also a signal to the
+        * unwinder that it's a pt_regs pointer in disguise.
+        *

arch/x86/entry/calling.h For 32-bit we have the following conventions - kernel is built with · line 220

Quoted 10 lines
arch/x86/entry/calling.h +10 -0
+        * NOTE: This must be called *after* SAVE_EXTRA_REGS because it
+        * corrupts rbp.
+        */
+       .macro ENCODE_FRAME_POINTER ptregs_offset=0
+#ifdef CONFIG_FRAME_POINTER
+       leaq \ptregs_offset(%rsp), %rbp
+       btr $63, %rbp
+#endif
+       .endm
+

Maybe optimize slightly:

.ifeq \ptregs_offset
mov %rsp, %rbp
.else
leaq \ptregs_offset(%rsp), %rbp
.endif