diff options
-rw-r--r-- | arch/sh/include/asm/hw_breakpoint.h | 5 | ||||
-rw-r--r-- | arch/sh/kernel/hw_breakpoint.c | 34 | ||||
-rw-r--r-- | arch/x86/include/asm/hw_breakpoint.h | 5 | ||||
-rw-r--r-- | arch/x86/kernel/hw_breakpoint.c | 41 | ||||
-rw-r--r-- | kernel/hw_breakpoint.c | 26 |
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 */ |
50 | extern int arch_check_va_in_userspace(unsigned long va, u16 hbp_len); | 50 | extern int arch_check_bp_in_kernelspace(struct perf_event *bp); |
51 | extern int arch_validate_hwbkpt_settings(struct perf_event *bp, | 51 | extern int arch_validate_hwbkpt_settings(struct perf_event *bp); |
52 | struct task_struct *tsk); | ||
53 | extern int hw_breakpoint_exceptions_notify(struct notifier_block *unused, | 52 | extern 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 | */ | ||
125 | int 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 | */ |
137 | static int arch_check_va_in_kernelspace(unsigned long va, u8 hbp_len) | 125 | int 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 | */ |
229 | int arch_validate_hwbkpt_settings(struct perf_event *bp, | 220 | int 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 { | |||
44 | struct perf_event; | 44 | struct perf_event; |
45 | struct pmu; | 45 | struct pmu; |
46 | 46 | ||
47 | extern int arch_check_va_in_userspace(unsigned long va, u8 hbp_len); | 47 | extern int arch_check_bp_in_kernelspace(struct perf_event *bp); |
48 | extern int arch_validate_hwbkpt_settings(struct perf_event *bp, | 48 | extern int arch_validate_hwbkpt_settings(struct perf_event *bp); |
49 | struct task_struct *tsk); | ||
50 | extern int hw_breakpoint_exceptions_notify(struct notifier_block *unused, | 49 | extern 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 | */ | ||
194 | int 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 | */ |
206 | static int arch_check_va_in_kernelspace(unsigned long va, u8 hbp_len) | 194 | int 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 | */ |
303 | int arch_validate_hwbkpt_settings(struct perf_event *bp, | 294 | int 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 | ||
311 | static 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 | |||
311 | int register_perf_hw_breakpoint(struct perf_event *bp) | 333 | int 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 | ||