diff options
Diffstat (limited to 'arch/x86/kernel/ptrace.c')
-rw-r--r-- | arch/x86/kernel/ptrace.c | 74 |
1 files changed, 41 insertions, 33 deletions
diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c index 75e0cd847bd6..2941b32ea666 100644 --- a/arch/x86/kernel/ptrace.c +++ b/arch/x86/kernel/ptrace.c | |||
@@ -593,6 +593,34 @@ static unsigned long ptrace_get_dr7(struct perf_event *bp[]) | |||
593 | return dr7; | 593 | return dr7; |
594 | } | 594 | } |
595 | 595 | ||
596 | static struct perf_event * | ||
597 | ptrace_modify_breakpoint(struct perf_event *bp, int len, int type, | ||
598 | struct task_struct *tsk) | ||
599 | { | ||
600 | int err; | ||
601 | int gen_len, gen_type; | ||
602 | DEFINE_BREAKPOINT_ATTR(attr); | ||
603 | |||
604 | /* | ||
605 | * We shoud have at least an inactive breakpoint at this | ||
606 | * slot. It means the user is writing dr7 without having | ||
607 | * written the address register first | ||
608 | */ | ||
609 | if (!bp) | ||
610 | return ERR_PTR(-EINVAL); | ||
611 | |||
612 | err = arch_bp_generic_fields(len, type, &gen_len, &gen_type); | ||
613 | if (err) | ||
614 | return ERR_PTR(err); | ||
615 | |||
616 | attr = bp->attr; | ||
617 | attr.bp_len = gen_len; | ||
618 | attr.bp_type = gen_type; | ||
619 | attr.disabled = 0; | ||
620 | |||
621 | return modify_user_hw_breakpoint(bp, &attr, bp->callback, tsk); | ||
622 | } | ||
623 | |||
596 | /* | 624 | /* |
597 | * Handle ptrace writes to debug register 7. | 625 | * Handle ptrace writes to debug register 7. |
598 | */ | 626 | */ |
@@ -603,7 +631,6 @@ static int ptrace_write_dr7(struct task_struct *tsk, unsigned long data) | |||
603 | int i, orig_ret = 0, rc = 0; | 631 | int i, orig_ret = 0, rc = 0; |
604 | int enabled, second_pass = 0; | 632 | int enabled, second_pass = 0; |
605 | unsigned len, type; | 633 | unsigned len, type; |
606 | int gen_len, gen_type; | ||
607 | struct perf_event *bp; | 634 | struct perf_event *bp; |
608 | 635 | ||
609 | data &= ~DR_CONTROL_RESERVED; | 636 | data &= ~DR_CONTROL_RESERVED; |
@@ -634,33 +661,12 @@ restore: | |||
634 | continue; | 661 | continue; |
635 | } | 662 | } |
636 | 663 | ||
637 | /* | 664 | bp = ptrace_modify_breakpoint(bp, len, type, tsk); |
638 | * We shoud have at least an inactive breakpoint at this | ||
639 | * slot. It means the user is writing dr7 without having | ||
640 | * written the address register first | ||
641 | */ | ||
642 | if (!bp) { | ||
643 | rc = -EINVAL; | ||
644 | break; | ||
645 | } | ||
646 | |||
647 | rc = arch_bp_generic_fields(len, type, &gen_len, &gen_type); | ||
648 | if (rc) | ||
649 | break; | ||
650 | |||
651 | /* | ||
652 | * This is a temporary thing as bp is unregistered/registered | ||
653 | * to simulate modification | ||
654 | */ | ||
655 | bp = modify_user_hw_breakpoint(bp, bp->attr.bp_addr, gen_len, | ||
656 | gen_type, bp->callback, | ||
657 | tsk, true); | ||
658 | thread->ptrace_bps[i] = NULL; | ||
659 | 665 | ||
660 | /* Incorrect bp, or we have a bug in bp API */ | 666 | /* Incorrect bp, or we have a bug in bp API */ |
661 | if (IS_ERR(bp)) { | 667 | if (IS_ERR(bp)) { |
662 | rc = PTR_ERR(bp); | 668 | rc = PTR_ERR(bp); |
663 | bp = NULL; | 669 | thread->ptrace_bps[i] = NULL; |
664 | break; | 670 | break; |
665 | } | 671 | } |
666 | thread->ptrace_bps[i] = bp; | 672 | thread->ptrace_bps[i] = bp; |
@@ -707,24 +713,26 @@ static int ptrace_set_breakpoint_addr(struct task_struct *tsk, int nr, | |||
707 | { | 713 | { |
708 | struct perf_event *bp; | 714 | struct perf_event *bp; |
709 | struct thread_struct *t = &tsk->thread; | 715 | struct thread_struct *t = &tsk->thread; |
716 | DEFINE_BREAKPOINT_ATTR(attr); | ||
710 | 717 | ||
711 | if (!t->ptrace_bps[nr]) { | 718 | if (!t->ptrace_bps[nr]) { |
712 | /* | 719 | /* |
713 | * Put stub len and type to register (reserve) an inactive but | 720 | * Put stub len and type to register (reserve) an inactive but |
714 | * correct bp | 721 | * correct bp |
715 | */ | 722 | */ |
716 | bp = register_user_hw_breakpoint(addr, HW_BREAKPOINT_LEN_1, | 723 | attr.bp_addr = addr; |
717 | HW_BREAKPOINT_W, | 724 | attr.bp_len = HW_BREAKPOINT_LEN_1; |
718 | ptrace_triggered, tsk, | 725 | attr.bp_type = HW_BREAKPOINT_W; |
719 | false); | 726 | attr.disabled = 1; |
727 | |||
728 | bp = register_user_hw_breakpoint(&attr, ptrace_triggered, tsk); | ||
720 | } else { | 729 | } else { |
721 | bp = t->ptrace_bps[nr]; | 730 | bp = t->ptrace_bps[nr]; |
722 | t->ptrace_bps[nr] = NULL; | 731 | t->ptrace_bps[nr] = NULL; |
723 | bp = modify_user_hw_breakpoint(bp, addr, bp->attr.bp_len, | 732 | |
724 | bp->attr.bp_type, | 733 | attr = bp->attr; |
725 | bp->callback, | 734 | attr.bp_addr = addr; |
726 | tsk, | 735 | bp = modify_user_hw_breakpoint(bp, &attr, bp->callback, tsk); |
727 | bp->attr.disabled); | ||
728 | } | 736 | } |
729 | /* | 737 | /* |
730 | * CHECKME: the previous code returned -EIO if the addr wasn't a | 738 | * CHECKME: the previous code returned -EIO if the addr wasn't a |