aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFrederic Weisbecker <fweisbec@gmail.com>2010-04-18 12:11:53 -0400
committerFrederic Weisbecker <fweisbec@gmail.com>2010-04-30 22:32:10 -0400
commitb2812d031dea86926e9c10f7714af33ac2f6b43d (patch)
treea48b18b49fa064e89b7b03e057e46e5a294a8053
parent87e9b2024659c614a876ce359a57e98a47b5ef37 (diff)
hw-breakpoints: Change/Enforce some breakpoints policies
The current policies of breakpoints in x86 and SH are the following: - task bound breakpoints can only break on userspace addresses - cpu wide breakpoints can only break on kernel addresses The former rule prevents ptrace breakpoints to be set to trigger on kernel addresses, which is good. But as a side effect, we can't breakpoint on kernel addresses for task bound breakpoints. The latter rule simply makes no sense, there is no reason why we can't set breakpoints on userspace while performing cpu bound profiles. We want the following new policies: - task bound breakpoint can set userspace address breakpoints, with no particular privilege required. - task bound breakpoints can set kernelspace address breakpoints but must be privileged to do that. - cpu bound breakpoints can do what they want as they are privileged already. To implement these new policies, this patch checks if we are dealing with a kernel address breakpoint, if so and if the exclude_kernel parameter is set, we tell the user that the breakpoint is invalid, which makes a good generic ptrace protection. If we don't have exclude_kernel, ensure the user has the right privileges as kernel breakpoints are quite sensitive (risk of trap recursion attacks and global performance impacts). [ Paul Mundt: keep addr space check for sh signal delivery and fix double function declaration] Signed-off-by: Frederic Weisbecker <fweisbec@gmail.com> Cc: Will Deacon <will.deacon@arm.com> Cc: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com> Cc: K. Prasad <prasad@linux.vnet.ibm.com> Cc: Paul Mundt <lethal@linux-sh.org> Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org> Cc: Paul Mackerras <paulus@samba.org> Cc: Jason Wessel <jason.wessel@windriver.com> Cc: Ingo Molnar <mingo@elte.hu> Signed-off-by: Paul Mundt <lethal@linux-sh.org>
-rw-r--r--arch/sh/include/asm/hw_breakpoint.h5
-rw-r--r--arch/sh/kernel/hw_breakpoint.c34
-rw-r--r--arch/x86/include/asm/hw_breakpoint.h5
-rw-r--r--arch/x86/kernel/hw_breakpoint.c41
-rw-r--r--kernel/hw_breakpoint.c26
5 files changed, 41 insertions, 70 deletions
diff --git a/arch/sh/include/asm/hw_breakpoint.h b/arch/sh/include/asm/hw_breakpoint.h
index 965dd780d51b..382bad937dcc 100644
--- a/arch/sh/include/asm/hw_breakpoint.h
+++ b/arch/sh/include/asm/hw_breakpoint.h
@@ -47,9 +47,8 @@ struct pmu;
47#define HBP_NUM 2 47#define HBP_NUM 2
48 48
49/* arch/sh/kernel/hw_breakpoint.c */ 49/* arch/sh/kernel/hw_breakpoint.c */
50extern int arch_check_va_in_userspace(unsigned long va, u16 hbp_len); 50extern int arch_check_bp_in_kernelspace(struct perf_event *bp);
51extern int arch_validate_hwbkpt_settings(struct perf_event *bp, 51extern int arch_validate_hwbkpt_settings(struct perf_event *bp);
52 struct task_struct *tsk);
53extern int hw_breakpoint_exceptions_notify(struct notifier_block *unused, 52extern int hw_breakpoint_exceptions_notify(struct notifier_block *unused,
54 unsigned long val, void *data); 53 unsigned long val, void *data);
55 54
diff --git a/arch/sh/kernel/hw_breakpoint.c b/arch/sh/kernel/hw_breakpoint.c
index 675eea7785d9..1f2cf6229862 100644
--- a/arch/sh/kernel/hw_breakpoint.c
+++ b/arch/sh/kernel/hw_breakpoint.c
@@ -120,25 +120,16 @@ static int get_hbp_len(u16 hbp_len)
120} 120}
121 121
122/* 122/*
123 * Check for virtual address in user space.
124 */
125int arch_check_va_in_userspace(unsigned long va, u16 hbp_len)
126{
127 unsigned int len;
128
129 len = get_hbp_len(hbp_len);
130
131 return (va <= TASK_SIZE - len);
132}
133
134/*
135 * Check for virtual address in kernel space. 123 * Check for virtual address in kernel space.
136 */ 124 */
137static int arch_check_va_in_kernelspace(unsigned long va, u8 hbp_len) 125int arch_check_bp_in_kernelspace(struct perf_event *bp)
138{ 126{
139 unsigned int len; 127 unsigned int len;
128 unsigned long va;
129 struct arch_hw_breakpoint *info = counter_arch_bp(bp);
140 130
141 len = get_hbp_len(hbp_len); 131 va = info->address;
132 len = get_hbp_len(info->len);
142 133
143 return (va >= TASK_SIZE) && ((va + len - 1) >= TASK_SIZE); 134 return (va >= TASK_SIZE) && ((va + len - 1) >= TASK_SIZE);
144} 135}
@@ -226,8 +217,7 @@ static int arch_build_bp_info(struct perf_event *bp)
226/* 217/*
227 * Validate the arch-specific HW Breakpoint register settings 218 * Validate the arch-specific HW Breakpoint register settings
228 */ 219 */
229int arch_validate_hwbkpt_settings(struct perf_event *bp, 220int arch_validate_hwbkpt_settings(struct perf_event *bp)
230 struct task_struct *tsk)
231{ 221{
232 struct arch_hw_breakpoint *info = counter_arch_bp(bp); 222 struct arch_hw_breakpoint *info = counter_arch_bp(bp);
233 unsigned int align; 223 unsigned int align;
@@ -270,15 +260,6 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp,
270 if (info->address & align) 260 if (info->address & align)
271 return -EINVAL; 261 return -EINVAL;
272 262
273 /* Check that the virtual address is in the proper range */
274 if (tsk) {
275 if (!arch_check_va_in_userspace(info->address, info->len))
276 return -EFAULT;
277 } else {
278 if (!arch_check_va_in_kernelspace(info->address, info->len))
279 return -EFAULT;
280 }
281
282 return 0; 263 return 0;
283} 264}
284 265
@@ -363,8 +344,7 @@ static int __kprobes hw_breakpoint_handler(struct die_args *args)
363 perf_bp_event(bp, args->regs); 344 perf_bp_event(bp, args->regs);
364 345
365 /* Deliver the signal to userspace */ 346 /* Deliver the signal to userspace */
366 if (arch_check_va_in_userspace(bp->attr.bp_addr, 347 if (!arch_check_bp_in_kernelspace(bp)) {
367 bp->attr.bp_len)) {
368 siginfo_t info; 348 siginfo_t info;
369 349
370 info.si_signo = args->signr; 350 info.si_signo = args->signr;
diff --git a/arch/x86/include/asm/hw_breakpoint.h b/arch/x86/include/asm/hw_breakpoint.h
index 2a1bd8f4f23a..c77a5a6fab9d 100644
--- a/arch/x86/include/asm/hw_breakpoint.h
+++ b/arch/x86/include/asm/hw_breakpoint.h
@@ -44,9 +44,8 @@ struct arch_hw_breakpoint {
44struct perf_event; 44struct perf_event;
45struct pmu; 45struct pmu;
46 46
47extern int arch_check_va_in_userspace(unsigned long va, u8 hbp_len); 47extern int arch_check_bp_in_kernelspace(struct perf_event *bp);
48extern int arch_validate_hwbkpt_settings(struct perf_event *bp, 48extern int arch_validate_hwbkpt_settings(struct perf_event *bp);
49 struct task_struct *tsk);
50extern int hw_breakpoint_exceptions_notify(struct notifier_block *unused, 49extern int hw_breakpoint_exceptions_notify(struct notifier_block *unused,
51 unsigned long val, void *data); 50 unsigned long val, void *data);
52 51
diff --git a/arch/x86/kernel/hw_breakpoint.c b/arch/x86/kernel/hw_breakpoint.c
index d6cc065f519f..a8f1b803d2fd 100644
--- a/arch/x86/kernel/hw_breakpoint.c
+++ b/arch/x86/kernel/hw_breakpoint.c
@@ -189,25 +189,16 @@ static int get_hbp_len(u8 hbp_len)
189} 189}
190 190
191/* 191/*
192 * Check for virtual address in user space.
193 */
194int arch_check_va_in_userspace(unsigned long va, u8 hbp_len)
195{
196 unsigned int len;
197
198 len = get_hbp_len(hbp_len);
199
200 return (va <= TASK_SIZE - len);
201}
202
203/*
204 * Check for virtual address in kernel space. 192 * Check for virtual address in kernel space.
205 */ 193 */
206static int arch_check_va_in_kernelspace(unsigned long va, u8 hbp_len) 194int arch_check_bp_in_kernelspace(struct perf_event *bp)
207{ 195{
208 unsigned int len; 196 unsigned int len;
197 unsigned long va;
198 struct arch_hw_breakpoint *info = counter_arch_bp(bp);
209 199
210 len = get_hbp_len(hbp_len); 200 va = info->address;
201 len = get_hbp_len(info->len);
211 202
212 return (va >= TASK_SIZE) && ((va + len - 1) >= TASK_SIZE); 203 return (va >= TASK_SIZE) && ((va + len - 1) >= TASK_SIZE);
213} 204}
@@ -300,8 +291,7 @@ static int arch_build_bp_info(struct perf_event *bp)
300/* 291/*
301 * Validate the arch-specific HW Breakpoint register settings 292 * Validate the arch-specific HW Breakpoint register settings
302 */ 293 */
303int arch_validate_hwbkpt_settings(struct perf_event *bp, 294int arch_validate_hwbkpt_settings(struct perf_event *bp)
304 struct task_struct *tsk)
305{ 295{
306 struct arch_hw_breakpoint *info = counter_arch_bp(bp); 296 struct arch_hw_breakpoint *info = counter_arch_bp(bp);
307 unsigned int align; 297 unsigned int align;
@@ -314,16 +304,6 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp,
314 304
315 ret = -EINVAL; 305 ret = -EINVAL;
316 306
317 if (info->type == X86_BREAKPOINT_EXECUTE)
318 /*
319 * Ptrace-refactoring code
320 * For now, we'll allow instruction breakpoint only for user-space
321 * addresses
322 */
323 if ((!arch_check_va_in_userspace(info->address, info->len)) &&
324 info->len != X86_BREAKPOINT_EXECUTE)
325 return ret;
326
327 switch (info->len) { 307 switch (info->len) {
328 case X86_BREAKPOINT_LEN_1: 308 case X86_BREAKPOINT_LEN_1:
329 align = 0; 309 align = 0;
@@ -350,15 +330,6 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp,
350 if (info->address & align) 330 if (info->address & align)
351 return -EINVAL; 331 return -EINVAL;
352 332
353 /* Check that the virtual address is in the proper range */
354 if (tsk) {
355 if (!arch_check_va_in_userspace(info->address, info->len))
356 return -EFAULT;
357 } else {
358 if (!arch_check_va_in_kernelspace(info->address, info->len))
359 return -EFAULT;
360 }
361
362 return 0; 333 return 0;
363} 334}
364 335
diff --git a/kernel/hw_breakpoint.c b/kernel/hw_breakpoint.c
index 9ed9ae3a48b3..89e8a050c43a 100644
--- a/kernel/hw_breakpoint.c
+++ b/kernel/hw_breakpoint.c
@@ -308,6 +308,28 @@ int dbg_release_bp_slot(struct perf_event *bp)
308 return 0; 308 return 0;
309} 309}
310 310
311static int validate_hw_breakpoint(struct perf_event *bp)
312{
313 int ret;
314
315 ret = arch_validate_hwbkpt_settings(bp);
316 if (ret)
317 return ret;
318
319 if (arch_check_bp_in_kernelspace(bp)) {
320 if (bp->attr.exclude_kernel)
321 return -EINVAL;
322 /*
323 * Don't let unprivileged users set a breakpoint in the trap
324 * path to avoid trap recursion attacks.
325 */
326 if (!capable(CAP_SYS_ADMIN))
327 return -EPERM;
328 }
329
330 return 0;
331}
332
311int register_perf_hw_breakpoint(struct perf_event *bp) 333int register_perf_hw_breakpoint(struct perf_event *bp)
312{ 334{
313 int ret; 335 int ret;
@@ -316,7 +338,7 @@ int register_perf_hw_breakpoint(struct perf_event *bp)
316 if (ret) 338 if (ret)
317 return ret; 339 return ret;
318 340
319 ret = arch_validate_hwbkpt_settings(bp, bp->ctx->task); 341 ret = validate_hw_breakpoint(bp);
320 342
321 /* if arch_validate_hwbkpt_settings() fails then release bp slot */ 343 /* if arch_validate_hwbkpt_settings() fails then release bp slot */
322 if (ret) 344 if (ret)
@@ -363,7 +385,7 @@ int modify_user_hw_breakpoint(struct perf_event *bp, struct perf_event_attr *att
363 if (attr->disabled) 385 if (attr->disabled)
364 goto end; 386 goto end;
365 387
366 err = arch_validate_hwbkpt_settings(bp, bp->ctx->task); 388 err = validate_hw_breakpoint(bp);
367 if (!err) 389 if (!err)
368 perf_event_enable(bp); 390 perf_event_enable(bp);
369 391