[v5][PATCH] livepatch/ppc: Enable livepatching on powerpc

Subsystems: function hooks (ftrace), linux for powerpc (32-bit and 64-bit), live patching, the rest, tracing

9 messages, 3 authors, 2016-03-09 · open the first message on its own page

[v5][PATCH] livepatch/ppc: Enable livepatching on powerpc

From: Balbir Singh <bsingharora@gmail.com>
Date: 2016-03-08 07:34:32

Changelog v5:
	1. Removed the mini-stack frame created for klp_return_helper.
	   As a result of the mini-stack frame, function with > 8
	   arguments could not be patched
	2. Removed camel casing in the comments
Changelog v4:
	1. Renamed klp_matchaddr() to klp_get_ftrace_location()
	   and used it just to convert the function address.
	2. Synced klp_write_module_reloc() with s390(); made it
	   inline, no error message, return -ENOSYS
	3. Added an error message when including
	   powerpc/include/asm/livepatch.h without HAVE_LIVEPATCH
	4. Update some comments.
Changelog v3:
	1. Moved -ENOSYS to -EINVAL in klp_write_module_reloc
	2. Moved klp_matchaddr to use ftrace_location_range
Changelog v2:
	1. Implement review comments by Michael
	2. The previous version compared _NIP from the
	   wrong location to check for whether we
	   are going to a patched location

This patch enables live patching for powerpc. The current patch
is applied on top of topic/mprofile-kernel at
https://git.kernel.org/cgit/linux/kernel/git/powerpc/linux.git/

This patch builds on top of ftrace with regs changes and the
-mprofile-kernel changes. It detects a change in NIP after
the klp subsystem has potentially changes the NIP as a result
of a livepatch. In that case it saves the TOC in the parents
stack and the offset of the return address from the TOC in
the reserved (CR+4) space. This hack allows us to provide the
complete frame of the calling function as is to the caller
without having to create a mini-frame

Upon return from the patched function, the TOC and correct
LR is restored.

I tested the sample in the livepatch and an additional sample
that patches int_to_scsilun. I'll post out that sample if there
is interest later. I also tested ftrace functionality on the
command line to check for breakage

Signed-off-by: Torsten Duwe <redacted>
Signed-off-by: Balbir Singh <bsingharora@gmail.com>
Signed-off-by: Petr Mladek <pmladek@suse.com>
---
 arch/powerpc/Kconfig                 |  3 ++
 arch/powerpc/include/asm/livepatch.h | 47 ++++++++++++++++++++++++++++
 arch/powerpc/kernel/Makefile         |  1 +
 arch/powerpc/kernel/entry_64.S       | 60 ++++++++++++++++++++++++++++++++++++
 arch/powerpc/kernel/livepatch.c      | 29 +++++++++++++++++
 include/linux/ftrace.h               |  1 +
 include/linux/livepatch.h            |  2 ++
 kernel/livepatch/core.c              | 28 +++++++++++++++--
 kernel/trace/ftrace.c                | 14 ++++++++-
 9 files changed, 181 insertions(+), 4 deletions(-)
 create mode 100644 arch/powerpc/include/asm/livepatch.h
 create mode 100644 arch/powerpc/kernel/livepatch.c
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index 91da283..926c0ea 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -159,6 +159,7 @@ config PPC
 	select ARCH_HAS_DEVMEM_IS_ALLOWED
 	select HAVE_ARCH_SECCOMP_FILTER
 	select ARCH_HAS_UBSAN_SANITIZE_ALL
+	select HAVE_LIVEPATCH if HAVE_DYNAMIC_FTRACE_WITH_REGS
 
 config GENERIC_CSUM
 	def_bool CPU_LITTLE_ENDIAN
@@ -1110,3 +1111,5 @@ config PPC_LIB_RHEAP
 	bool
 
 source "arch/powerpc/kvm/Kconfig"
+
+source "kernel/livepatch/Kconfig"
diff --git a/arch/powerpc/include/asm/livepatch.h b/arch/powerpc/include/asm/livepatch.h
new file mode 100644
index 0000000..b9856ce
--- /dev/null
+++ b/arch/powerpc/include/asm/livepatch.h
@@ -0,0 +1,47 @@
+/*
+ * livepatch.h - powerpc-specific Kernel Live Patching Core
+ *
+ * Copyright (C) 2015 SUSE
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef _ASM_POWERPC64_LIVEPATCH_H
+#define _ASM_POWERPC64_LIVEPATCH_H
+
+#include <linux/module.h>
+
+#ifdef CONFIG_LIVEPATCH
+
+static inline int klp_check_compiler_support(void)
+{
+	return 0;
+}
+
+static inline int klp_write_module_reloc(struct module *mod, unsigned long
+		type, unsigned long loc, unsigned long value)
+{
+	/* This requires infrastructure changes; we need the loadinfos. */
+	return -ENOSYS;
+}
+
+static inline void klp_arch_set_pc(struct pt_regs *regs, unsigned long ip)
+{
+	regs->nip = ip;
+}
+
+#else /* CONFIG_LIVEPATCH */
+#error Include linux/livepatch.h, not asm/livepatch.h
+#endif /* CONFIG_LIVEPATCH */
+
+#endif /* _ASM_POWERPC64_LIVEPATCH_H */
diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile
index 2da380f..b767e14 100644
--- a/arch/powerpc/kernel/Makefile
+++ b/arch/powerpc/kernel/Makefile
@@ -119,6 +119,7 @@ obj-$(CONFIG_DYNAMIC_FTRACE)	+= ftrace.o
 obj-$(CONFIG_FUNCTION_GRAPH_TRACER)	+= ftrace.o
 obj-$(CONFIG_FTRACE_SYSCALLS)	+= ftrace.o
 obj-$(CONFIG_TRACING)		+= trace_clock.o
