diff options
-rw-r--r-- | arch/x86/kernel/ptrace.c | 214 |
1 files changed, 214 insertions, 0 deletions
diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c index d56aa18309f8..3399c1be79b8 100644 --- a/arch/x86/kernel/ptrace.c +++ b/arch/x86/kernel/ptrace.c | |||
@@ -636,6 +636,10 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) | |||
636 | 636 | ||
637 | #ifdef CONFIG_IA32_EMULATION | 637 | #ifdef CONFIG_IA32_EMULATION |
638 | 638 | ||
639 | #include <linux/compat.h> | ||
640 | #include <linux/syscalls.h> | ||
641 | #include <asm/ia32.h> | ||
642 | #include <asm/fpu32.h> | ||
639 | #include <asm/user32.h> | 643 | #include <asm/user32.h> |
640 | 644 | ||
641 | #define R32(l,q) \ | 645 | #define R32(l,q) \ |
@@ -758,6 +762,216 @@ static int getreg32(struct task_struct *child, unsigned regno, u32 *val) | |||
758 | #undef R32 | 762 | #undef R32 |
759 | #undef SEG32 | 763 | #undef SEG32 |
760 | 764 | ||
765 | static long ptrace32_siginfo(unsigned request, u32 pid, u32 addr, u32 data) | ||
766 | { | ||
767 | siginfo_t __user *si = compat_alloc_user_space(sizeof(siginfo_t)); | ||
768 | compat_siginfo_t __user *si32 = compat_ptr(data); | ||
769 | siginfo_t ssi; | ||
770 | int ret; | ||
771 | |||
772 | if (request == PTRACE_SETSIGINFO) { | ||
773 | memset(&ssi, 0, sizeof(siginfo_t)); | ||
774 | ret = copy_siginfo_from_user32(&ssi, si32); | ||
775 | if (ret) | ||
776 | return ret; | ||
777 | if (copy_to_user(si, &ssi, sizeof(siginfo_t))) | ||
778 | return -EFAULT; | ||
779 | } | ||
780 | ret = sys_ptrace(request, pid, addr, (unsigned long)si); | ||
781 | if (ret) | ||
782 | return ret; | ||
783 | if (request == PTRACE_GETSIGINFO) { | ||
784 | if (copy_from_user(&ssi, si, sizeof(siginfo_t))) | ||
785 | return -EFAULT; | ||
786 | ret = copy_siginfo_to_user32(si32, &ssi); | ||
787 | } | ||
788 | return ret; | ||
789 | } | ||
790 | |||
791 | asmlinkage long sys32_ptrace(long request, u32 pid, u32 addr, u32 data) | ||
792 | { | ||
793 | struct task_struct *child; | ||
794 | struct pt_regs *childregs; | ||
795 | void __user *datap = compat_ptr(data); | ||
796 | int ret; | ||
797 | __u32 val; | ||
798 | |||
799 | switch (request) { | ||
800 | case PTRACE_TRACEME: | ||
801 | case PTRACE_ATTACH: | ||
802 | case PTRACE_KILL: | ||
803 | case PTRACE_CONT: | ||
804 | case PTRACE_SINGLESTEP: | ||
805 | case PTRACE_SINGLEBLOCK: | ||
806 | case PTRACE_DETACH: | ||
807 | case PTRACE_SYSCALL: | ||
808 | case PTRACE_OLDSETOPTIONS: | ||
809 | case PTRACE_SETOPTIONS: | ||
810 | case PTRACE_SET_THREAD_AREA: | ||
811 | case PTRACE_GET_THREAD_AREA: | ||
812 | return sys_ptrace(request, pid, addr, data); | ||
813 | |||
814 | default: | ||
815 | return -EINVAL; | ||
816 | |||
817 | case PTRACE_PEEKTEXT: | ||
818 | case PTRACE_PEEKDATA: | ||
819 | case PTRACE_POKEDATA: | ||
820 | case PTRACE_POKETEXT: | ||
821 | case PTRACE_POKEUSR: | ||
822 | case PTRACE_PEEKUSR: | ||
823 | case PTRACE_GETREGS: | ||
824 | case PTRACE_SETREGS: | ||
825 | case PTRACE_SETFPREGS: | ||
826 | case PTRACE_GETFPREGS: | ||
827 | case PTRACE_SETFPXREGS: | ||
828 | case PTRACE_GETFPXREGS: | ||
829 | case PTRACE_GETEVENTMSG: | ||
830 | break; | ||
831 | |||
832 | case PTRACE_SETSIGINFO: | ||
833 | case PTRACE_GETSIGINFO: | ||
834 | return ptrace32_siginfo(request, pid, addr, data); | ||
835 | } | ||
836 | |||
837 | child = ptrace_get_task_struct(pid); | ||
838 | if (IS_ERR(child)) | ||
839 | return PTR_ERR(child); | ||
840 | |||
841 | ret = ptrace_check_attach(child, request == PTRACE_KILL); | ||
842 | if (ret < 0) | ||
843 | goto out; | ||
844 | |||
845 | childregs = task_pt_regs(child); | ||
846 | |||
847 | switch (request) { | ||
848 | case PTRACE_PEEKDATA: | ||
849 | case PTRACE_PEEKTEXT: | ||
850 | ret = 0; | ||
851 | if (access_process_vm(child, addr, &val, sizeof(u32), 0) != | ||
852 | sizeof(u32)) | ||
853 | ret = -EIO; | ||
854 | else | ||
855 | ret = put_user(val, (unsigned int __user *)datap); | ||
856 | break; | ||
857 | |||
858 | case PTRACE_POKEDATA: | ||
859 | case PTRACE_POKETEXT: | ||
860 | ret = 0; | ||
861 | if (access_process_vm(child, addr, &data, sizeof(u32), 1) != | ||
862 | sizeof(u32)) | ||
863 | ret = -EIO; | ||
864 | break; | ||
865 | |||
866 | case PTRACE_PEEKUSR: | ||
867 | ret = getreg32(child, addr, &val); | ||
868 | if (ret == 0) | ||
869 | ret = put_user(val, (__u32 __user *)datap); | ||
870 | break; | ||
871 | |||
872 | case PTRACE_POKEUSR: | ||
873 | ret = putreg32(child, addr, data); | ||
874 | break; | ||
875 | |||
876 | case PTRACE_GETREGS: { /* Get all gp regs from the child. */ | ||
877 | int i; | ||
878 | |||
879 | if (!access_ok(VERIFY_WRITE, datap, 16*4)) { | ||
880 | ret = -EIO; | ||
881 | break; | ||
882 | } | ||
883 | ret = 0; | ||
884 | for (i = 0; i < sizeof(struct user_regs_struct32); i += sizeof(__u32)) { | ||
885 | getreg32(child, i, &val); | ||
886 | ret |= __put_user(val, (u32 __user *)datap); | ||
887 | datap += sizeof(u32); | ||
888 | } | ||
889 | break; | ||
890 | } | ||
891 | |||
892 | case PTRACE_SETREGS: { /* Set all gp regs in the child. */ | ||
893 | unsigned long tmp; | ||
894 | int i; | ||
895 | |||
896 | if (!access_ok(VERIFY_READ, datap, 16*4)) { | ||
897 | ret = -EIO; | ||
898 | break; | ||
899 | } | ||
900 | ret = 0; | ||
901 | for (i = 0; i < sizeof(struct user_regs_struct32); i += sizeof(u32)) { | ||
902 | ret |= __get_user(tmp, (u32 __user *)datap); | ||
903 | putreg32(child, i, tmp); | ||
904 | datap += sizeof(u32); | ||
905 | } | ||
906 | break; | ||
907 | } | ||
908 | |||
909 | case PTRACE_GETFPREGS: | ||
910 | ret = -EIO; | ||
911 | if (!access_ok(VERIFY_READ, compat_ptr(data), | ||
912 | sizeof(struct user_i387_struct))) | ||
913 | break; | ||
914 | save_i387_ia32(child, datap, childregs, 1); | ||
915 | ret = 0; | ||
916 | break; | ||
917 | |||
918 | case PTRACE_SETFPREGS: | ||
919 | ret = -EIO; | ||
920 | if (!access_ok(VERIFY_WRITE, datap, | ||
921 | sizeof(struct user_i387_struct))) | ||
922 | break; | ||
923 | ret = 0; | ||
924 | /* don't check EFAULT to be bug-to-bug compatible to i386 */ | ||
925 | restore_i387_ia32(child, datap, 1); | ||
926 | break; | ||
927 | |||
928 | case PTRACE_GETFPXREGS: { | ||
929 | struct user32_fxsr_struct __user *u = datap; | ||
930 | |||
931 | init_fpu(child); | ||
932 | ret = -EIO; | ||
933 | if (!access_ok(VERIFY_WRITE, u, sizeof(*u))) | ||
934 | break; | ||
935 | ret = -EFAULT; | ||
936 | if (__copy_to_user(u, &child->thread.i387.fxsave, sizeof(*u))) | ||
937 | break; | ||
938 | ret = __put_user(childregs->cs, &u->fcs); | ||
939 | ret |= __put_user(child->thread.ds, &u->fos); | ||
940 | break; | ||
941 | } | ||
942 | case PTRACE_SETFPXREGS: { | ||
943 | struct user32_fxsr_struct __user *u = datap; | ||
944 | |||
945 | unlazy_fpu(child); | ||
946 | ret = -EIO; | ||
947 | if (!access_ok(VERIFY_READ, u, sizeof(*u))) | ||
948 | break; | ||
949 | /* | ||
950 | * no checking to be bug-to-bug compatible with i386. | ||
951 | * but silence warning | ||
952 | */ | ||
953 | if (__copy_from_user(&child->thread.i387.fxsave, u, sizeof(*u))) | ||
954 | ; | ||
955 | set_stopped_child_used_math(child); | ||
956 | child->thread.i387.fxsave.mxcsr &= mxcsr_feature_mask; | ||
957 | ret = 0; | ||
958 | break; | ||
959 | } | ||
960 | |||
961 | case PTRACE_GETEVENTMSG: | ||
962 | ret = put_user(child->ptrace_message, | ||
963 | (unsigned int __user *)compat_ptr(data)); | ||
964 | break; | ||
965 | |||
966 | default: | ||
967 | BUG(); | ||
968 | } | ||
969 | |||
970 | out: | ||
971 | put_task_struct(child); | ||
972 | return ret; | ||
973 | } | ||
974 | |||
761 | #endif /* CONFIG_IA32_EMULATION */ | 975 | #endif /* CONFIG_IA32_EMULATION */ |
762 | 976 | ||
763 | #ifdef CONFIG_X86_32 | 977 | #ifdef CONFIG_X86_32 |