diff options
Diffstat (limited to 'arch/x86/kernel/ptrace.c')
-rw-r--r-- | arch/x86/kernel/ptrace.c | 68 |
1 files changed, 33 insertions, 35 deletions
diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c index 04d182a7cfdb..7079ddaf0731 100644 --- a/arch/x86/kernel/ptrace.c +++ b/arch/x86/kernel/ptrace.c | |||
@@ -555,7 +555,9 @@ static int genregs_set(struct task_struct *target, | |||
555 | return ret; | 555 | return ret; |
556 | } | 556 | } |
557 | 557 | ||
558 | static void ptrace_triggered(struct perf_event *bp, void *data) | 558 | static void ptrace_triggered(struct perf_event *bp, int nmi, |
559 | struct perf_sample_data *data, | ||
560 | struct pt_regs *regs) | ||
559 | { | 561 | { |
560 | int i; | 562 | int i; |
561 | struct thread_struct *thread = &(current->thread); | 563 | struct thread_struct *thread = &(current->thread); |
@@ -593,13 +595,13 @@ static unsigned long ptrace_get_dr7(struct perf_event *bp[]) | |||
593 | return dr7; | 595 | return dr7; |
594 | } | 596 | } |
595 | 597 | ||
596 | static struct perf_event * | 598 | static int |
597 | ptrace_modify_breakpoint(struct perf_event *bp, int len, int type, | 599 | ptrace_modify_breakpoint(struct perf_event *bp, int len, int type, |
598 | struct task_struct *tsk, int disabled) | 600 | struct task_struct *tsk, int disabled) |
599 | { | 601 | { |
600 | int err; | 602 | int err; |
601 | int gen_len, gen_type; | 603 | int gen_len, gen_type; |
602 | DEFINE_BREAKPOINT_ATTR(attr); | 604 | struct perf_event_attr attr; |
603 | 605 | ||
604 | /* | 606 | /* |
605 | * We shoud have at least an inactive breakpoint at this | 607 | * We shoud have at least an inactive breakpoint at this |
@@ -607,18 +609,18 @@ ptrace_modify_breakpoint(struct perf_event *bp, int len, int type, | |||
607 | * written the address register first | 609 | * written the address register first |
608 | */ | 610 | */ |
609 | if (!bp) | 611 | if (!bp) |
610 | return ERR_PTR(-EINVAL); | 612 | return -EINVAL; |
611 | 613 | ||
612 | err = arch_bp_generic_fields(len, type, &gen_len, &gen_type); | 614 | err = arch_bp_generic_fields(len, type, &gen_len, &gen_type); |
613 | if (err) | 615 | if (err) |
614 | return ERR_PTR(err); | 616 | return err; |
615 | 617 | ||
616 | attr = bp->attr; | 618 | attr = bp->attr; |
617 | attr.bp_len = gen_len; | 619 | attr.bp_len = gen_len; |
618 | attr.bp_type = gen_type; | 620 | attr.bp_type = gen_type; |
619 | attr.disabled = disabled; | 621 | attr.disabled = disabled; |
620 | 622 | ||
621 | return modify_user_hw_breakpoint(bp, &attr, bp->callback, tsk); | 623 | return modify_user_hw_breakpoint(bp, &attr); |
622 | } | 624 | } |
623 | 625 | ||
624 | /* | 626 | /* |
@@ -656,28 +658,17 @@ restore: | |||
656 | if (!second_pass) | 658 | if (!second_pass) |
657 | continue; | 659 | continue; |
658 | 660 | ||
659 | thread->ptrace_bps[i] = NULL; | 661 | rc = ptrace_modify_breakpoint(bp, len, type, |
660 | bp = ptrace_modify_breakpoint(bp, len, type, | ||
661 | tsk, 1); | 662 | tsk, 1); |
662 | if (IS_ERR(bp)) { | 663 | if (rc) |
663 | rc = PTR_ERR(bp); | ||
664 | thread->ptrace_bps[i] = NULL; | ||
665 | break; | 664 | break; |
666 | } | ||
667 | thread->ptrace_bps[i] = bp; | ||
668 | } | 665 | } |
669 | continue; | 666 | continue; |
670 | } | 667 | } |
671 | 668 | ||
672 | bp = ptrace_modify_breakpoint(bp, len, type, tsk, 0); | 669 | rc = ptrace_modify_breakpoint(bp, len, type, tsk, 0); |
673 | 670 | if (rc) | |
674 | /* Incorrect bp, or we have a bug in bp API */ | ||
675 | if (IS_ERR(bp)) { | ||
676 | rc = PTR_ERR(bp); | ||
677 | thread->ptrace_bps[i] = NULL; | ||
678 | break; | 671 | break; |
679 | } | ||
680 | thread->ptrace_bps[i] = bp; | ||
681 | } | 672 | } |
682 | /* | 673 | /* |
683 | * Make a second pass to free the remaining unused breakpoints | 674 | * Make a second pass to free the remaining unused breakpoints |
@@ -721,9 +712,10 @@ static int ptrace_set_breakpoint_addr(struct task_struct *tsk, int nr, | |||
721 | { | 712 | { |
722 | struct perf_event *bp; | 713 | struct perf_event *bp; |
723 | struct thread_struct *t = &tsk->thread; | 714 | struct thread_struct *t = &tsk->thread; |
724 | DEFINE_BREAKPOINT_ATTR(attr); | 715 | struct perf_event_attr attr; |
725 | 716 | ||
726 | if (!t->ptrace_bps[nr]) { | 717 | if (!t->ptrace_bps[nr]) { |
718 | hw_breakpoint_init(&attr); | ||
727 | /* | 719 | /* |
728 | * Put stub len and type to register (reserve) an inactive but | 720 | * Put stub len and type to register (reserve) an inactive but |
729 | * correct bp | 721 | * correct bp |
@@ -734,26 +726,32 @@ static int ptrace_set_breakpoint_addr(struct task_struct *tsk, int nr, | |||
734 | attr.disabled = 1; | 726 | attr.disabled = 1; |
735 | 727 | ||
736 | bp = register_user_hw_breakpoint(&attr, ptrace_triggered, tsk); | 728 | bp = register_user_hw_breakpoint(&attr, ptrace_triggered, tsk); |
729 | |||
730 | /* | ||
731 | * CHECKME: the previous code returned -EIO if the addr wasn't | ||
732 | * a valid task virtual addr. The new one will return -EINVAL in | ||
733 | * this case. | ||
734 | * -EINVAL may be what we want for in-kernel breakpoints users, | ||
735 | * but -EIO looks better for ptrace, since we refuse a register | ||
736 | * writing for the user. And anyway this is the previous | ||
737 | * behaviour. | ||
738 | */ | ||
739 | if (IS_ERR(bp)) | ||
740 | return PTR_ERR(bp); | ||
741 | |||
742 | t->ptrace_bps[nr] = bp; | ||
737 | } else { | 743 | } else { |
744 | int err; | ||
745 | |||
738 | bp = t->ptrace_bps[nr]; | 746 | bp = t->ptrace_bps[nr]; |
739 | t->ptrace_bps[nr] = NULL; | ||
740 | 747 | ||
741 | attr = bp->attr; | 748 | attr = bp->attr; |
742 | attr.bp_addr = addr; | 749 | attr.bp_addr = addr; |
743 | bp = modify_user_hw_breakpoint(bp, &attr, bp->callback, tsk); | 750 | err = modify_user_hw_breakpoint(bp, &attr); |
751 | if (err) | ||
752 | return err; | ||
744 | } | 753 | } |
745 | /* | ||
746 | * CHECKME: the previous code returned -EIO if the addr wasn't a | ||
747 | * valid task virtual addr. The new one will return -EINVAL in this | ||
748 | * case. | ||
749 | * -EINVAL may be what we want for in-kernel breakpoints users, but | ||
750 | * -EIO looks better for ptrace, since we refuse a register writing | ||
751 | * for the user. And anyway this is the previous behaviour. | ||
752 | */ | ||
753 | if (IS_ERR(bp)) | ||
754 | return PTR_ERR(bp); | ||
755 | 754 | ||
756 | t->ptrace_bps[nr] = bp; | ||
757 | 755 | ||
758 | return 0; | 756 | return 0; |
759 | } | 757 | } |