+obj-$(CONFIG_LIVEPATCH)		+= livepatch.o
 
 ifneq ($(CONFIG_PPC_INDIRECT_PIO),y)
 obj-y				+= iomap.o
diff --git a/arch/powerpc/kernel/entry_64.S b/arch/powerpc/kernel/entry_64.S
index ec7f8aa..e0406e6 100644
--- a/arch/powerpc/kernel/entry_64.S
+++ b/arch/powerpc/kernel/entry_64.S
@@ -1224,6 +1224,9 @@ _GLOBAL(ftrace_caller)
 	addi	r3,r3,function_trace_op@toc@l
 	ld	r5,0(r3)
 
+#ifdef CONFIG_LIVEPATCH
+	mr	r14,r7		/* remember old NIP */
+#endif
 	/* Calculate ip from nip-4 into r3 for call below */
 	subi    r3, r7, MCOUNT_INSN_SIZE
 
@@ -1248,6 +1251,9 @@ ftrace_call:
 	/* Load ctr with the possibly modified NIP */
 	ld	r3, _NIP(r1)
 	mtctr	r3
+#ifdef CONFIG_LIVEPATCH
+	cmpd	r14,r3		/* has NIP been altered? */
+#endif
 
 	/* Restore gprs */
 	REST_8GPRS(0,r1)
@@ -1265,6 +1271,39 @@ ftrace_call:
 	ld	r0, LRSAVE(r1)
 	mtlr	r0
 
+#ifdef CONFIG_LIVEPATCH
+	beq+	4f		/* likely(old_NIP == new_NIP) */
+	/*
+	 * For a local call, restore this TOC after calling the patch function.
+	 * For a global call, it does not matter what we restore here,
+	 * since the global caller does its own restore right afterwards,
+	 * anyway. Just insert a klp_return_helper frame in any case,
+	 * so a patch function can always count on the changed stack offsets.
+	 * The patch introduces a frame such that from the patched function
+	 * we return back to klp_return helper. For ABI compliance r12,
+	 * lr and LRSAVE(r1) contain the address of klp_return_helper.
+	 * We loaded ctr with the address of the patched function earlier
+	 * We use the reserved space in the parent stack frame at CR+4.
+	 * This space is stored with the contents of LR-TOC, where LR
+	 * refers to the address to return to.
+	 * Why do we need this?
+	 * After patching we need to return to a trampoline return function
+	 * that guarantees that we restore the TOC and return to the correct
+	 * caller back
+	 */
+	std	r2, 24(r1)	/* save TOC now, unconditionally. */
+	subf	r0, r2, r0	/* Calculate offset from current TOC */
+	stw	r0, 12(r1)	/* Of the final LR and save it in CR+4 */
+	bl	5f
+5:	mflr	r12
+	addi	r12, r12, (klp_return_helper + 4 - .)@l
+	std	r12, LRSAVE(r1)
+	mtlr	r12
+	mfctr	r12		/* allow for TOC calculation in newfunc */
+	bctr
+4:
+#endif
+
 #ifdef CONFIG_FUNCTION_GRAPH_TRACER
 	stdu	r1, -112(r1)
 .globl ftrace_graph_call
@@ -1281,6 +1320,27 @@ _GLOBAL(ftrace_graph_stub)
 
 _GLOBAL(ftrace_stub)
 	blr
+#ifdef CONFIG_LIVEPATCH
+/*
+ * Helper function for local calls that are becoming global
+ * due to live patching.
+ * We can't simply patch the NOP after the original call,
+ * because, depending on the consistency model, some kernel
+ * threads may still have called the original, local function
+ * *without* saving their TOC in the respective stack frame slot,
+ * so the decision is made per-thread during function return by
+ * maybe inserting a klp_return_helper frame or not.
+*/
+klp_return_helper:
+	ld	r2, 24(r1)	/* restore TOC (saved by ftrace_caller) */
+	lwa	r0, 12(r1)	/* Load from CR+4, offset of LR w.r.t TOC */
+	add	r0, r0, r2	/* Add the offset to current TOC */
+	std	r0, LRSAVE(r1)	/* save the real return address */
+	mtlr	r0
+	blr
+#endif
+
+
 #else
 _GLOBAL_TOC(_mcount)
 	/* Taken from output of objdump from lib64/glibc */
