aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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