diff options
Diffstat (limited to 'arch/x86/kernel/ptrace.c')
-rw-r--r-- | arch/x86/kernel/ptrace.c | 93 |
1 files changed, 73 insertions, 20 deletions
diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c index 45e9855da2d2..0a5df5f82fb9 100644 --- a/arch/x86/kernel/ptrace.c +++ b/arch/x86/kernel/ptrace.c | |||
@@ -650,6 +650,24 @@ static int ptrace_bts_drain(struct task_struct *child, | |||
650 | return drained; | 650 | return drained; |
651 | } | 651 | } |
652 | 652 | ||
653 | static int ptrace_bts_allocate_buffer(struct task_struct *child, size_t size) | ||
654 | { | ||
655 | child->bts_buffer = alloc_locked_buffer(size); | ||
656 | if (!child->bts_buffer) | ||
657 | return -ENOMEM; | ||
658 | |||
659 | child->bts_size = size; | ||
660 | |||
661 | return 0; | ||
662 | } | ||
663 | |||
664 | static void ptrace_bts_free_buffer(struct task_struct *child) | ||
665 | { | ||
666 | free_locked_buffer(child->bts_buffer, child->bts_size); | ||
667 | child->bts_buffer = NULL; | ||
668 | child->bts_size = 0; | ||
669 | } | ||
670 | |||
653 | static int ptrace_bts_config(struct task_struct *child, | 671 | static int ptrace_bts_config(struct task_struct *child, |
654 | long cfg_size, | 672 | long cfg_size, |
655 | const struct ptrace_bts_config __user *ucfg) | 673 | const struct ptrace_bts_config __user *ucfg) |
@@ -679,14 +697,13 @@ static int ptrace_bts_config(struct task_struct *child, | |||
679 | 697 | ||
680 | if ((cfg.flags & PTRACE_BTS_O_ALLOC) && | 698 | if ((cfg.flags & PTRACE_BTS_O_ALLOC) && |
681 | (cfg.size != child->bts_size)) { | 699 | (cfg.size != child->bts_size)) { |
682 | kfree(child->bts_buffer); | 700 | int error; |
683 | 701 | ||
684 | child->bts_size = cfg.size; | 702 | ptrace_bts_free_buffer(child); |
685 | child->bts_buffer = kzalloc(cfg.size, GFP_KERNEL); | 703 | |
686 | if (!child->bts_buffer) { | 704 | error = ptrace_bts_allocate_buffer(child, cfg.size); |
687 | child->bts_size = 0; | 705 | if (error < 0) |
688 | return -ENOMEM; | 706 | return error; |
689 | } | ||
690 | } | 707 | } |
691 | 708 | ||
692 | if (cfg.flags & PTRACE_BTS_O_TRACE) | 709 | if (cfg.flags & PTRACE_BTS_O_TRACE) |
@@ -701,10 +718,8 @@ static int ptrace_bts_config(struct task_struct *child, | |||
701 | if (IS_ERR(child->bts)) { | 718 | if (IS_ERR(child->bts)) { |
702 | int error = PTR_ERR(child->bts); | 719 | int error = PTR_ERR(child->bts); |
703 | 720 | ||
704 | kfree(child->bts_buffer); | 721 | ptrace_bts_free_buffer(child); |
705 | child->bts = NULL; | 722 | child->bts = NULL; |
706 | child->bts_buffer = NULL; | ||
707 | child->bts_size = 0; | ||
708 | 723 | ||
709 | return error; | 724 | return error; |
710 | } | 725 | } |
@@ -769,8 +784,55 @@ static int ptrace_bts_size(struct task_struct *child) | |||
769 | 784 | ||
770 | return (trace->ds.top - trace->ds.begin) / trace->ds.size; | 785 | return (trace->ds.top - trace->ds.begin) / trace->ds.size; |
771 | } | 786 | } |
787 | |||
788 | static void ptrace_bts_fork(struct task_struct *tsk) | ||
789 | { | ||
790 | tsk->bts = NULL; | ||
791 | tsk->bts_buffer = NULL; | ||
792 | tsk->bts_size = 0; | ||
793 | tsk->thread.bts_ovfl_signal = 0; | ||
794 | } | ||
795 | |||
796 | static void ptrace_bts_untrace(struct task_struct *child) | ||
797 | { | ||
798 | if (unlikely(child->bts)) { | ||
799 | ds_release_bts(child->bts); | ||
800 | child->bts = NULL; | ||
801 | |||
802 | /* We cannot update total_vm and locked_vm since | ||
803 | child's mm is already gone. But we can reclaim the | ||
804 | memory. */ | ||
805 | kfree(child->bts_buffer); | ||
806 | child->bts_buffer = NULL; | ||
807 | child->bts_size = 0; | ||
808 | } | ||
809 | } | ||
810 | |||
811 | static void ptrace_bts_detach(struct task_struct *child) | ||
812 | { | ||
813 | if (unlikely(child->bts)) { | ||
814 | ds_release_bts(child->bts); | ||
815 | child->bts = NULL; | ||
816 | |||
817 | ptrace_bts_free_buffer(child); | ||
818 | } | ||
819 | } | ||
820 | #else | ||
821 | static inline void ptrace_bts_fork(struct task_struct *tsk) {} | ||
822 | static inline void ptrace_bts_detach(struct task_struct *child) {} | ||
823 | static inline void ptrace_bts_untrace(struct task_struct *child) {} | ||
772 | #endif /* CONFIG_X86_PTRACE_BTS */ | 824 | #endif /* CONFIG_X86_PTRACE_BTS */ |
773 | 825 | ||
826 | void x86_ptrace_fork(struct task_struct *child, unsigned long clone_flags) | ||
827 | { | ||
828 | ptrace_bts_fork(child); | ||
829 | } | ||
830 | |||
831 | void x86_ptrace_untrace(struct task_struct *child) | ||
832 | { | ||
833 | ptrace_bts_untrace(child); | ||
834 | } | ||
835 | |||
774 | /* | 836 | /* |
775 | * Called by kernel/ptrace.c when detaching.. | 837 | * Called by kernel/ptrace.c when detaching.. |
776 | * | 838 | * |
@@ -782,16 +844,7 @@ void ptrace_disable(struct task_struct *child) | |||
782 | #ifdef TIF_SYSCALL_EMU | 844 | #ifdef TIF_SYSCALL_EMU |
783 | clear_tsk_thread_flag(child, TIF_SYSCALL_EMU); | 845 | clear_tsk_thread_flag(child, TIF_SYSCALL_EMU); |
784 | #endif | 846 | #endif |
785 | #ifdef CONFIG_X86_PTRACE_BTS | 847 | ptrace_bts_detach(child); |
786 | if (child->bts) { | ||
787 | ds_release_bts(child->bts); | ||
788 | child->bts = NULL; | ||
789 | |||
790 | kfree(child->bts_buffer); | ||
791 | child->bts_buffer = NULL; | ||
792 | child->bts_size = 0; | ||
793 | } | ||
794 | #endif /* CONFIG_X86_PTRACE_BTS */ | ||
795 | } | 848 | } |
796 | 849 | ||
797 | #if defined CONFIG_X86_32 || defined CONFIG_IA32_EMULATION | 850 | #if defined CONFIG_X86_32 || defined CONFIG_IA32_EMULATION |