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>
This commit is contained in:
Родитель
75fea300d7
Коммит
c608906165
|
@ -10,11 +10,10 @@
|
||||||
|
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
|
|
||||||
#define __exception __attribute__((section(".exception.text")))
|
|
||||||
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
|
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
|
||||||
#define __exception_irq_entry __irq_entry
|
#define __exception_irq_entry __irq_entry
|
||||||
#else
|
#else
|
||||||
#define __exception_irq_entry __exception
|
#define __exception_irq_entry
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif /* __ASM_ARM_EXCEPTION_H */
|
#endif /* __ASM_ARM_EXCEPTION_H */
|
||||||
|
|
|
@ -6,4 +6,25 @@
|
||||||
|
|
||||||
extern char _exiprom[];
|
extern char _exiprom[];
|
||||||
|
|
||||||
|
extern char __idmap_text_start[];
|
||||||
|
extern char __idmap_text_end[];
|
||||||
|
extern char __entry_text_start[];
|
||||||
|
extern char __entry_text_end[];
|
||||||
|
extern char __hyp_idmap_text_start[];
|
||||||
|
extern char __hyp_idmap_text_end[];
|
||||||
|
|
||||||
|
static inline bool in_entry_text(unsigned long addr)
|
||||||
|
{
|
||||||
|
return memory_contains(__entry_text_start, __entry_text_end,
|
||||||
|
(void *)addr, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool in_idmap_text(unsigned long addr)
|
||||||
|
{
|
||||||
|
void *a = (void *)addr;
|
||||||
|
return memory_contains(__idmap_text_start, __idmap_text_end, a, 1) ||
|
||||||
|
memory_contains(__hyp_idmap_text_start, __hyp_idmap_text_end,
|
||||||
|
a, 1);
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* _ASM_ARM_SECTIONS_H */
|
#endif /* _ASM_ARM_SECTIONS_H */
|
||||||
|
|
|
@ -28,18 +28,6 @@ static inline int __in_irqentry_text(unsigned long ptr)
|
||||||
ptr < (unsigned long)&__irqentry_text_end;
|
ptr < (unsigned long)&__irqentry_text_end;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int in_exception_text(unsigned long ptr)
|
|
||||||
{
|
|
||||||
extern char __exception_text_start[];
|
|
||||||
extern char __exception_text_end[];
|
|
||||||
int in;
|
|
||||||
|
|
||||||
in = ptr >= (unsigned long)&__exception_text_start &&
|
|
||||||
ptr < (unsigned long)&__exception_text_end;
|
|
||||||
|
|
||||||
return in ? : __in_irqentry_text(ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
extern void __init early_trap_init(void *);
|
extern void __init early_trap_init(void *);
|
||||||
extern void dump_backtrace_entry(unsigned long where, unsigned long from, unsigned long frame);
|
extern void dump_backtrace_entry(unsigned long where, unsigned long from, unsigned long frame);
|
||||||
extern void ptrace_break(struct task_struct *tsk, struct pt_regs *regs);
|
extern void ptrace_break(struct task_struct *tsk, struct pt_regs *regs);
|
||||||
|
|
|
@ -82,11 +82,7 @@
|
||||||
#endif
|
#endif
|
||||||
.endm
|
.endm
|
||||||
|
|
||||||
#ifdef CONFIG_KPROBES
|
.section .entry.text,"ax",%progbits
|
||||||
.section .kprobes.text,"ax",%progbits
|
|
||||||
#else
|
|
||||||
.text
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Invalid mode handlers
|
* Invalid mode handlers
|
||||||
|
|
|
@ -37,6 +37,7 @@ saved_pc .req lr
|
||||||
#define TRACE(x...)
|
#define TRACE(x...)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
.section .entry.text,"ax",%progbits
|
||||||
.align 5
|
.align 5
|
||||||
#if !(IS_ENABLED(CONFIG_TRACE_IRQFLAGS) || IS_ENABLED(CONFIG_CONTEXT_TRACKING))
|
#if !(IS_ENABLED(CONFIG_TRACE_IRQFLAGS) || IS_ENABLED(CONFIG_CONTEXT_TRACKING))
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include <linux/sched/debug.h>
|
#include <linux/sched/debug.h>
|
||||||
#include <linux/stacktrace.h>
|
#include <linux/stacktrace.h>
|
||||||
|
|
||||||
|
#include <asm/sections.h>
|
||||||
#include <asm/stacktrace.h>
|
#include <asm/stacktrace.h>
|
||||||
#include <asm/traps.h>
|
#include <asm/traps.h>
|
||||||
|
|
||||||
|
@ -63,7 +64,6 @@ EXPORT_SYMBOL(walk_stackframe);
|
||||||
#ifdef CONFIG_STACKTRACE
|
#ifdef CONFIG_STACKTRACE
|
||||||
struct stack_trace_data {
|
struct stack_trace_data {
|
||||||
struct stack_trace *trace;
|
struct stack_trace *trace;
|
||||||
unsigned long last_pc;
|
|
||||||
unsigned int no_sched_functions;
|
unsigned int no_sched_functions;
|
||||||
unsigned int skip;
|
unsigned int skip;
|
||||||
};
|
};
|
||||||
|
@ -87,16 +87,7 @@ static int save_trace(struct stackframe *frame, void *d)
|
||||||
if (trace->nr_entries >= trace->max_entries)
|
if (trace->nr_entries >= trace->max_entries)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
/*
|
if (!in_entry_text(frame->pc))
|
||||||
* in_exception_text() is designed to test if the PC is one of
|
|
||||||
* the functions which has an exception stack above it, but
|
|
||||||
* unfortunately what is in frame->pc is the return LR value,
|
|
||||||
* not the saved PC value. So, we need to track the previous
|
|
||||||
* frame PC value when doing this.
|
|
||||||
*/
|
|
||||||
addr = data->last_pc;
|
|
||||||
data->last_pc = frame->pc;
|
|
||||||
if (!in_exception_text(addr))
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
regs = (struct pt_regs *)frame->sp;
|
regs = (struct pt_regs *)frame->sp;
|
||||||
|
@ -114,7 +105,6 @@ static noinline void __save_stack_trace(struct task_struct *tsk,
|
||||||
struct stackframe frame;
|
struct stackframe frame;
|
||||||
|
|
||||||
data.trace = trace;
|
data.trace = trace;
|
||||||
data.last_pc = ULONG_MAX;
|
|
||||||
data.skip = trace->skip;
|
data.skip = trace->skip;
|
||||||
data.no_sched_functions = nosched;
|
data.no_sched_functions = nosched;
|
||||||
|
|
||||||
|
|
|
@ -72,7 +72,7 @@ void dump_backtrace_entry(unsigned long where, unsigned long from, unsigned long
|
||||||
printk("Function entered at [<%08lx>] from [<%08lx>]\n", where, from);
|
printk("Function entered at [<%08lx>] from [<%08lx>]\n", where, from);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (in_exception_text(where))
|
if (in_entry_text(from))
|
||||||
dump_mem("", "Exception stack", frame + 4, frame + 4 + sizeof(struct pt_regs));
|
dump_mem("", "Exception stack", frame + 4, frame + 4 + sizeof(struct pt_regs));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -433,7 +433,7 @@ static int call_undef_hook(struct pt_regs *regs, unsigned int instr)
|
||||||
return fn ? fn(regs, instr) : 1;
|
return fn ? fn(regs, instr) : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
asmlinkage void __exception do_undefinstr(struct pt_regs *regs)
|
asmlinkage void do_undefinstr(struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
unsigned int instr;
|
unsigned int instr;
|
||||||
siginfo_t info;
|
siginfo_t info;
|
||||||
|
|
|
@ -96,9 +96,9 @@ SECTIONS
|
||||||
.text : { /* Real text segment */
|
.text : { /* Real text segment */
|
||||||
_stext = .; /* Text and read-only data */
|
_stext = .; /* Text and read-only data */
|
||||||
IDMAP_TEXT
|
IDMAP_TEXT
|
||||||
__exception_text_start = .;
|
__entry_text_start = .;
|
||||||
*(.exception.text)
|
*(.entry.text)
|
||||||
__exception_text_end = .;
|
__entry_text_end = .;
|
||||||
IRQENTRY_TEXT
|
IRQENTRY_TEXT
|
||||||
TEXT_TEXT
|
TEXT_TEXT
|
||||||
SCHED_TEXT
|
SCHED_TEXT
|
||||||
|
|
|
@ -105,9 +105,9 @@ SECTIONS
|
||||||
.text : { /* Real text segment */
|
.text : { /* Real text segment */
|
||||||
_stext = .; /* Text and read-only data */
|
_stext = .; /* Text and read-only data */
|
||||||
IDMAP_TEXT
|
IDMAP_TEXT
|
||||||
__exception_text_start = .;
|
__entry_text_start = .;
|
||||||
*(.exception.text)
|
*(.entry.text)
|
||||||
__exception_text_end = .;
|
__entry_text_end = .;
|
||||||
IRQENTRY_TEXT
|
IRQENTRY_TEXT
|
||||||
SOFTIRQENTRY_TEXT
|
SOFTIRQENTRY_TEXT
|
||||||
TEXT_TEXT
|
TEXT_TEXT
|
||||||
|
|
|
@ -21,7 +21,6 @@
|
||||||
#include <linux/highmem.h>
|
#include <linux/highmem.h>
|
||||||
#include <linux/perf_event.h>
|
#include <linux/perf_event.h>
|
||||||
|
|
||||||
#include <asm/exception.h>
|
|
||||||
#include <asm/pgtable.h>
|
#include <asm/pgtable.h>
|
||||||
#include <asm/system_misc.h>
|
#include <asm/system_misc.h>
|
||||||
#include <asm/system_info.h>
|
#include <asm/system_info.h>
|
||||||
|
@ -545,7 +544,7 @@ hook_fault_code(int nr, int (*fn)(unsigned long, unsigned int, struct pt_regs *)
|
||||||
/*
|
/*
|
||||||
* Dispatch a data abort to the relevant handler.
|
* Dispatch a data abort to the relevant handler.
|
||||||
*/
|
*/
|
||||||
asmlinkage void __exception
|
asmlinkage void
|
||||||
do_DataAbort(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
|
do_DataAbort(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
const struct fsr_info *inf = fsr_info + fsr_fs(fsr);
|
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 *
|
||||||
ifsr_info[nr].name = name;
|
ifsr_info[nr].name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
asmlinkage void __exception
|
asmlinkage void
|
||||||
do_PrefetchAbort(unsigned long addr, unsigned int ifsr, struct pt_regs *regs)
|
do_PrefetchAbort(unsigned long addr, unsigned int ifsr, struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
const struct fsr_info *inf = ifsr_info + fsr_fs(ifsr);
|
const struct fsr_info *inf = ifsr_info + fsr_fs(ifsr);
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
#include <linux/percpu.h>
|
#include <linux/percpu.h>
|
||||||
#include <linux/bug.h>
|
#include <linux/bug.h>
|
||||||
#include <asm/patch.h>
|
#include <asm/patch.h>
|
||||||
|
#include <asm/sections.h>
|
||||||
|
|
||||||
#include "../decode-arm.h"
|
#include "../decode-arm.h"
|
||||||
#include "../decode-thumb.h"
|
#include "../decode-thumb.h"
|
||||||
|
@ -64,9 +65,6 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
|
||||||
int is;
|
int is;
|
||||||
const struct decode_checker **checkers;
|
const struct decode_checker **checkers;
|
||||||
|
|
||||||
if (in_exception_text(addr))
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
#ifdef CONFIG_THUMB2_KERNEL
|
#ifdef CONFIG_THUMB2_KERNEL
|
||||||
thumb = true;
|
thumb = true;
|
||||||
addr &= ~1; /* Bit 0 would normally be set to indicate Thumb code */
|
addr &= ~1; /* Bit 0 would normally be set to indicate Thumb code */
|
||||||
|
@ -680,3 +678,13 @@ int __init arch_init_kprobes()
|
||||||
#endif
|
#endif
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool arch_within_kprobe_blacklist(unsigned long addr)
|
||||||
|
{
|
||||||
|
void *a = (void *)addr;
|
||||||
|
|
||||||
|
return __in_irqentry_text(addr) ||
|
||||||
|
in_entry_text(addr) ||
|
||||||
|
in_idmap_text(addr) ||
|
||||||
|
memory_contains(__kprobes_text_start, __kprobes_text_end, a, 1);
|
||||||
|
}
|
||||||
|
|
Загрузка…
Ссылка в новой задаче