diff --git a/arch/powerpc/kernel/livepatch.c b/arch/powerpc/kernel/livepatch.c
new file mode 100644
index 0000000..17f1a14
--- /dev/null
+++ b/arch/powerpc/kernel/livepatch.c
@@ -0,0 +1,29 @@
+/*
+ * livepatch.c - powerpc-specific Kernel Live Patching Core
+ *
+ * Copyright (C) 2015 SUSE
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/ftrace.h>
+#include <asm/livepatch.h>
+
+/*
+ * Live patch works only with -mprofile-kernel on PPC. In this case,
+ * the ftrace location is always within the first 16 bytes.
+ */
+unsigned long klp_get_ftrace_location(unsigned long faddr)
+{
+	return ftrace_location_range(faddr, faddr + 16);
+}
diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
index 81de712..3481a8e 100644
--- a/include/linux/ftrace.h
+++ b/include/linux/ftrace.h
@@ -455,6 +455,7 @@ int ftrace_update_record(struct dyn_ftrace *rec, int enable);
 int ftrace_test_record(struct dyn_ftrace *rec, int enable);
 void ftrace_run_stop_machine(int command);
 unsigned long ftrace_location(unsigned long ip);
+unsigned long ftrace_location_range(unsigned long start, unsigned long end);
 unsigned long ftrace_get_addr_new(struct dyn_ftrace *rec);
 unsigned long ftrace_get_addr_curr(struct dyn_ftrace *rec);
 
diff --git a/include/linux/livepatch.h b/include/linux/livepatch.h
index a882865..25a267b 100644
--- a/include/linux/livepatch.h
+++ b/include/linux/livepatch.h
@@ -134,6 +134,8 @@ int klp_unregister_patch(struct klp_patch *);
 int klp_enable_patch(struct klp_patch *);
 int klp_disable_patch(struct klp_patch *);
 
+unsigned long klp_get_ftrace_location(unsigned long faddr);
+
 #endif /* CONFIG_LIVEPATCH */
 
 #endif /* _LINUX_LIVEPATCH_H_ */
diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c
index bc2c85c..e2dbf01 100644
--- a/kernel/livepatch/core.c
+++ b/kernel/livepatch/core.c
@@ -298,22 +298,39 @@ unlock:
 	rcu_read_unlock();
 }
 
