diff options
Diffstat (limited to 'arch/x86/kernel/ptrace.c')
-rw-r--r-- | arch/x86/kernel/ptrace.c | 42 |
1 files changed, 40 insertions, 2 deletions
diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c index b00b33a18390..b629bbe0d9bd 100644 --- a/arch/x86/kernel/ptrace.c +++ b/arch/x86/kernel/ptrace.c | |||
@@ -22,6 +22,8 @@ | |||
22 | #include <linux/perf_event.h> | 22 | #include <linux/perf_event.h> |
23 | #include <linux/hw_breakpoint.h> | 23 | #include <linux/hw_breakpoint.h> |
24 | #include <linux/rcupdate.h> | 24 | #include <linux/rcupdate.h> |
25 | #include <linux/module.h> | ||
26 | #include <linux/context_tracking.h> | ||
25 | 27 | ||
26 | #include <asm/uaccess.h> | 28 | #include <asm/uaccess.h> |
27 | #include <asm/pgtable.h> | 29 | #include <asm/pgtable.h> |
@@ -166,6 +168,35 @@ static inline bool invalid_selector(u16 value) | |||
166 | 168 | ||
167 | #define FLAG_MASK FLAG_MASK_32 | 169 | #define FLAG_MASK FLAG_MASK_32 |
168 | 170 | ||
171 | /* | ||
172 | * X86_32 CPUs don't save ss and esp if the CPU is already in kernel mode | ||
173 | * when it traps. The previous stack will be directly underneath the saved | ||
174 | * registers, and 'sp/ss' won't even have been saved. Thus the '®s->sp'. | ||
175 | * | ||
176 | * Now, if the stack is empty, '®s->sp' is out of range. In this | ||
177 | * case we try to take the previous stack. To always return a non-null | ||
178 | * stack pointer we fall back to regs as stack if no previous stack | ||
179 | * exists. | ||
180 | * | ||
181 | * This is valid only for kernel mode traps. | ||
182 | */ | ||
183 | unsigned long kernel_stack_pointer(struct pt_regs *regs) | ||
184 | { | ||
185 | unsigned long context = (unsigned long)regs & ~(THREAD_SIZE - 1); | ||
186 | unsigned long sp = (unsigned long)®s->sp; | ||
187 | struct thread_info *tinfo; | ||
188 | |||
189 | if (context == (sp & ~(THREAD_SIZE - 1))) | ||
190 | return sp; | ||
191 | |||
192 | tinfo = (struct thread_info *)context; | ||
193 | if (tinfo->previous_esp) | ||
194 | return tinfo->previous_esp; | ||
195 | |||
196 | return (unsigned long)regs; | ||
197 | } | ||
198 | EXPORT_SYMBOL_GPL(kernel_stack_pointer); | ||
199 | |||
169 | static unsigned long *pt_regs_access(struct pt_regs *regs, unsigned long regno) | 200 | static unsigned long *pt_regs_access(struct pt_regs *regs, unsigned long regno) |
170 | { | 201 | { |
171 | BUILD_BUG_ON(offsetof(struct pt_regs, bx) != 0); | 202 | BUILD_BUG_ON(offsetof(struct pt_regs, bx) != 0); |
@@ -1461,7 +1492,7 @@ long syscall_trace_enter(struct pt_regs *regs) | |||
1461 | { | 1492 | { |
1462 | long ret = 0; | 1493 | long ret = 0; |
1463 | 1494 | ||
1464 | rcu_user_exit(); | 1495 | user_exit(); |
1465 | 1496 | ||
1466 | /* | 1497 | /* |
1467 | * If we stepped into a sysenter/syscall insn, it trapped in | 1498 | * If we stepped into a sysenter/syscall insn, it trapped in |
@@ -1511,6 +1542,13 @@ void syscall_trace_leave(struct pt_regs *regs) | |||
1511 | { | 1542 | { |
1512 | bool step; | 1543 | bool step; |
1513 | 1544 | ||
1545 | /* | ||
1546 | * We may come here right after calling schedule_user() | ||
1547 | * or do_notify_resume(), in which case we can be in RCU | ||
1548 | * user mode. | ||
1549 | */ | ||
1550 | user_exit(); | ||
1551 | |||
1514 | audit_syscall_exit(regs); | 1552 | audit_syscall_exit(regs); |
1515 | 1553 | ||
1516 | if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT))) | 1554 | if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT))) |
@@ -1527,5 +1565,5 @@ void syscall_trace_leave(struct pt_regs *regs) | |||
1527 | if (step || test_thread_flag(TIF_SYSCALL_TRACE)) | 1565 | if (step || test_thread_flag(TIF_SYSCALL_TRACE)) |
1528 | tracehook_report_syscall_exit(regs, step); | 1566 | tracehook_report_syscall_exit(regs, step); |
1529 | 1567 | ||
1530 | rcu_user_enter(); | 1568 | user_enter(); |
1531 | } | 1569 | } |