summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRussell King <rmk+kernel@armlinux.org.uk>2017-11-24 18:54:22 -0500
committerRussell King <rmk+kernel@armlinux.org.uk>2017-12-17 17:14:21 -0500
commitc608906165355089a4de3c9133c72e81e011096c (patch)
treecc1a76bc86bdcc8f0e69e4e3cb7874dcfccd26d2
parent75fea300d73ae5b18957949a53ec770daaeb6fc2 (diff)
ARM: probes: avoid adding kprobes to sensitive kernel-entry/exit code
Avoid adding kprobes to any of the kernel entry/exit or startup assembly code, or code in the identity-mapped region. This code does not conform to the standard C conventions, which means that the expectations of the kprobes code is not forfilled. Placing kprobes at some of these locations results in the kernel trying to return to userspace addresses while retaining the CPU in kernel mode. Tested-by: Naresh Kamboju <naresh.kamboju@linaro.org> Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
-rw-r--r--arch/arm/include/asm/exception.h3
-rw-r--r--arch/arm/include/asm/sections.h21
-rw-r--r--arch/arm/include/asm/traps.h12
-rw-r--r--arch/arm/kernel/entry-armv.S6
-rw-r--r--arch/arm/kernel/entry-common.S1
-rw-r--r--arch/arm/kernel/stacktrace.c14
-rw-r--r--arch/arm/kernel/traps.c4
-rw-r--r--arch/arm/kernel/vmlinux-xip.lds.S6
-rw-r--r--arch/arm/kernel/vmlinux.lds.S6
-rw-r--r--arch/arm/mm/fault.c5
-rw-r--r--arch/arm/probes/kprobes/core.c14
11 files changed, 47 insertions, 45 deletions
diff --git a/arch/arm/include/asm/exception.h b/arch/arm/include/asm/exception.h
index a7273ad9587a..58e039a851af 100644
--- a/arch/arm/include/asm/exception.h
+++ b/arch/arm/include/asm/exception.h
@@ -10,11 +10,10 @@
10 10
11#include <linux/interrupt.h> 11#include <linux/interrupt.h>
12 12
13#define __exception __attribute__((section(".exception.text")))
14#ifdef CONFIG_FUNCTION_GRAPH_TRACER 13#ifdef CONFIG_FUNCTION_GRAPH_TRACER
15#define __exception_irq_entry __irq_entry 14#define __exception_irq_entry __irq_entry
16#else 15#else
17#define __exception_irq_entry __exception 16#define __exception_irq_entry
18#endif 17#endif
19 18
20#endif /* __ASM_ARM_EXCEPTION_H */ 19#endif /* __ASM_ARM_EXCEPTION_H */
diff --git a/arch/arm/include/asm/sections.h b/arch/arm/include/asm/sections.h
index 63dfe1f10335..4ceb4f757d4d 100644
--- a/arch/arm/include/asm/sections.h
+++ b/arch/arm/include/asm/sections.h
@@ -6,4 +6,25 @@
6 6
7extern char _exiprom[]; 7extern char _exiprom[];
8 8
9extern char __idmap_text_start[];
10extern char __idmap_text_end[];
11extern char __entry_text_start[];
12extern char __entry_text_end[];
13extern char __hyp_idmap_text_start[];
14extern char __hyp_idmap_text_end[];
15
16static inline bool in_entry_text(unsigned long addr)
17{
18 return memory_contains(__entry_text_start, __entry_text_end,
19 (void *)addr, 1);
20}
21
22static inline bool in_idmap_text(unsigned long addr)
23{
24 void *a = (void *)addr;
25 return memory_contains(__idmap_text_start, __idmap_text_end, a, 1) ||
26 memory_contains(__hyp_idmap_text_start, __hyp_idmap_text_end,
27 a, 1);
28}
29
9#endif /* _ASM_ARM_SECTIONS_H */ 30#endif /* _ASM_ARM_SECTIONS_H */
diff --git a/arch/arm/include/asm/traps.h b/arch/arm/include/asm/traps.h
index f9a6c5fc3fd1..a00288d75ee6 100644
--- a/arch/arm/include/asm/traps.h
+++ b/arch/arm/include/asm/traps.h
@@ -28,18 +28,6 @@ static inline int __in_irqentry_text(unsigned long ptr)
28 ptr < (unsigned long)&__irqentry_text_end; 28 ptr < (unsigned long)&__irqentry_text_end;
29} 29}
30 30
31static inline int in_exception_text(unsigned long ptr)
32{
33 extern char __exception_text_start[];
34 extern char __exception_text_end[];
35 int in;
36
37 in = ptr >= (unsigned long)&__exception_text_start &&
38 ptr < (unsigned long)&__exception_text_end;
39
40 return in ? : __in_irqentry_text(ptr);
41}
42
43extern void __init early_trap_init(void *); 31extern void __init early_trap_init(void *);
44extern void dump_backtrace_entry(unsigned long where, unsigned long from, unsigned long frame); 32extern void dump_backtrace_entry(unsigned long where, unsigned long from, unsigned long frame);
45extern void ptrace_break(struct task_struct *tsk, struct pt_regs *regs); 33extern void ptrace_break(struct task_struct *tsk, struct pt_regs *regs);
diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S
index fbc707626b3e..1752033b0070 100644
--- a/arch/arm/kernel/entry-armv.S
+++ b/arch/arm/kernel/entry-armv.S
@@ -82,11 +82,7 @@
82#endif 82#endif
83 .endm 83 .endm
84 84
85#ifdef CONFIG_KPROBES 85 .section .entry.text,"ax",%progbits
86 .section .kprobes.text,"ax",%progbits
87#else
88 .text
89#endif
90 86
91/* 87/*
92 * Invalid mode handlers 88 * Invalid mode handlers
diff --git a/arch/arm/kernel/entry-common.S b/arch/arm/kernel/entry-common.S
index e655dcd0a933..3c4f88701f22 100644
--- a/arch/arm/kernel/entry-common.S
+++ b/arch/arm/kernel/entry-common.S
@@ -37,6 +37,7 @@ saved_pc .req lr
37#define TRACE(x...) 37#define TRACE(x...)
38#endif 38#endif
39 39
40 .section .entry.text,"ax",%progbits
40 .align 5 41 .align 5
41#if !(IS_ENABLED(CONFIG_TRACE_IRQFLAGS) || IS_ENABLED(CONFIG_CONTEXT_TRACKING)) 42#if !(IS_ENABLED(CONFIG_TRACE_IRQFLAGS) || IS_ENABLED(CONFIG_CONTEXT_TRACKING))
42/* 43/*
diff --git a/arch/arm/kernel/stacktrace.c b/arch/arm/kernel/stacktrace.c
index 65228bf4c6df..a56e7c856ab5 100644
--- a/arch/arm/kernel/stacktrace.c
+++ b/arch/arm/kernel/stacktrace.c
@@ -3,6 +3,7 @@
3#include <linux/sched/debug.h> 3#include <linux/sched/debug.h>
4#include <linux/stacktrace.h> 4#include <linux/stacktrace.h>
5 5
6#include <asm/sections.h>
6#include <asm/stacktrace.h> 7#include <asm/stacktrace.h>
7#include <asm/traps.h> 8#include <asm/traps.h>
8 9
@@ -63,7 +64,6 @@ EXPORT_SYMBOL(walk_stackframe);
63#ifdef CONFIG_STACKTRACE 64#ifdef CONFIG_STACKTRACE
64struct stack_trace_data { 65struct stack_trace_data {
65 struct stack_trace *trace; 66 struct stack_trace *trace;
66 unsigned long last_pc;
67 unsigned int no_sched_functions; 67 unsigned int no_sched_functions;
68 unsigned int skip; 68 unsigned int skip;
69}; 69};
@@ -87,16 +87,7 @@ static int save_trace(struct stackframe *frame, void *d)
87 if (trace->nr_entries >= trace->max_entries) 87 if (trace->nr_entries >= trace->max_entries)
88 return 1; 88 return 1;
89 89
90 /* 90 if (!in_entry_text(frame->pc))
91 * in_exception_text() is designed to test if the PC is one of
92 * the functions which has an exception stack above it, but
93 * unfortunately what is in frame->pc is the return LR value,
94 * not the saved PC value. So, we need to track the previous
95 * frame PC value when doing this.
96 */
97 addr = data->last_pc;
98 data->last_pc = frame->pc;
99 if (!in_exception_text(addr))
100 return 0; 91 return 0;
101 92
102 regs = (struct pt_regs *)frame->sp; 93 regs = (struct pt_regs *)frame->sp;
@@ -114,7 +105,6 @@ static noinline void __save_stack_trace(struct task_struct *tsk,
114 struct stackframe frame; 105 struct stackframe frame;
115 106
116 data.trace = trace; 107 data.trace = trace;
117 data.last_pc = ULONG_MAX;
118 data.skip = trace->skip; 108 data.skip = trace->skip;
119 data.no_sched_functions = nosched; 109 data.no_sched_functions = nosched;
120 110
diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c
index 5cf04888c581..e344bdd2e5ac 100644
--- a/arch/arm/kernel/traps.c
+++ b/arch/arm/kernel/traps.c
@@ -72,7 +72,7 @@ void dump_backtrace_entry(unsigned long where, unsigned long from, unsigned long
72 printk("Function entered at [<%08lx>] from [<%08lx>]\n", where, from); 72 printk("Function entered at [<%08lx>] from [<%08lx>]\n", where, from);
73#endif 73#endif
74 74
75 if (in_exception_text(where)) 75 if (in_entry_text(from))
76 dump_mem("", "Exception stack", frame + 4, frame + 4 + sizeof(struct pt_regs)); 76 dump_mem("", "Exception stack", frame + 4, frame + 4 + sizeof(struct pt_regs));
77} 77}
78 78
@@ -433,7 +433,7 @@ static int call_undef_hook(struct pt_regs *regs, unsigned int instr)
433 return fn ? fn(regs, instr) : 1; 433 return fn ? fn(regs, instr) : 1;
434} 434}
435 435
436asmlinkage void __exception do_undefinstr(struct pt_regs *regs) 436asmlinkage void do_undefinstr(struct pt_regs *regs)
437{ 437{
438 unsigned int instr; 438 unsigned int instr;
439 siginfo_t info; 439 siginfo_t info;
diff --git a/arch/arm/kernel/vmlinux-xip.lds.S b/arch/arm/kernel/vmlinux-xip.lds.S
index ec4b3f94ad80..12b87591eb7c 100644
--- a/arch/arm/kernel/vmlinux-xip.lds.S
+++ b/arch/arm/kernel/vmlinux-xip.lds.S
@@ -96,9 +96,9 @@ SECTIONS
96 .text : { /* Real text segment */ 96 .text : { /* Real text segment */
97 _stext = .; /* Text and read-only data */ 97 _stext = .; /* Text and read-only data */
98 IDMAP_TEXT 98 IDMAP_TEXT
99 __exception_text_start = .; 99 __entry_text_start = .;
100 *(.exception.text) 100 *(.entry.text)
101 __exception_text_end = .; 101 __entry_text_end = .;
102 IRQENTRY_TEXT 102 IRQENTRY_TEXT
103 TEXT_TEXT 103 TEXT_TEXT
104 SCHED_TEXT 104 SCHED_TEXT
diff --git a/arch/arm/kernel/vmlinux.lds.S b/arch/arm/kernel/vmlinux.lds.S
index ee53f6518872..84a1ae3ce46e 100644
--- a/arch/arm/kernel/vmlinux.lds.S
+++ b/arch/arm/kernel/vmlinux.lds.S
@@ -105,9 +105,9 @@ SECTIONS
105 .text : { /* Real text segment */ 105 .text : { /* Real text segment */
106 _stext = .; /* Text and read-only data */ 106 _stext = .; /* Text and read-only data */
107 IDMAP_TEXT 107 IDMAP_TEXT
108 __exception_text_start = .; 108 __entry_text_start = .;
109 *(.exception.text) 109 *(.entry.text)
110 __exception_text_end = .; 110 __entry_text_end = .;
111 IRQENTRY_TEXT 111 IRQENTRY_TEXT
112 SOFTIRQENTRY_TEXT 112 SOFTIRQENTRY_TEXT
113 TEXT_TEXT 113 TEXT_TEXT
diff --git a/arch/arm/mm/fault.c b/arch/arm/mm/fault.c
index 42f585379e19..b75eada23d0a 100644
--- a/arch/arm/mm/fault.c
+++ b/arch/arm/mm/fault.c
@@ -21,7 +21,6 @@
21#include <linux/highmem.h> 21#include <linux/highmem.h>
22#include <linux/perf_event.h> 22#include <linux/perf_event.h>
23 23
24#include <asm/exception.h>
25#include <asm/pgtable.h> 24#include <asm/pgtable.h>
26#include <asm/system_misc.h> 25#include <asm/system_misc.h>
27#include <asm/system_info.h> 26#include <asm/system_info.h>
@@ -545,7 +544,7 @@ hook_fault_code(int nr, int (*fn)(unsigned long, unsigned int, struct pt_regs *)
545/* 544/*
546 * Dispatch a data abort to the relevant handler. 545 * Dispatch a data abort to the relevant handler.
547 */ 546 */
548asmlinkage void __exception 547asmlinkage void
549do_DataAbort(unsigned long addr, unsigned int fsr, struct pt_regs *regs) 548do_DataAbort(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
550{ 549{
551 const struct fsr_info *inf = fsr_info + fsr_fs(fsr); 550 const struct fsr_info *inf = fsr_info + fsr_fs(fsr);
@@ -578,7 +577,7 @@ hook_ifault_code(int nr, int (*fn)(unsigned long, unsigned int, struct pt_regs *
578 ifsr_info[nr].name = name; 577 ifsr_info[nr].name = name;
579} 578}
580 579
581asmlinkage void __exception 580asmlinkage void
582do_PrefetchAbort(unsigned long addr, unsigned int ifsr, struct pt_regs *regs) 581do_PrefetchAbort(unsigned long addr, unsigned int ifsr, struct pt_regs *regs)
583{ 582{
584 const struct fsr_info *inf = ifsr_info + fsr_fs(ifsr); 583 const struct fsr_info *inf = ifsr_info + fsr_fs(ifsr);
diff --git a/arch/arm/probes/kprobes/core.c b/arch/arm/probes/kprobes/core.c
index 52d1cd14fda4..e90cc8a08186 100644
--- a/arch/arm/probes/kprobes/core.c
+++ b/arch/arm/probes/kprobes/core.c
@@ -32,6 +32,7 @@
32#include <linux/percpu.h> 32#include <linux/percpu.h>
33#include <linux/bug.h> 33#include <linux/bug.h>
34#include <asm/patch.h> 34#include <asm/patch.h>
35#include <asm/sections.h>
35 36
36#include "../decode-arm.h" 37#include "../decode-arm.h"
37#include "../decode-thumb.h" 38#include "../decode-thumb.h"
@@ -64,9 +65,6 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
64 int is; 65 int is;
65 const struct decode_checker **checkers; 66 const struct decode_checker **checkers;
66 67
67 if (in_exception_text(addr))
68 return -EINVAL;
69
70#ifdef CONFIG_THUMB2_KERNEL 68#ifdef CONFIG_THUMB2_KERNEL
71 thumb = true; 69 thumb = true;
72 addr &= ~1; /* Bit 0 would normally be set to indicate Thumb code */ 70 addr &= ~1; /* Bit 0 would normally be set to indicate Thumb code */
@@ -680,3 +678,13 @@ int __init arch_init_kprobes()
680#endif 678#endif
681 return 0; 679 return 0;
682} 680}
681
682bool arch_within_kprobe_blacklist(unsigned long addr)
683{
684 void *a = (void *)addr;
685
686 return __in_irqentry_text(addr) ||
687 in_entry_text(addr) ||
688 in_idmap_text(addr) ||
689 memory_contains(__kprobes_text_start, __kprobes_text_end, a, 1);
690}