diff options
Diffstat (limited to 'arch/x86/kernel/ptrace.c')
| -rw-r--r-- | arch/x86/kernel/ptrace.c | 384 |
1 files changed, 1 insertions, 383 deletions
diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c index 2e9b55027b7e..70c4872cd8aa 100644 --- a/arch/x86/kernel/ptrace.c +++ b/arch/x86/kernel/ptrace.c | |||
| @@ -2,9 +2,6 @@ | |||
| 2 | /* | 2 | /* |
| 3 | * Pentium III FXSR, SSE support | 3 | * Pentium III FXSR, SSE support |
| 4 | * Gareth Hughes <gareth@valinux.com>, May 2000 | 4 | * Gareth Hughes <gareth@valinux.com>, May 2000 |
| 5 | * | ||
| 6 | * BTS tracing | ||
| 7 | * Markus Metzger <markus.t.metzger@intel.com>, Dec 2007 | ||
| 8 | */ | 5 | */ |
| 9 | 6 | ||
| 10 | #include <linux/kernel.h> | 7 | #include <linux/kernel.h> |
| @@ -22,7 +19,6 @@ | |||
| 22 | #include <linux/audit.h> | 19 | #include <linux/audit.h> |
| 23 | #include <linux/seccomp.h> | 20 | #include <linux/seccomp.h> |
| 24 | #include <linux/signal.h> | 21 | #include <linux/signal.h> |
| 25 | #include <linux/workqueue.h> | ||
| 26 | #include <linux/perf_event.h> | 22 | #include <linux/perf_event.h> |
| 27 | #include <linux/hw_breakpoint.h> | 23 | #include <linux/hw_breakpoint.h> |
| 28 | 24 | ||
| @@ -36,7 +32,6 @@ | |||
| 36 | #include <asm/desc.h> | 32 | #include <asm/desc.h> |
| 37 | #include <asm/prctl.h> | 33 | #include <asm/prctl.h> |
| 38 | #include <asm/proto.h> | 34 | #include <asm/proto.h> |
| 39 | #include <asm/ds.h> | ||
| 40 | #include <asm/hw_breakpoint.h> | 35 | #include <asm/hw_breakpoint.h> |
| 41 | 36 | ||
| 42 | #include "tls.h" | 37 | #include "tls.h" |
| @@ -693,7 +688,7 @@ static int ptrace_set_breakpoint_addr(struct task_struct *tsk, int nr, | |||
| 693 | struct perf_event_attr attr; | 688 | struct perf_event_attr attr; |
| 694 | 689 | ||
| 695 | if (!t->ptrace_bps[nr]) { | 690 | if (!t->ptrace_bps[nr]) { |
| 696 | hw_breakpoint_init(&attr); | 691 | ptrace_breakpoint_init(&attr); |
| 697 | /* | 692 | /* |
| 698 | * Put stub len and type to register (reserve) an inactive but | 693 | * Put stub len and type to register (reserve) an inactive but |
| 699 | * correct bp | 694 | * correct bp |
| @@ -789,342 +784,6 @@ static int ioperm_get(struct task_struct *target, | |||
| 789 | 0, IO_BITMAP_BYTES); | 784 | 0, IO_BITMAP_BYTES); |
| 790 | } | 785 | } |
| 791 | 786 | ||
| 792 | #ifdef CONFIG_X86_PTRACE_BTS | ||
| 793 | /* | ||
| 794 | * A branch trace store context. | ||
| 795 | * | ||
| 796 | * Contexts may only be installed by ptrace_bts_config() and only for | ||
| 797 | * ptraced tasks. | ||
| 798 | * | ||
| 799 | * Contexts are destroyed when the tracee is detached from the tracer. | ||
| 800 | * The actual destruction work requires interrupts enabled, so the | ||
| 801 | * work is deferred and will be scheduled during __ptrace_unlink(). | ||
| 802 | * | ||
| 803 | * Contexts hold an additional task_struct reference on the traced | ||
| 804 | * task, as well as a reference on the tracer's mm. | ||
| 805 | * | ||
| 806 | * Ptrace already holds a task_struct for the duration of ptrace operations, | ||
| 807 | * but since destruction is deferred, it may be executed after both | ||
| 808 | * tracer and tracee exited. | ||
| 809 | */ | ||
| 810 | struct bts_context { | ||
| 811 | /* The branch trace handle. */ | ||
| 812 | struct bts_tracer *tracer; | ||
| 813 | |||
| 814 | /* The buffer used to store the branch trace and its size. */ | ||
| 815 | void *buffer; | ||
| 816 | unsigned int size; | ||
| 817 | |||
| 818 | /* The mm that paid for the above buffer. */ | ||
| 819 | struct mm_struct *mm; | ||
| 820 | |||
| 821 | /* The task this context belongs to. */ | ||
| 822 | struct task_struct *task; | ||
| 823 | |||
| 824 | /* The signal to send on a bts buffer overflow. */ | ||
| 825 | unsigned int bts_ovfl_signal; | ||
| 826 | |||
| 827 | /* The work struct to destroy a context. */ | ||
| 828 | struct work_struct work; | ||
| 829 | }; | ||
| 830 | |||
| 831 | static int alloc_bts_buffer(struct bts_context *context, unsigned int size) | ||
| 832 | { | ||
| 833 | void *buffer = NULL; | ||
| 834 | int err = -ENOMEM; | ||
| 835 | |||
| 836 | err = account_locked_memory(current->mm, current->signal->rlim, size); | ||
| 837 | if (err < 0) | ||
| 838 | return err; | ||
| 839 | |||
| 840 | buffer = kzalloc(size, GFP_KERNEL); | ||
| 841 | if (!buffer) | ||
| 842 | goto out_refund; | ||
| 843 | |||
| 844 | context->buffer = buffer; | ||
| 845 | context->size = size; | ||
| 846 | context->mm = get_task_mm(current); | ||
| 847 | |||
| 848 | return 0; | ||
| 849 | |||
| 850 | out_refund: | ||
| 851 | refund_locked_memory(current->mm, size); | ||
| 852 | return err; | ||
| 853 | } | ||
| 854 | |||
| 855 | static inline void free_bts_buffer(struct bts_context *context) | ||
| 856 | { | ||
| 857 | if (!context->buffer) | ||
| 858 | return; | ||
| 859 | |||
| 860 | kfree(context->buffer); | ||
| 861 | context->buffer = NULL; | ||
| 862 | |||
| 863 | refund_locked_memory(context->mm, context->size); | ||
| 864 | context->size = 0; | ||
| 865 | |||
| 866 | mmput(context->mm); | ||
| 867 | context->mm = NULL; | ||
| 868 | } | ||
| 869 | |||
| 870 | static void free_bts_context_work(struct work_struct *w) | ||
| 871 | { | ||
| 872 | struct bts_context *context; | ||
| 873 | |||
| 874 | context = container_of(w, struct bts_context, work); | ||
| 875 | |||
| 876 | ds_release_bts(context->tracer); | ||
| 877 | put_task_struct(context->task); | ||
| 878 | free_bts_buffer(context); | ||
| 879 | kfree(context); | ||
| 880 | } | ||
| 881 | |||
| 882 | static inline void free_bts_context(struct bts_context *context) | ||
| 883 | { | ||
| 884 | INIT_WORK(&context->work, free_bts_context_work); | ||
| 885 | schedule_work(&context->work); | ||
| 886 | } | ||
| 887 | |||
| 888 | static inline struct bts_context *alloc_bts_context(struct task_struct *task) | ||
| 889 | { | ||
| 890 | struct bts_context *context = kzalloc(sizeof(*context), GFP_KERNEL); | ||
| 891 | if (context) { | ||
| 892 | context->task = task; | ||
| 893 | task->bts = context; | ||
| 894 | |||
| 895 | get_task_struct(task); | ||
| 896 | } | ||
| 897 | |||
| 898 | return context; | ||
| 899 | } | ||
| 900 | |||
| 901 | static int ptrace_bts_read_record(struct task_struct *child, size_t index, | ||
| 902 | struct bts_struct __user *out) | ||
| 903 | { | ||
| 904 | struct bts_context *context; | ||
| 905 | const struct bts_trace *trace; | ||
| 906 | struct bts_struct bts; | ||
| 907 | const unsigned char *at; | ||
| 908 | int error; | ||
| 909 | |||
| 910 | context = child->bts; | ||
| 911 | if (!context) | ||
| 912 | return -ESRCH; | ||
| 913 | |||
| 914 | trace = ds_read_bts(context->tracer); | ||
| 915 | if (!trace) | ||
| 916 | return -ESRCH; | ||
| 917 | |||
| 918 | at = trace->ds.top - ((index + 1) * trace->ds.size); | ||
| 919 | if ((void *)at < trace->ds.begin) | ||
| 920 | at += (trace->ds.n * trace->ds.size); | ||
| 921 | |||
| 922 | if (!trace->read) | ||
| 923 | return -EOPNOTSUPP; | ||
| 924 | |||
| 925 | error = trace->read(context->tracer, at, &bts); | ||
| 926 | if (error < 0) | ||
| 927 | return error; | ||
| 928 | |||
| 929 | if (copy_to_user(out, &bts, sizeof(bts))) | ||
| 930 | return -EFAULT; | ||
| 931 | |||
| 932 | return sizeof(bts); | ||
| 933 | } | ||
| 934 | |||
| 935 | static int ptrace_bts_drain(struct task_struct *child, | ||
| 936 | long size, | ||
| 937 | struct bts_struct __user *out) | ||
| 938 | { | ||
| 939 | struct bts_context *context; | ||
| 940 | const struct bts_trace *trace; | ||
| 941 | const unsigned char *at; | ||
| 942 | int error, drained = 0; | ||
| 943 | |||
| 944 | context = child->bts; | ||
| 945 | if (!context) | ||
| 946 | return -ESRCH; | ||
| 947 | |||
| 948 | trace = ds_read_bts(context->tracer); | ||
| 949 | if (!trace) | ||
| 950 | return -ESRCH; | ||
| 951 | |||
| 952 | if (!trace->read) | ||
| 953 | return -EOPNOTSUPP; | ||
| 954 | |||
| 955 | if (size < (trace->ds.top - trace->ds.begin)) | ||
| 956 | return -EIO; | ||
| 957 | |||
| 958 | for (at = trace->ds.begin; (void *)at < trace->ds.top; | ||
| 959 | out++, drained++, at += trace->ds.size) { | ||
| 960 | struct bts_struct bts; | ||
| 961 | |||
| 962 | error = trace->read(context->tracer, at, &bts); | ||
| 963 | if (error < 0) | ||
| 964 | return error; | ||
| 965 | |||
| 966 | if (copy_to_user(out, &bts, sizeof(bts))) | ||
| 967 | return -EFAULT; | ||
| 968 | } | ||
| 969 | |||
| 970 | memset(trace->ds.begin, 0, trace->ds.n * trace->ds.size); | ||
| 971 | |||
| 972 | error = ds_reset_bts(context->tracer); | ||
| 973 | if (error < 0) | ||
| 974 | return error; | ||
| 975 | |||
| 976 | return drained; | ||
| 977 | } | ||
| 978 | |||
| 979 | static int ptrace_bts_config(struct task_struct *child, | ||
| 980 | long cfg_size, | ||
| 981 | const struct ptrace_bts_config __user *ucfg) | ||
| 982 | { | ||
| 983 | struct bts_context *context; | ||
| 984 | struct ptrace_bts_config cfg; | ||
| 985 | unsigned int flags = 0; | ||
| 986 | |||
| 987 | if (cfg_size < sizeof(cfg)) | ||
| 988 | return -EIO; | ||
| 989 | |||
| 990 | if (copy_from_user(&cfg, ucfg, sizeof(cfg))) | ||
| 991 | return -EFAULT; | ||
| 992 | |||
| 993 | context = child->bts; | ||
| 994 | if (!context) | ||
| 995 | context = alloc_bts_context(child); | ||
| 996 | if (!context) | ||
| 997 | return -ENOMEM; | ||
| 998 | |||
| 999 | if (cfg.flags & PTRACE_BTS_O_SIGNAL) { | ||
| 1000 | if (!cfg.signal) | ||
| 1001 | return -EINVAL; | ||
| 1002 | |||
| 1003 | return -EOPNOTSUPP; | ||
| 1004 | context->bts_ovfl_signal = cfg.signal; | ||
| 1005 | } | ||
| 1006 | |||
| 1007 | ds_release_bts(context->tracer); | ||
| 1008 | context->tracer = NULL; | ||
| 1009 | |||
| 1010 | if ((cfg.flags & PTRACE_BTS_O_ALLOC) && (cfg.size != context->size)) { | ||
| 1011 | int err; | ||
| 1012 | |||
| 1013 | free_bts_buffer(context); | ||
| 1014 | if (!cfg.size) | ||
| 1015 | return 0; | ||
| 1016 | |||
| 1017 | err = alloc_bts_buffer(context, cfg.size); | ||
| 1018 | if (err < 0) | ||
| 1019 | return err; | ||
| 1020 | } | ||
| 1021 | |||
| 1022 | if (cfg.flags & PTRACE_BTS_O_TRACE) | ||
| 1023 | flags |= BTS_USER; | ||
| 1024 | |||
| 1025 | if (cfg.flags & PTRACE_BTS_O_SCHED) | ||
| 1026 | flags |= BTS_TIMESTAMPS; | ||
| 1027 | |||
| 1028 | context->tracer = | ||
| 1029 | ds_request_bts_task(child, context->buffer, context->size, | ||
| 1030 | NULL, (size_t)-1, flags); | ||
| 1031 | if (unlikely(IS_ERR(context->tracer))) { | ||
| 1032 | int error = PTR_ERR(context->tracer); | ||
| 1033 | |||
| 1034 | free_bts_buffer(context); | ||
| 1035 | context->tracer = NULL; | ||
| 1036 | return error; | ||
| 1037 | } | ||
| 1038 | |||
| 1039 | return sizeof(cfg); | ||
| 1040 | } | ||
| 1041 | |||
| 1042 | static int ptrace_bts_status(struct task_struct *child, | ||
| 1043 | long cfg_size, | ||
| 1044 | struct ptrace_bts_config __user *ucfg) | ||
| 1045 | { | ||
| 1046 | struct bts_context *context; | ||
| 1047 | const struct bts_trace *trace; | ||
| 1048 | struct ptrace_bts_config cfg; | ||
| 1049 | |||
| 1050 | context = child->bts; | ||
| 1051 | if (!context) | ||
| 1052 | return -ESRCH; | ||
| 1053 | |||
| 1054 | if (cfg_size < sizeof(cfg)) | ||
| 1055 | return -EIO; | ||
| 1056 | |||
| 1057 | trace = ds_read_bts(context->tracer); | ||
| 1058 | if (!trace) | ||
| 1059 | return -ESRCH; | ||
| 1060 | |||
| 1061 | memset(&cfg, 0, sizeof(cfg)); | ||
| 1062 | cfg.size = trace->ds.end - trace->ds.begin; | ||
| 1063 | cfg.signal = context->bts_ovfl_signal; | ||
| 1064 | cfg.bts_size = sizeof(struct bts_struct); | ||
| 1065 | |||
| 1066 | if (cfg.signal) | ||
| 1067 | cfg.flags |= PTRACE_BTS_O_SIGNAL; | ||
| 1068 | |||
| 1069 | if (trace->ds.flags & BTS_USER) | ||
| 1070 | cfg.flags |= PTRACE_BTS_O_TRACE; | ||
| 1071 | |||
| 1072 | if (trace->ds.flags & BTS_TIMESTAMPS) | ||
| 1073 | cfg.flags |= PTRACE_BTS_O_SCHED; | ||
| 1074 | |||
| 1075 | if (copy_to_user(ucfg, &cfg, sizeof(cfg))) | ||
| 1076 | return -EFAULT; | ||
| 1077 | |||
| 1078 | return sizeof(cfg); | ||
| 1079 | } | ||
| 1080 | |||
| 1081 | static int ptrace_bts_clear(struct task_struct *child) | ||
| 1082 | { | ||
| 1083 | struct bts_context *context; | ||
| 1084 | const struct bts_trace *trace; | ||
| 1085 | |||
| 1086 | context = child->bts; | ||
| 1087 | if (!context) | ||
| 1088 | return -ESRCH; | ||
| 1089 | |||
| 1090 | trace = ds_read_bts(context->tracer); | ||
| 1091 | if (!trace) | ||
| 1092 | return -ESRCH; | ||
| 1093 | |||
| 1094 | memset(trace->ds.begin, 0, trace->ds.n * trace->ds.size); | ||
| 1095 | |||
| 1096 | return ds_reset_bts(context->tracer); | ||
| 1097 | } | ||
| 1098 | |||
| 1099 | static int ptrace_bts_size(struct task_struct *child) | ||
| 1100 | { | ||
| 1101 | struct bts_context *context; | ||
| 1102 | const struct bts_trace *trace; | ||
| 1103 | |||
| 1104 | context = child->bts; | ||
| 1105 | if (!context) | ||
| 1106 | return -ESRCH; | ||
| 1107 | |||
| 1108 | trace = ds_read_bts(context->tracer); | ||
| 1109 | if (!trace) | ||
| 1110 | return -ESRCH; | ||
| 1111 | |||
| 1112 | return (trace->ds.top - trace->ds.begin) / trace->ds.size; | ||
| 1113 | } | ||
| 1114 | |||
| 1115 | /* | ||
| 1116 | * Called from __ptrace_unlink() after the child has been moved back | ||
| 1117 | * to its original parent. | ||
| 1118 | */ | ||
| 1119 | void ptrace_bts_untrace(struct task_struct *child) | ||
| 1120 | { | ||
| 1121 | if (unlikely(child->bts)) { | ||
| 1122 | free_bts_context(child->bts); | ||
| 1123 | child->bts = NULL; | ||
| 1124 | } | ||
| 1125 | } | ||
| 1126 | #endif /* CONFIG_X86_PTRACE_BTS */ | ||
| 1127 | |||
| 1128 | /* | 787 | /* |
| 1129 | * Called by kernel/ptrace.c when detaching.. | 788 | * Called by kernel/ptrace.c when detaching.. |
| 1130 | * | 789 | * |
| @@ -1252,39 +911,6 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) | |||
| 1252 | break; | 911 | break; |
| 1253 | #endif | 912 | #endif |
| 1254 | 913 | ||
| 1255 | /* | ||
| 1256 | * These bits need more cooking - not enabled yet: | ||
| 1257 | */ | ||
| 1258 | #ifdef CONFIG_X86_PTRACE_BTS | ||
| 1259 | case PTRACE_BTS_CONFIG: | ||
| 1260 | ret = ptrace_bts_config | ||
| 1261 | (child, data, (struct ptrace_bts_config __user *)addr); | ||
| 1262 | break; | ||
| 1263 | |||
| 1264 | case PTRACE_BTS_STATUS: | ||
| 1265 | ret = ptrace_bts_status | ||
| 1266 | (child, data, (struct ptrace_bts_config __user *)addr); | ||
| 1267 | break; | ||
| 1268 | |||
| 1269 | case PTRACE_BTS_SIZE: | ||
| 1270 | ret = ptrace_bts_size(child); | ||
| 1271 | break; | ||
| 1272 | |||
| 1273 | case PTRACE_BTS_GET: | ||
| 1274 | ret = ptrace_bts_read_record | ||
| 1275 | (child, data, (struct bts_struct __user *) addr); | ||
| 1276 | break; | ||
| 1277 | |||
| 1278 | case PTRACE_BTS_CLEAR: | ||
| 1279 | ret = ptrace_bts_clear(child); | ||
| 1280 | break; | ||
| 1281 | |||
| 1282 | case PTRACE_BTS_DRAIN: | ||
| 1283 | ret = ptrace_bts_drain | ||
| 1284 | (child, data, (struct bts_struct __user *) addr); | ||
| 1285 | break; | ||
| 1286 | #endif /* CONFIG_X86_PTRACE_BTS */ | ||
| 1287 | |||
| 1288 | default: | 914 | default: |
| 1289 | ret = ptrace_request(child, request, addr, data); | 915 | ret = ptrace_request(child, request, addr, data); |
| 1290 | break; | 916 | break; |
| @@ -1544,14 +1170,6 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request, | |||
| 1544 | 1170 | ||
| 1545 | case PTRACE_GET_THREAD_AREA: | 1171 | case PTRACE_GET_THREAD_AREA: |
| 1546 | case PTRACE_SET_THREAD_AREA: | 1172 | case PTRACE_SET_THREAD_AREA: |
| 1547 | #ifdef CONFIG_X86_PTRACE_BTS | ||
| 1548 | case PTRACE_BTS_CONFIG: | ||
| 1549 | case PTRACE_BTS_STATUS: | ||
| 1550 | case PTRACE_BTS_SIZE: | ||
| 1551 | case PTRACE_BTS_GET: | ||
| 1552 | case PTRACE_BTS_CLEAR: | ||
| 1553 | case PTRACE_BTS_DRAIN: | ||
| 1554 | #endif /* CONFIG_X86_PTRACE_BTS */ | ||
| 1555 | return arch_ptrace(child, request, addr, data); | 1173 | return arch_ptrace(child, request, addr, data); |
| 1556 | 1174 | ||
| 1557 | default: | 1175 | default: |
