aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/kernel/ptrace.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/x86/kernel/ptrace.c')
-rw-r--r--arch/x86/kernel/ptrace.c42
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 '&regs->sp'.
175 *
176 * Now, if the stack is empty, '&regs->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 */
183unsigned 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)&regs->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}
198EXPORT_SYMBOL_GPL(kernel_stack_pointer);
199
169static unsigned long *pt_regs_access(struct pt_regs *regs, unsigned long regno) 200static 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}