+/*
+ * Convert a function address into the appropriate ftrace location.
+ *
+ * The given address is returned on most architectures. Live patching
+ * usually works only when the ftrace location is the first instruction
+ * in the function.
+ */
+unsigned long __weak klp_get_ftrace_location(unsigned long faddr)
+{
+	return faddr;
+}
+
 static void klp_disable_func(struct klp_func *func)
 {
 	struct klp_ops *ops;
+	unsigned long ftrace_loc;
 
 	if (WARN_ON(func->state != KLP_ENABLED))
 		return;
 	if (WARN_ON(!func->old_addr))
 		return;
 
+	ftrace_loc = klp_get_ftrace_location(func->old_addr);
+	if (WARN_ON(!ftrace_loc))
+		return;
+
 	ops = klp_find_ops(func->old_addr);
 	if (WARN_ON(!ops))
 		return;
 
 	if (list_is_singular(&ops->func_stack)) {
 		WARN_ON(unregister_ftrace_function(&ops->fops));
-		WARN_ON(ftrace_set_filter_ip(&ops->fops, func->old_addr, 1, 0));
+		WARN_ON(ftrace_set_filter_ip(&ops->fops, ftrace_loc, 1, 0));
 
 		list_del_rcu(&func->stack_node);
 		list_del(&ops->node);
@@ -328,6 +345,7 @@ static void klp_disable_func(struct klp_func *func)
 static int klp_enable_func(struct klp_func *func)
 {
 	struct klp_ops *ops;
+	unsigned long ftrace_loc;
 	int ret;
 
 	if (WARN_ON(!func->old_addr))
@@ -336,6 +354,10 @@ static int klp_enable_func(struct klp_func *func)
 	if (WARN_ON(func->state != KLP_DISABLED))
 		return -EINVAL;
 
+	ftrace_loc = klp_get_ftrace_location(func->old_addr);
+	if (WARN_ON(!ftrace_loc))
+		return -EINVAL;
+
 	ops = klp_find_ops(func->old_addr);
 	if (!ops) {
 		ops = kzalloc(sizeof(*ops), GFP_KERNEL);
@@ -352,7 +374,7 @@ static int klp_enable_func(struct klp_func *func)
 		INIT_LIST_HEAD(&ops->func_stack);
 		list_add_rcu(&func->stack_node, &ops->func_stack);
 
-		ret = ftrace_set_filter_ip(&ops->fops, func->old_addr, 0, 0);
+		ret = ftrace_set_filter_ip(&ops->fops, ftrace_loc, 0, 0);
 		if (ret) {
 			pr_err("failed to set ftrace filter for function '%s' (%d)\n",
 			       func->old_name, ret);
@@ -363,7 +385,7 @@ static int klp_enable_func(struct klp_func *func)
 		if (ret) {
 			pr_err("failed to register ftrace handler for function '%s' (%d)\n",
 			       func->old_name, ret);
-			ftrace_set_filter_ip(&ops->fops, func->old_addr, 1, 0);
+			ftrace_set_filter_ip(&ops->fops, ftrace_loc, 1, 0);
 			goto err;
 		}
 
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index eca592f..e1b3f23 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -1533,7 +1533,19 @@ static int ftrace_cmp_recs(const void *a, const void *b)
 	return 0;
 }
 
-static unsigned long ftrace_location_range(unsigned long start, unsigned long end)
+/**
+ * ftrace_location_range - return the first address of a traced location
+ *	if it touches the given ip range
+ * @start: start of range to search.
+ * @end: end of range to search (inclusive). @end points to the last byte
+ *	to check.
+ *
+ * Returns rec->ip if the related ftrace location is a least partly within
+ * the given address range. That is, the first address of the instruction
+ * that is either a NOP or call to the function tracer. It checks the ftrace
+ * internal tables to determine if the address belongs or not.
+ */
+unsigned long ftrace_location_range(unsigned long start, unsigned long end)
 {
 	struct ftrace_page *pg;
 	struct dyn_ftrace *rec;
-- 
2.5.0

Re: [v5][PATCH] livepatch/ppc: Enable livepatching on powerpc

From: Torsten Duwe <hidden>
Date: 2016-03-08 10:46:00

On Tue, Mar 08, 2016 at 06:33:57PM +1100, Balbir Singh wrote:
Changelog v5:
	1. Removed the mini-stack frame created for klp_return_helper.
	   As a result of the mini-stack frame, function with > 8
	   arguments could not be patched
Did you get my previous mails? Those functions only require special
care, the _can_ be patched. In general, writing replacement functions
always requires attention!

Have you *tested* this patch? Replacing a function in the kernel?
Replacing a function in a module? For local calls? For global calls?
I strongly doubt so because it does not work this way.

To be fair, my last mail still was not 100% correct, but the conclusion
that the mini frame is not needed at all is invalid. Please leave it as it
was, I'm working on a test / demonstrator for how to handle these.
+	 * Why do we need this?
+	 * After patching we need to return to a trampoline return function
+	 * that guarantees that we restore the TOC and return to the correct
+	 * caller back
+	 */
+	std	r2, 24(r1)	/* save TOC now, unconditionally. */
+	subf	r0, r2, r0	/* Calculate offset from current TOC */
+	stw	r0, 12(r1)	/* Of the final LR and save it in CR+4 */
+	bl	5f
+5:	mflr	r12
+	addi	r12, r12, (klp_return_helper + 4 - .)@l
+	std	r12, LRSAVE(r1)
[...]
+ * maybe inserting a klp_return_helper frame or not.
+*/
+klp_return_helper:
+	ld	r2, 24(r1)	/* restore TOC (saved by ftrace_caller) */
+	lwa	r0, 12(r1)	/* Load from CR+4, offset of LR w.r.t TOC */
+	add	r0, r0, r2	/* Add the offset to current TOC */
+	std	r0, LRSAVE(r1)	/* save the real return address */
+	mtlr	r0
+	blr
+#endif
NAKed-by: Torsten Duwe [off-list ref]

	Torsten

Re: [v5][PATCH] livepatch/ppc: Enable livepatching on powerpc

From: Balbir Singh <bsingharora@gmail.com>
Date: 2016-03-08 13:52:35


On 08/03/16 21:45, Torsten Duwe wrote:
On Tue, Mar 08, 2016 at 06:33:57PM +1100, Balbir Singh wrote:
quoted
Changelog v5:
	1. Removed the mini-stack frame created for klp_return_helper.
	   As a result of the mini-stack frame, function with > 8
	   arguments could not be patched
Did you get my previous mails? Those functions only require special
care, the _can_ be patched. In general, writing replacement functions
always requires attention!
Yes, I did. We did think about documenting that limitation, but the big concern
was that the system can be panic'd with a simple test case. We expect live patches
to be tested and signed so we should be OK, but there still is a window
Have you *tested* this patch? Replacing a function in the kernel?
Replacing a function in a module? For local calls? For global calls?
I strongly doubt so because it does not work this way.
Yes, if you care to read the changelog
"

I tested the sample in the livepatch and an additional sample
that patches int_to_scsilun. I'll post out that sample if there
is interest later. I also tested ftrace functionality on the
command line to check for breakage"

I've tested patching calls from module to module
(ibmvscsi to scsi_mod), within the kernel (livepatch-sample/
proc_cmdline_show). I must admit there is more testing to
be done
To be fair, my last mail still was not 100% correct, but the conclusion
that the mini frame is not needed at all is invalid. Please leave it as it
was, I'm working on a test / demonstrator for how to handle these.
Why, the magic will be in the patched function? Please share the test/demonstrator
quoted
+	 * Why do we need this?
+	 * After patching we need to return to a trampoline return function
+	 * that guarantees that we restore the TOC and return to the correct
+	 * caller back
+	 */
+	std	r2, 24(r1)	/* save TOC now, unconditionally. */
+	subf	r0, r2, r0	/* Calculate offset from current TOC */
+	stw	r0, 12(r1)	/* Of the final LR and save it in CR+4 */
+	bl	5f
+5:	mflr	r12
+	addi	r12, r12, (klp_return_helper + 4 - .)@l
+	std	r12, LRSAVE(r1)
[...]
quoted
+ * maybe inserting a klp_return_helper frame or not.
+*/
+klp_return_helper:
+	ld	r2, 24(r1)	/* restore TOC (saved by ftrace_caller) */
+	lwa	r0, 12(r1)	/* Load from CR+4, offset of LR w.r.t TOC */
+	add	r0, r0, r2	/* Add the offset to current TOC */
+	std	r0, LRSAVE(r1)	/* save the real return address */
+	mtlr	r0
+	blr
+#endif
NAKed-by: Torsten Duwe [off-list ref]
Why? For using CR+4 or removing the frame? Or you believe there is a better way to
handle this that work, IOW what is broken?
	Torsten
Balbir Singh

Re: [v5][PATCH] livepatch/ppc: Enable livepatching on powerpc

From: Torsten Duwe <hidden>
Date: 2016-03-08 15:34:47

On Wed, Mar 09, 2016 at 12:52:03AM +1100, Balbir Singh wrote:
On 08/03/16 21:45, Torsten Duwe wrote:
quoted
To be fair, my last mail still was not 100% correct, but the conclusion
Wrote a correction to the correction. It should be clear now. Please nag me
if it isn't clear why klp_return_helper and its stack frame is needed.
quoted
that the mini frame is not needed at all is invalid. Please leave it as it
was, I'm working on a test / demonstrator for how to handle these.
Why, the magic will be in the patched function? Please share the test/demonstrator
Here it comes...
quoted
NAKed-by: Torsten Duwe [off-list ref]
Why? For using CR+4 or removing the frame? Or you believe there is a better way to
handle this that work, IOW what is broken?
The stack frame removal. You're risking a memory access or jump into nirvana
or and endless loop.

klp_return_helper will do the right thing, and functions like e.g. printk
I would live patch like this (untested :-) :

------------------------8<----------------------
#include <stdarg.h>

/* compile using "-ffixed-r14"! */
register unsigned long pass_TOC asm("r14");

/*
 * Function pre-prologue to pop the klp_return_helper
 * mini stack frame. The saved r2 TOC value is read and
 * passed in pass_TOC (r14), the original LR is passed
 * in r0 and the LR itself. R12 is updated appropriately
 * for local TOC recalculation.
 */
extern void caller(void) asm("printk");
void caller(void)
{
  asm("ld %0,24(1)" : "=r" (pass_TOC));
  asm("addi 1,1,32");
  asm("addi 12,12,(real_printk-printk)@l");
  asm("ld 0,16(1)\n\tmtlr 0");
  asm("b real_printk");
}

extern int vprintk_default(const char *fmt, va_list args);

extern int printk(const char *fmt, ...) asm("real_printk");
int printk(const char *fmt, ...)
{
        va_list args;
        int r;

        va_start(args, fmt);

        r = vprintk_default(fmt, args);

        va_end(args);

	asm("mr 2,%0" : : "r" (pass_TOC));
        return r;
}
------------------------8<----------------------
Signed-off-by: Torsten Duwe <redacted>

As you can see, the extra effort for args on the stack is limited.

	Torsten

Re: [v5][PATCH] livepatch/ppc: Enable livepatching on powerpc

From: Petr Mladek <pmladek@suse.com>
Date: 2016-03-08 16:03:13

On Tue 2016-03-08 18:33:57, Balbir Singh wrote:
Changelog v5:
	1. Removed the mini-stack frame created for klp_return_helper.
	   As a result of the mini-stack frame, function with > 8
	   arguments could not be patched
	2. Removed camel casing in the comments
I tested this patch and it fails when I call a patched printk()
from a module.

You might try it with the test patch below. It is a bit twisted
because it calls the patched printk from livepatch_cmdline_proc_show()
that it added by the same patch module. Please, look at
livepatch_cmdline_proc_show(), it does:

	static int count;

	if (!count++)
		trace_printk("%s\n", "this has been live patched");
	else
		printk("%s\n", "this has been live patched");


It means that calls only trace_printk() when called first time.
It calls the patched printk when called second time.


I have tested it the following way:


# booted kernel with the changes below
# applied the patch:
$> modprobe livepatch-sample

# trigger the pached printk()
$>cat /sys/kernel/livepatch/livepatch_sample/enabled
1

# look into both dmesg and trace buffer
$> dmesg | tail -n 1
[  727.537307] patch enabled: 1
$> cat /sys/kernel/debug/tracing/trace | tail -n 1
             cat-3588  [003] ....   727.537448: livepatch_printk: patch enabled: 1

# trigger livepatch_cmdline_proc_show() 1st time
c79:~ # cat /proc/cmdline 
this has been live patched

# the message appeared only in trace buffer
$> dmesg | tail -n 1
[  727.537307] patch enabled: 1
c79:~ # cat /sys/kernel/debug/tracing/trace | tail -n 1
             cat-3511  [000] ....    862.958383:	     livepatch_cmdline_proc_show: this has been live patched


# trigger livepatch_cmdline_proc_show() 2nd time
c79:~ # cat /proc/cmdline 

!!! KABOOM !!!

It is becaused it tried to call the patched printk()?

Unable to handle kernel paging request for instruction fetch
Faulting instruction address: 0xc0000000023f014c
Oops: Kernel access of bad area, sig: 11 [#1]
SMP NR_CPUS=2048 NUMA pSeries
Modules linked in: livepatch_sample af_packet dm_mod rtc_generic e1000 ext4 crc16 mbcache jbd2 sr_mod cdrom sd_mod ibmvscsi scsi_transport_srp sg scsi_mod autofs4
CPU: 1 PID: 3514 Comm: cat Tainted: G              K 4.5.0-rc7-11-default+ #110
task: c000000003e60e20 ti: c000000003d38000 task.ti: c000000003d38000
NIP: c0000000023f014c LR: c0000000023f014c CTR: c0000000001a72c0
REGS: c000000003d3b930 TRAP: 0400   Tainted: G              K  (4.5.0-rc7-11-default+)
MSR: 8000000010009033 <SF,EE,ME,IR,DR,RI,LE>  CR: 28222022  XER: 20000000
CFAR: c000000000009e9c SOFTE: 1 
GPR00: c0000000023f014c c000000003d3bbb0 c000000000fae100 0000000000000000 
GPR04: c0000000fea60038 000000000000000c 0000000068637461 0000000000000068 
GPR08: 0000000000000000 c000000003e627cc c000000003e60e20 d0000000023f0308 
GPR12: 0000000000002200 c000000007e80300 0000000010020360 0000000000010000 
GPR16: 0000000000000000 0000000000000000 0000000000000000 0000000000000000 
GPR20: 0000010000300000 c0000000035bf540 0000000000010000 c000000003d3be00 
GPR24: c0000000035b8500 0000000000000000 fffffffffffff000 c000000003d3bc58 
GPR28: 0000010000300000 c0000000035bf500 d0000000023f0578 d0000000023f0558 
NIP [c0000000023f014c] 0xc0000000023f014c
LR [c0000000023f014c] 0xc0000000023f014c
Call Trace:
[c000000003d3bbb0] [c0000000023f014c] 0xc0000000023f014c (unreliable)
[c000000003d3bc30] [c000000000009e88] klp_return_helper+0x0/0x18
[c000000003d3bcd0] [c00000000034798c] proc_reg_read+0x8c/0xd0
[c000000003d3bd00] [c0000000002b7fbc] __vfs_read+0x4c/0x160
[c000000003d3bd90] [c0000000002b9318] vfs_read+0xa8/0x1c0
[c000000003d3bde0] [c0000000002ba61c] SyS_read+0x6c/0x110
[c000000003d3be30] [c000000000009204] system_call+0x38/0xb4
Instruction dump:
XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX 
XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX 
---[ end trace 17a32fcaa99f5af5 ]---



Here is the patch that I used:
From 313627cab861dd53d3325efc3d4d364eee77f9db Mon Sep 17 00:00:00 2001
From: Petr Mladek <pmladek@suse.com>
Date: Tue, 8 Mar 2016 13:51:02 +0100
Subject: [PATCH] livepatch: test_printk() patch

!!!!IMPORTANT!!!

The patch is a bit ugly because cmdline_proc_show() can be called
also by some other code. So, you might get the crash earlier than
you expect.
---
 include/linux/printk.h               |  3 +++
 kernel/livepatch/core.c              |  1 +
 kernel/printk/printk.c               | 23 +++++++++++++++++++++
 samples/livepatch/livepatch-sample.c | 39 ++++++++++++++++++++++++++++++++++++
 4 files changed, 66 insertions(+)
diff --git a/include/linux/printk.h b/include/linux/printk.h
index 9ccbdf2c1453..ffe0ceb56972 100644
--- a/include/linux/printk.h
+++ b/include/linux/printk.h
@@ -125,6 +125,9 @@ void early_printk(const char *s, ...) { }
 typedef __printf(1, 0) int (*printk_func_t)(const char *fmt, va_list args);
 
 #ifdef CONFIG_PRINTK
+int vprintk_default(const char *fmt, va_list args);
+int test_printk(const char *fmt, ...);
+
 asmlinkage __printf(5, 0)
 int vprintk_emit(int facility, int level,
 		 const char *dict, size_t dictlen,
diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c
index e2dbf0127f0f..7d0a6029043c 100644
--- a/kernel/livepatch/core.c
+++ b/kernel/livepatch/core.c
@@ -615,6 +615,7 @@ static ssize_t enabled_show(struct kobject *kobj,
 	struct klp_patch *patch;
 
 	patch = container_of(kobj, struct klp_patch, kobj);
+	printk("patch enabled: %d\n", patch->state);
 	return snprintf(buf, PAGE_SIZE-1, "%d\n", patch->state);
 }
 
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index c963ba534a78..9f785bfbb3fd 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -1920,6 +1920,29 @@ asmlinkage __visible int printk(const char *fmt, ...)
 }
 EXPORT_SYMBOL(printk);
 
+int test_printk(const char *fmt, ...)
+{
+	printk_func_t vprintk_func;
+	va_list args;
+	int r;
+
+	va_start(args, fmt);
+
+	/*
+	 * If a caller overrides the per_cpu printk_func, then it needs
+	 * to disable preemption when calling printk(). Otherwise
+	 * the printk_func should be set to the default. No need to
+	 * disable preemption here.
+	 */
+	vprintk_func = this_cpu_read(printk_func);
+	r = vprintk_func(fmt, args);
+
+	va_end(args);
+
+	return r;
+}
+EXPORT_SYMBOL(test_printk);
+
 #else /* CONFIG_PRINTK */
 
 #define LOG_LINE_MAX		0
diff --git a/samples/livepatch/livepatch-sample.c b/samples/livepatch/livepatch-sample.c
index fb8c8614e728..d5c09bc629e8 100644
--- a/samples/livepatch/livepatch-sample.c
+++ b/samples/livepatch/livepatch-sample.c
@@ -40,16 +40,53 @@
  */
 
 #include <linux/seq_file.h>
+#include <linux/printk.h>
 static int livepatch_cmdline_proc_show(struct seq_file *m, void *v)
 {
+	static int count;
+
+	if (!count++)
+		trace_printk("%s\n", "this has been live patched");
+	else
+		printk("%s\n", "this has been live patched");
+
 	seq_printf(m, "%s\n", "this has been live patched");
 	return 0;
 }
 
+asmlinkage static int livepatch_printk(const char *fmt, ...)
+{
+	va_list args, args2;
+	int r = 0;
+
+	va_start(args, fmt);
+
+	/*
+	 * If a caller overrides the per_cpu printk_func, then it needs
+	 * to disable preemption when calling printk(). Otherwise
+	 * the printk_func should be set to the default. No need to
+	 * disable preemption here.
+	 */
+	vprintk_default(fmt, args);
+
+	va_end(args);
+
+	va_start(args2, fmt);
+	ftrace_vprintk(fmt, args2);
+	va_end(args2);
+
+
+	return r;
+}
+
 static struct klp_func funcs[] = {
 	{
 		.old_name = "cmdline_proc_show",
 		.new_func = livepatch_cmdline_proc_show,
+	},
+	{
+		.old_name = "printk",
+		.new_func = livepatch_printk,
 	}, { }
 };
 
@@ -77,6 +114,8 @@ static int livepatch_init(void)
 		WARN_ON(klp_unregister_patch(&patch));
 		return ret;
 	}
+	/* Make sure that trace_printk buffers are allocated. */
+	trace_printk("LivePatch sample loaded\n");
 	return 0;
 }
 
-- 
1.8.5.6

Re: [v5][PATCH] livepatch/ppc: Enable livepatching on powerpc

From: Balbir Singh <bsingharora@gmail.com>
Date: 2016-03-09 03:37:52


On 09/03/16 03:02, Petr Mladek wrote:
On Tue 2016-03-08 18:33:57, Balbir Singh wrote:
quoted
Changelog v5:
	1. Removed the mini-stack frame created for klp_return_helper.
	   As a result of the mini-stack frame, function with > 8
	   arguments could not be patched
	2. Removed camel casing in the comments
I tested this patch and it fails when I call a patched printk()
from a module.

You might try it with the test patch below. It is a bit twisted
because it calls the patched printk from livepatch_cmdline_proc_show()
that it added by the same patch module. Please, look at
livepatch_cmdline_proc_show(), it does:

	static int count;

	if (!count++)
		trace_printk("%s\n", "this has been live patched");
	else
		printk("%s\n", "this has been live patched");


It means that calls only trace_printk() when called first time.
It calls the patched printk when called second time.


I have tested it the following way:


# booted kernel with the changes below
# applied the patch:
$> modprobe livepatch-sample

# trigger the pached printk()
$>cat /sys/kernel/livepatch/livepatch_sample/enabled
1

# look into both dmesg and trace buffer
$> dmesg | tail -n 1
[  727.537307] patch enabled: 1
$> cat /sys/kernel/debug/tracing/trace | tail -n 1
             cat-3588  [003] ....   727.537448: livepatch_printk: patch enabled: 1

# trigger livepatch_cmdline_proc_show() 1st time
c79:~ # cat /proc/cmdline 
this has been live patched

# the message appeared only in trace buffer
$> dmesg | tail -n 1
[  727.537307] patch enabled: 1
c79:~ # cat /sys/kernel/debug/tracing/trace | tail -n 1
             cat-3511  [000] ....    862.958383:	     livepatch_cmdline_proc_show: this has been live patched


# trigger livepatch_cmdline_proc_show() 2nd time
c79:~ # cat /proc/cmdline 

!!! KABOOM !!!

It is becaused it tried to call the patched printk()?
Yes, the situation is that we restored the r2 for the kernel (from ftrace_caller, it is now kernel_toc),
whereas the LR points to the module. The difference between r2 and r0 > 4GB.

Very good test case. Did it work with v4? I presume it did because we have enough space to save both

Thanks,
Balbir Singh

Re: [v5][PATCH] livepatch/ppc: Enable livepatching on powerpc

From: Balbir Singh <bsingharora@gmail.com>
Date: 2016-03-09 03:41:31


On 09/03/16 02:34, Torsten Duwe wrote:
On Wed, Mar 09, 2016 at 12:52:03AM +1100, Balbir Singh wrote:
quoted
On 08/03/16 21:45, Torsten Duwe wrote:
quoted
To be fair, my last mail still was not 100% correct, but the conclusion
Wrote a correction to the correction. It should be clear now. Please nag me
if it isn't clear why klp_return_helper and its stack frame is needed.
quoted
quoted
that the mini frame is not needed at all is invalid. Please leave it as it
was, I'm working on a test / demonstrator for how to handle these.
Why, the magic will be in the patched function? Please share the test/demonstrator
Here it comes...
quoted
quoted
NAKed-by: Torsten Duwe [off-list ref]
Why? For using CR+4 or removing the frame? Or you believe there is a better way to
handle this that work, IOW what is broken?
The stack frame removal. You're risking a memory access or jump into nirvana
or and endless loop.

klp_return_helper will do the right thing, and functions like e.g. printk
I would live patch like this (untested :-) :

------------------------8<----------------------
#include <stdarg.h>

/* compile using "-ffixed-r14"! */
register unsigned long pass_TOC asm("r14");

/*
 * Function pre-prologue to pop the klp_return_helper
 * mini stack frame. The saved r2 TOC value is read and
 * passed in pass_TOC (r14), the original LR is passed
 * in r0 and the LR itself. R12 is updated appropriately
 * for local TOC recalculation.
 */
extern void caller(void) asm("printk");
void caller(void)
{
  asm("ld %0,24(1)" : "=r" (pass_TOC));
  asm("addi 1,1,32");
  asm("addi 12,12,(real_printk-printk)@l");
  asm("ld 0,16(1)\n\tmtlr 0");
  asm("b real_printk");
}

extern int vprintk_default(const char *fmt, va_list args);

extern int printk(const char *fmt, ...) asm("real_printk");
int printk(const char *fmt, ...)
{
        va_list args;
        int r;

        va_start(args, fmt);

        r = vprintk_default(fmt, args);

        va_end(args);

	asm("mr 2,%0" : : "r" (pass_TOC));
        return r;
}
------------------------8<----------------------
Signed-off-by: Torsten Duwe <redacted>

As you can see, the extra effort for args on the stack is limited.
Leaving it to the patch to do the right thing I think makes it more
complex and each livepatch hardware dependent to a large extent.
I find it hard to read the patch, let alone audit it and apply it or
worse create one
	Torsten
Balbir Singh.

Re: [v5][PATCH] livepatch/ppc: Enable livepatching on powerpc

From: Petr Mladek <pmladek@suse.com>
Date: 2016-03-09 16:10:36

On Tue 2016-03-08 16:34:35, Torsten Duwe wrote:
On Wed, Mar 09, 2016 at 12:52:03AM +1100, Balbir Singh wrote:
quoted
On 08/03/16 21:45, Torsten Duwe wrote:
quoted
To be fair, my last mail still was not 100% correct, but the conclusion
Wrote a correction to the correction. It should be clear now. Please nag me
if it isn't clear why klp_return_helper and its stack frame is needed.
quoted
quoted
that the mini frame is not needed at all is invalid. Please leave it as it
was, I'm working on a test / demonstrator for how to handle these.
Why, the magic will be in the patched function? Please share the test/demonstrator
Here it comes...
quoted
quoted
NAKed-by: Torsten Duwe [off-list ref]
Why? For using CR+4 or removing the frame? Or you believe there is a better way to
handle this that work, IOW what is broken?
The stack frame removal. You're risking a memory access or jump into nirvana
or and endless loop.

klp_return_helper will do the right thing, and functions like e.g. printk
I would live patch like this (untested :-) :

------------------------8<----------------------
#include <stdarg.h>

/* compile using "-ffixed-r14"! */
register unsigned long pass_TOC asm("r14");
BTW: Is this reentrant, please? I mean, is it possible to use this
hack for two functions? Could the functions call each other?

Sigh, I still need to sort all the information. I am not sure
what are the exact limitations of each proposed solution.

Thanks,
Petr
/*
 * Function pre-prologue to pop the klp_return_helper
 * mini stack frame. The saved r2 TOC value is read and
 * passed in pass_TOC (r14), the original LR is passed
 * in r0 and the LR itself. R12 is updated appropriately
 * for local TOC recalculation.
 */
extern void caller(void) asm("printk");
void caller(void)
{
  asm("ld %0,24(1)" : "=r" (pass_TOC));
  asm("addi 1,1,32");
  asm("addi 12,12,(real_printk-printk)@l");
  asm("ld 0,16(1)\n\tmtlr 0");
  asm("b real_printk");
}

extern int vprintk_default(const char *fmt, va_list args);

extern int printk(const char *fmt, ...) asm("real_printk");
int printk(const char *fmt, ...)
{
        va_list args;
        int r;

        va_start(args, fmt);

        r = vprintk_default(fmt, args);

        va_end(args);

	asm("mr 2,%0" : : "r" (pass_TOC));
        return r;
}
------------------------8<----------------------
Signed-off-by: Torsten Duwe <redacted>

As you can see, the extra effort for args on the stack is limited.

	Torsten

Re: [v5][PATCH] livepatch/ppc: Enable livepatching on powerpc

From: Torsten Duwe <hidden>
Date: 2016-03-09 17:26:40

On Wed, Mar 09, 2016 at 05:10:25PM +0100, Petr Mladek wrote:
On Tue 2016-03-08 16:34:35, Torsten Duwe wrote:
quoted
/* compile using "-ffixed-r14"! */
register unsigned long pass_TOC asm("r14");
BTW: Is this reentrant, please? I mean, is it possible to use this
hack for two functions? Could the functions call each other?
Yes, it is. pass_TOC isn't really a global variable. It only lives
between the caller and the real function's return. With this notation
the task can easily be shifted to any other register.

	Torsten
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help