diff options
Diffstat (limited to 'arch/x86/kernel/ptrace.c')
-rw-r--r-- | arch/x86/kernel/ptrace.c | 57 |
1 files changed, 26 insertions, 31 deletions
diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c index b361d28061d0..7079ddaf0731 100644 --- a/arch/x86/kernel/ptrace.c +++ b/arch/x86/kernel/ptrace.c | |||
@@ -595,7 +595,7 @@ static unsigned long ptrace_get_dr7(struct perf_event *bp[]) | |||
595 | return dr7; | 595 | return dr7; |
596 | } | 596 | } |
597 | 597 | ||
598 | static struct perf_event * | 598 | static int |
599 | ptrace_modify_breakpoint(struct perf_event *bp, int len, int type, | 599 | ptrace_modify_breakpoint(struct perf_event *bp, int len, int type, |
600 | struct task_struct *tsk, int disabled) | 600 | struct task_struct *tsk, int disabled) |
601 | { | 601 | { |
@@ -609,11 +609,11 @@ ptrace_modify_breakpoint(struct perf_event *bp, int len, int type, | |||
609 | * written the address register first | 609 | * written the address register first |
610 | */ | 610 | */ |
611 | if (!bp) | 611 | if (!bp) |
612 | return ERR_PTR(-EINVAL); | 612 | return -EINVAL; |
613 | 613 | ||
614 | err = arch_bp_generic_fields(len, type, &gen_len, &gen_type); | 614 | err = arch_bp_generic_fields(len, type, &gen_len, &gen_type); |
615 | if (err) | 615 | if (err) |
616 | return ERR_PTR(err); | 616 | return err; |
617 | 617 | ||
618 | attr = bp->attr; | 618 | attr = bp->attr; |
619 | attr.bp_len = gen_len; | 619 | attr.bp_len = gen_len; |
@@ -658,28 +658,17 @@ restore: | |||
658 | if (!second_pass) | 658 | if (!second_pass) |
659 | continue; | 659 | continue; |
660 | 660 | ||
661 | thread->ptrace_bps[i] = NULL; | 661 | rc = ptrace_modify_breakpoint(bp, len, type, |
662 | bp = ptrace_modify_breakpoint(bp, len, type, | ||
663 | tsk, 1); | 662 | tsk, 1); |
664 | if (IS_ERR(bp)) { | 663 | if (rc) |
665 | rc = PTR_ERR(bp); | ||
666 | thread->ptrace_bps[i] = NULL; | ||
667 | break; | 664 | break; |
668 | } | ||
669 | thread->ptrace_bps[i] = bp; | ||
670 | } | 665 | } |
671 | continue; | 666 | continue; |
672 | } | 667 | } |
673 | 668 | ||
674 | bp = ptrace_modify_breakpoint(bp, len, type, tsk, 0); | 669 | rc = ptrace_modify_breakpoint(bp, len, type, tsk, 0); |
675 | 670 | if (rc) | |
676 | /* Incorrect bp, or we have a bug in bp API */ | ||
677 | if (IS_ERR(bp)) { | ||
678 | rc = PTR_ERR(bp); | ||
679 | thread->ptrace_bps[i] = NULL; | ||
680 | break; | 671 | break; |
681 | } | ||
682 | thread->ptrace_bps[i] = bp; | ||
683 | } | 672 | } |
684 | /* | 673 | /* |
685 | * Make a second pass to free the remaining unused breakpoints | 674 | * Make a second pass to free the remaining unused breakpoints |
@@ -737,26 +726,32 @@ static int ptrace_set_breakpoint_addr(struct task_struct *tsk, int nr, | |||
737 | attr.disabled = 1; | 726 | attr.disabled = 1; |
738 | 727 | ||
739 | 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; | ||
740 | } else { | 743 | } else { |
744 | int err; | ||
745 | |||
741 | bp = t->ptrace_bps[nr]; | 746 | bp = t->ptrace_bps[nr]; |
742 | t->ptrace_bps[nr] = NULL; | ||
743 | 747 | ||
744 | attr = bp->attr; | 748 | attr = bp->attr; |
745 | attr.bp_addr = addr; | 749 | attr.bp_addr = addr; |
746 | bp = modify_user_hw_breakpoint(bp, &attr); | 750 | err = modify_user_hw_breakpoint(bp, &attr); |
751 | if (err) | ||
752 | return err; | ||
747 | } | 753 | } |
748 | /* | ||
749 | * CHECKME: the previous code returned -EIO if the addr wasn't a | ||
750 | * valid task virtual addr. The new one will return -EINVAL in this | ||
751 | * case. | ||
752 | * -EINVAL may be what we want for in-kernel breakpoints users, but | ||
753 | * -EIO looks better for ptrace, since we refuse a register writing | ||
754 | * for the user. And anyway this is the previous behaviour. | ||
755 | */ | ||
756 | if (IS_ERR(bp)) | ||
757 | return PTR_ERR(bp); | ||
758 | 754 | ||
759 | t->ptrace_bps[nr] = bp; | ||
760 | 755 | ||
761 | return 0; | 756 | return 0; |
762 | } | 757 | } |