]>
Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
1da177e4 LT |
2 | /* |
3 | * Copyright (C) 2000-2003, Axis Communications AB. | |
4 | */ | |
5 | ||
6 | #include <linux/kernel.h> | |
7 | #include <linux/sched.h> | |
68db0cf1 | 8 | #include <linux/sched/task_stack.h> |
1da177e4 LT |
9 | #include <linux/mm.h> |
10 | #include <linux/smp.h> | |
1da177e4 LT |
11 | #include <linux/errno.h> |
12 | #include <linux/ptrace.h> | |
13 | #include <linux/user.h> | |
7ed20e1a | 14 | #include <linux/signal.h> |
5d01e6ce | 15 | #include <linux/security.h> |
1da177e4 | 16 | |
7c0f6ba6 | 17 | #include <linux/uaccess.h> |
1da177e4 LT |
18 | #include <asm/page.h> |
19 | #include <asm/pgtable.h> | |
1da177e4 LT |
20 | #include <asm/processor.h> |
21 | ||
22 | /* | |
23 | * Determines which bits in DCCR the user has access to. | |
24 | * 1 = access, 0 = no access. | |
25 | */ | |
26 | #define DCCR_MASK 0x0000001f /* XNZVC */ | |
27 | ||
28 | /* | |
29 | * Get contents of register REGNO in task TASK. | |
30 | */ | |
31 | inline long get_reg(struct task_struct *task, unsigned int regno) | |
32 | { | |
33 | /* USP is a special case, it's not in the pt_regs struct but | |
34 | * in the tasks thread struct | |
35 | */ | |
36 | ||
37 | if (regno == PT_USP) | |
38 | return task->thread.usp; | |
39 | else if (regno < PT_MAX) | |
95ca0dc6 | 40 | return ((unsigned long *)task_pt_regs(task))[regno]; |
1da177e4 LT |
41 | else |
42 | return 0; | |
43 | } | |
44 | ||
45 | /* | |
46 | * Write contents of register REGNO in task TASK. | |
47 | */ | |
48 | inline int put_reg(struct task_struct *task, unsigned int regno, | |
49 | unsigned long data) | |
50 | { | |
51 | if (regno == PT_USP) | |
52 | task->thread.usp = data; | |
53 | else if (regno < PT_MAX) | |
95ca0dc6 | 54 | ((unsigned long *)task_pt_regs(task))[regno] = data; |
1da177e4 LT |
55 | else |
56 | return -1; | |
57 | return 0; | |
58 | } | |
59 | ||
60 | /* | |
61 | * Called by kernel/ptrace.c when detaching. | |
62 | * | |
63 | * Make sure the single step bit is not set. | |
64 | */ | |
65 | void | |
66 | ptrace_disable(struct task_struct *child) | |
67 | { | |
68 | /* Todo - pending singlesteps? */ | |
2afab729 | 69 | clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); |
1da177e4 LT |
70 | } |
71 | ||
72 | /* | |
73 | * Note that this implementation of ptrace behaves differently from vanilla | |
74 | * ptrace. Contrary to what the man page says, in the PTRACE_PEEKTEXT, | |
75 | * PTRACE_PEEKDATA, and PTRACE_PEEKUSER requests the data variable is not | |
76 | * ignored. Instead, the data variable is expected to point at a location | |
77 | * (in user space) where the result of the ptrace call is written (instead of | |
78 | * being returned). | |
79 | */ | |
9b05a69e NK |
80 | long arch_ptrace(struct task_struct *child, long request, |
81 | unsigned long addr, unsigned long data) | |
1da177e4 | 82 | { |
1da177e4 | 83 | int ret; |
475a4b81 | 84 | unsigned int regno = addr >> 2; |
1da177e4 LT |
85 | unsigned long __user *datap = (unsigned long __user *)data; |
86 | ||
1da177e4 LT |
87 | switch (request) { |
88 | /* Read word at location address. */ | |
89 | case PTRACE_PEEKTEXT: | |
76647323 AD |
90 | case PTRACE_PEEKDATA: |
91 | ret = generic_ptrace_peekdata(child, addr, data); | |
1da177e4 | 92 | break; |
1da177e4 LT |
93 | |
94 | /* Read the word at location address in the USER area. */ | |
95 | case PTRACE_PEEKUSR: { | |
96 | unsigned long tmp; | |
97 | ||
98 | ret = -EIO; | |
475a4b81 | 99 | if ((addr & 3) || regno > PT_MAX) |
1da177e4 LT |
100 | break; |
101 | ||
475a4b81 | 102 | tmp = get_reg(child, regno); |
1da177e4 LT |
103 | ret = put_user(tmp, datap); |
104 | break; | |
105 | } | |
106 | ||
107 | /* Write the word at location address. */ | |
108 | case PTRACE_POKETEXT: | |
109 | case PTRACE_POKEDATA: | |
f284ce72 | 110 | ret = generic_ptrace_pokedata(child, addr, data); |
1da177e4 LT |
111 | break; |
112 | ||
113 | /* Write the word at location address in the USER area. */ | |
114 | case PTRACE_POKEUSR: | |
115 | ret = -EIO; | |
475a4b81 | 116 | if ((addr & 3) || regno > PT_MAX) |
1da177e4 LT |
117 | break; |
118 | ||
475a4b81 | 119 | if (regno == PT_DCCR) { |
1da177e4 LT |
120 | /* don't allow the tracing process to change stuff like |
121 | * interrupt enable, kernel/user bit, dma enables etc. | |
122 | */ | |
123 | data &= DCCR_MASK; | |
124 | data |= get_reg(child, PT_DCCR) & ~DCCR_MASK; | |
125 | } | |
475a4b81 | 126 | if (put_reg(child, regno, data)) |
1da177e4 LT |
127 | break; |
128 | ret = 0; | |
129 | break; | |
130 | ||
1da177e4 LT |
131 | /* Get all GP registers from the child. */ |
132 | case PTRACE_GETREGS: { | |
133 | int i; | |
134 | unsigned long tmp; | |
135 | ||
c3508858 | 136 | ret = 0; |
1da177e4 LT |
137 | for (i = 0; i <= PT_MAX; i++) { |
138 | tmp = get_reg(child, i); | |
139 | ||
140 | if (put_user(tmp, datap)) { | |
141 | ret = -EFAULT; | |
c3508858 | 142 | break; |
1da177e4 LT |
143 | } |
144 | ||
475a4b81 | 145 | datap++; |
1da177e4 LT |
146 | } |
147 | ||
1da177e4 LT |
148 | break; |
149 | } | |
150 | ||
151 | /* Set all GP registers in the child. */ | |
152 | case PTRACE_SETREGS: { | |
153 | int i; | |
154 | unsigned long tmp; | |
155 | ||
c3508858 | 156 | ret = 0; |
1da177e4 LT |
157 | for (i = 0; i <= PT_MAX; i++) { |
158 | if (get_user(tmp, datap)) { | |
159 | ret = -EFAULT; | |
c3508858 | 160 | break; |
1da177e4 LT |
161 | } |
162 | ||
163 | if (i == PT_DCCR) { | |
164 | tmp &= DCCR_MASK; | |
165 | tmp |= get_reg(child, PT_DCCR) & ~DCCR_MASK; | |
166 | } | |
167 | ||
168 | put_reg(child, i, tmp); | |
475a4b81 | 169 | datap++; |
1da177e4 LT |
170 | } |
171 | ||
1da177e4 LT |
172 | break; |
173 | } | |
174 | ||
175 | default: | |
176 | ret = ptrace_request(child, request, addr, data); | |
177 | break; | |
178 | } | |
481bed45 | 179 | |
1da177e4 LT |
180 | return ret; |
181 | } | |
182 | ||
183 | void do_syscall_trace(void) | |
184 | { | |
185 | if (!test_thread_flag(TIF_SYSCALL_TRACE)) | |
186 | return; | |
187 | ||
188 | if (!(current->ptrace & PT_PTRACED)) | |
189 | return; | |
190 | ||
191 | /* the 0x80 provides a way for the tracing parent to distinguish | |
192 | between a syscall stop and SIGTRAP delivery */ | |
193 | ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) | |
194 | ? 0x80 : 0)); | |
195 | ||
196 | /* | |
197 | * This isn't the same as continuing with a signal, but it will do for | |
198 | * normal use. | |
199 | */ | |
200 | if (current->exit_code) { | |
201 | send_sig(current->exit_code, current, 1); | |
202 | current->exit_code = 0; | |
203 | } | |
204 | } |