diff options
-rw-r--r-- | arch/x86/include/asm/ptrace.h | 15 | ||||
-rw-r--r-- | arch/x86/kernel/ptrace.c | 28 |
2 files changed, 32 insertions, 11 deletions
diff --git a/arch/x86/include/asm/ptrace.h b/arch/x86/include/asm/ptrace.h index dcfde52979c3..19f16ebaf4fa 100644 --- a/arch/x86/include/asm/ptrace.h +++ b/arch/x86/include/asm/ptrace.h | |||
@@ -205,21 +205,14 @@ static inline bool user_64bit_mode(struct pt_regs *regs) | |||
205 | } | 205 | } |
206 | #endif | 206 | #endif |
207 | 207 | ||
208 | /* | ||
209 | * X86_32 CPUs don't save ss and esp if the CPU is already in kernel mode | ||
210 | * when it traps. The previous stack will be directly underneath the saved | ||
211 | * registers, and 'sp/ss' won't even have been saved. Thus the '®s->sp'. | ||
212 | * | ||
213 | * This is valid only for kernel mode traps. | ||
214 | */ | ||
215 | static inline unsigned long kernel_stack_pointer(struct pt_regs *regs) | ||
216 | { | ||
217 | #ifdef CONFIG_X86_32 | 208 | #ifdef CONFIG_X86_32 |
218 | return (unsigned long)(®s->sp); | 209 | extern unsigned long kernel_stack_pointer(struct pt_regs *regs); |
219 | #else | 210 | #else |
211 | static inline unsigned long kernel_stack_pointer(struct pt_regs *regs) | ||
212 | { | ||
220 | return regs->sp; | 213 | return regs->sp; |
221 | #endif | ||
222 | } | 214 | } |
215 | #endif | ||
223 | 216 | ||
224 | #define GET_IP(regs) ((regs)->ip) | 217 | #define GET_IP(regs) ((regs)->ip) |
225 | #define GET_FP(regs) ((regs)->bp) | 218 | #define GET_FP(regs) ((regs)->bp) |
diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c index b00b33a18390..2484e331a64d 100644 --- a/arch/x86/kernel/ptrace.c +++ b/arch/x86/kernel/ptrace.c | |||
@@ -166,6 +166,34 @@ static inline bool invalid_selector(u16 value) | |||
166 | 166 | ||
167 | #define FLAG_MASK FLAG_MASK_32 | 167 | #define FLAG_MASK FLAG_MASK_32 |
168 | 168 | ||
169 | /* | ||
170 | * X86_32 CPUs don't save ss and esp if the CPU is already in kernel mode | ||
171 | * when it traps. The previous stack will be directly underneath the saved | ||
172 | * registers, and 'sp/ss' won't even have been saved. Thus the '®s->sp'. | ||
173 | * | ||
174 | * Now, if the stack is empty, '®s->sp' is out of range. In this | ||
175 | * case we try to take the previous stack. To always return a non-null | ||
176 | * stack pointer we fall back to regs as stack if no previous stack | ||
177 | * exists. | ||
178 | * | ||
179 | * This is valid only for kernel mode traps. | ||
180 | */ | ||
181 | unsigned long kernel_stack_pointer(struct pt_regs *regs) | ||
182 | { | ||
183 | unsigned long context = (unsigned long)regs & ~(THREAD_SIZE - 1); | ||
184 | unsigned long sp = (unsigned long)®s->sp; | ||
185 | struct thread_info *tinfo; | ||
186 | |||
187 | if (context == (sp & ~(THREAD_SIZE - 1))) | ||
188 | return sp; | ||
189 | |||
190 | tinfo = (struct thread_info *)context; | ||
191 | if (tinfo->previous_esp) | ||
192 | return tinfo->previous_esp; | ||
193 | |||
194 | return (unsigned long)regs; | ||
195 | } | ||
196 | |||
169 | static unsigned long *pt_regs_access(struct pt_regs *regs, unsigned long regno) | 197 | static unsigned long *pt_regs_access(struct pt_regs *regs, unsigned long regno) |
170 | { | 198 | { |
171 | BUILD_BUG_ON(offsetof(struct pt_regs, bx) != 0); | 199 | BUILD_BUG_ON(offsetof(struct pt_regs, bx) != 0); |