]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/amd64/linux/linux_ptrace.c
Import tzdata 2018c
[FreeBSD/FreeBSD.git] / sys / amd64 / linux / linux_ptrace.c
1 /*-
2  * Copyright (c) 2017 Edward Tomasz Napierala <trasz@FreeBSD.org>
3  * All rights reserved.
4  *
5  * This software was developed by SRI International and the University of
6  * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
7  * ("CTSRD"), as part of the DARPA CRASH research programme.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  */
30
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD$");
33
34 #include <sys/param.h>
35 #include <sys/proc.h>
36 #include <sys/ptrace.h>
37 #include <sys/syscallsubr.h>
38
39 #include <machine/pcb.h>
40 #include <machine/reg.h>
41
42 #include <amd64/linux/linux.h>
43 #include <amd64/linux/linux_proto.h>
44 #include <compat/linux/linux_signal.h>
45
46 #define LINUX_PTRACE_TRACEME            0
47 #define LINUX_PTRACE_PEEKTEXT           1
48 #define LINUX_PTRACE_PEEKDATA           2
49 #define LINUX_PTRACE_PEEKUSER           3
50 #define LINUX_PTRACE_POKETEXT           4
51 #define LINUX_PTRACE_POKEDATA           5
52 #define LINUX_PTRACE_POKEUSER           6
53 #define LINUX_PTRACE_CONT               7
54 #define LINUX_PTRACE_KILL               8
55 #define LINUX_PTRACE_SINGLESTEP         9
56 #define LINUX_PTRACE_GETREGS            12
57 #define LINUX_PTRACE_SETREGS            13
58 #define LINUX_PTRACE_GETFPREGS          14
59 #define LINUX_PTRACE_SETFPREGS          15
60 #define LINUX_PTRACE_ATTACH             16
61 #define LINUX_PTRACE_DETACH             17
62 #define LINUX_PTRACE_SYSCALL            24
63 #define LINUX_PTRACE_SETOPTIONS         0x4200
64 #define LINUX_PTRACE_GETREGSET          0x4204
65 #define LINUX_PTRACE_SEIZE              0x4206
66
67 #define LINUX_PTRACE_O_TRACESYSGOOD     1
68 #define LINUX_PTRACE_O_TRACEFORK        2
69 #define LINUX_PTRACE_O_TRACEVFORK       4
70 #define LINUX_PTRACE_O_TRACECLONE       8
71 #define LINUX_PTRACE_O_TRACEEXEC        16
72 #define LINUX_PTRACE_O_TRACEVFORKDONE   32
73 #define LINUX_PTRACE_O_TRACEEXIT        64
74 #define LINUX_PTRACE_O_TRACESECCOMP     128
75 #define LINUX_PTRACE_O_EXITKILL         1048576 
76 #define LINUX_PTRACE_O_SUSPEND_SECCOMP  2097152
77
78 #define LINUX_NT_PRSTATUS               1
79
80 #define LINUX_PTRACE_O_MASK     (LINUX_PTRACE_O_TRACESYSGOOD |  \
81     LINUX_PTRACE_O_TRACEFORK | LINUX_PTRACE_O_TRACEVFORK |      \
82     LINUX_PTRACE_O_TRACECLONE | LINUX_PTRACE_O_TRACEEXEC |      \
83     LINUX_PTRACE_O_TRACEVFORKDONE | LINUX_PTRACE_O_TRACEEXIT |  \
84     LINUX_PTRACE_O_TRACESECCOMP | LINUX_PTRACE_O_EXITKILL |     \
85     LINUX_PTRACE_O_SUSPEND_SECCOMP)
86
87 static int
88 map_signum(int lsig, int *bsigp)
89 {
90         int bsig;
91
92         if (lsig == 0) {
93                 *bsigp = 0;
94                 return (0);
95         }
96
97         if (lsig < 0 || lsig > LINUX_SIGRTMAX)
98                 return (EINVAL);
99
100         bsig = linux_to_bsd_signal(lsig);
101         if (bsig == SIGSTOP)
102                 bsig = 0;
103
104         *bsigp = bsig;
105         return (0);
106 }
107
108 struct linux_pt_reg {
109         l_ulong r15;
110         l_ulong r14;
111         l_ulong r13;
112         l_ulong r12;
113         l_ulong rbp;
114         l_ulong rbx;
115         l_ulong r11;
116         l_ulong r10;
117         l_ulong r9;
118         l_ulong r8;
119         l_ulong rax;
120         l_ulong rcx;
121         l_ulong rdx;
122         l_ulong rsi;
123         l_ulong rdi;
124         l_ulong orig_rax;
125         l_ulong rip;
126         l_ulong cs;
127         l_ulong eflags;
128         l_ulong rsp;
129         l_ulong ss;
130 };
131
132 /*
133  * Translate amd64 ptrace registers between Linux and FreeBSD formats.
134  * The translation is pretty straighforward, for all registers but
135  * orig_rax on Linux side and r_trapno and r_err in FreeBSD.
136  */
137 static void
138 map_regs_to_linux(struct reg *b_reg, struct linux_pt_reg *l_reg)
139 {
140
141         l_reg->r15 = b_reg->r_r15;
142         l_reg->r14 = b_reg->r_r14;
143         l_reg->r13 = b_reg->r_r13;
144         l_reg->r12 = b_reg->r_r12;
145         l_reg->rbp = b_reg->r_rbp;
146         l_reg->rbx = b_reg->r_rbx;
147         l_reg->r11 = b_reg->r_r11;
148         l_reg->r10 = b_reg->r_r10;
149         l_reg->r9 = b_reg->r_r9;
150         l_reg->r8 = b_reg->r_r8;
151         l_reg->rax = b_reg->r_rax;
152         l_reg->rcx = b_reg->r_rcx;
153         l_reg->rdx = b_reg->r_rdx;
154         l_reg->rsi = b_reg->r_rsi;
155         l_reg->rdi = b_reg->r_rdi;
156         l_reg->orig_rax = b_reg->r_rax;
157         l_reg->rip = b_reg->r_rip;
158         l_reg->cs = b_reg->r_cs;
159         l_reg->eflags = b_reg->r_rflags;
160         l_reg->rsp = b_reg->r_rsp;
161         l_reg->ss = b_reg->r_ss;
162 }
163
164 static void
165 map_regs_from_linux(struct reg *b_reg, struct linux_pt_reg *l_reg)
166 {
167         b_reg->r_r15 = l_reg->r15;
168         b_reg->r_r14 = l_reg->r14;
169         b_reg->r_r13 = l_reg->r13;
170         b_reg->r_r12 = l_reg->r12;
171         b_reg->r_r11 = l_reg->r11;
172         b_reg->r_r10 = l_reg->r10;
173         b_reg->r_r9 = l_reg->r9;
174         b_reg->r_r8 = l_reg->r8;
175         b_reg->r_rdi = l_reg->rdi;
176         b_reg->r_rsi = l_reg->rsi;
177         b_reg->r_rbp = l_reg->rbp;
178         b_reg->r_rbx = l_reg->rbx;
179         b_reg->r_rdx = l_reg->rdx;
180         b_reg->r_rcx = l_reg->rcx;
181         b_reg->r_rax = l_reg->rax;
182
183         /*
184          * XXX: Are zeroes the right thing to put here?
185          */
186         b_reg->r_trapno = 0;
187         b_reg->r_fs = 0;
188         b_reg->r_gs = 0;
189         b_reg->r_err = 0;
190         b_reg->r_es = 0;
191         b_reg->r_ds = 0;
192
193         b_reg->r_rip = l_reg->rip;
194         b_reg->r_cs = l_reg->cs;
195         b_reg->r_rflags = l_reg->eflags;
196         b_reg->r_rsp = l_reg->rsp;
197         b_reg->r_ss = l_reg->ss;
198 }
199
200 static int
201 linux_ptrace_peek(struct thread *td, pid_t pid, void *addr, void *data)
202 {
203         int error;
204
205         error = kern_ptrace(td, PT_READ_I, pid, addr, 0);
206         if (error == 0)
207                 error = copyout(td->td_retval, data, sizeof(l_int));
208         td->td_retval[0] = error;
209
210         return (error);
211 }
212
213 static int
214 linux_ptrace_setoptions(struct thread *td, pid_t pid, l_ulong data)
215 {
216         int mask;
217
218         mask = 0;
219
220         if (data & ~LINUX_PTRACE_O_MASK) {
221                 printf("%s: unknown ptrace option %lx set; "
222                     "returning EINVAL\n",
223                     __func__, data & ~LINUX_PTRACE_O_MASK);
224                 return (EINVAL);
225         }
226
227         /*
228          * PTRACE_O_EXITKILL is ignored, we do that by default.
229          */
230
231         if (data & LINUX_PTRACE_O_TRACESYSGOOD) {
232                 printf("%s: PTRACE_O_TRACESYSGOOD not implemented; "
233                     "returning EINVAL\n", __func__);
234                 return (EINVAL);
235         }
236
237         if (data & LINUX_PTRACE_O_TRACEFORK)
238                 mask |= PTRACE_FORK;
239
240         if (data & LINUX_PTRACE_O_TRACEVFORK)
241                 mask |= PTRACE_VFORK;
242
243         if (data & LINUX_PTRACE_O_TRACECLONE)
244                 mask |= PTRACE_VFORK;
245
246         if (data & LINUX_PTRACE_O_TRACEEXEC)
247                 mask |= PTRACE_EXEC;
248
249         if (data & LINUX_PTRACE_O_TRACEVFORKDONE)
250                 mask |= PTRACE_VFORK; /* XXX: Close enough? */
251
252         if (data & LINUX_PTRACE_O_TRACEEXIT) {
253                 printf("%s: PTRACE_O_TRACEEXIT not implemented; "
254                     "returning EINVAL\n", __func__);
255                 return (EINVAL);
256         }
257
258         return (kern_ptrace(td, PT_SET_EVENT_MASK, pid, &mask, sizeof(mask)));
259 }
260
261 static int
262 linux_ptrace_getregs(struct thread *td, pid_t pid, void *data)
263 {
264         struct ptrace_lwpinfo lwpinfo;
265         struct reg b_reg;
266         struct linux_pt_reg l_reg;
267         int error;
268
269         error = kern_ptrace(td, PT_GETREGS, pid, &b_reg, 0);
270         if (error != 0)
271                 return (error);
272
273         map_regs_to_linux(&b_reg, &l_reg);
274
275         /*
276          * The strace(1) utility depends on RAX being set to -ENOSYS
277          * on syscall entry.
278          */
279         error = kern_ptrace(td, PT_LWPINFO, pid, &lwpinfo, sizeof(lwpinfo));
280         if (error != 0) {
281                 printf("%s: PT_LWPINFO failed with error %d\n", __func__, error);
282                 return (error);
283         }
284         if (lwpinfo.pl_flags & PL_FLAG_SCE)
285                 l_reg.rax = -38; // XXX: Don't hardcode?
286
287         error = copyout(&l_reg, (void *)data, sizeof(l_reg));
288         return (error);
289 }
290
291 static int
292 linux_ptrace_setregs(struct thread *td, pid_t pid, void *data)
293 {
294         struct reg b_reg;
295         struct linux_pt_reg l_reg;
296         int error;
297
298         error = copyin(data, &l_reg, sizeof(l_reg));
299         if (error != 0)
300                 return (error);
301         map_regs_from_linux(&b_reg, &l_reg);
302         error = kern_ptrace(td, PT_SETREGS, pid, &b_reg, 0);
303         return (error);
304 }
305
306 static int
307 linux_ptrace_getregset(struct thread *td, pid_t pid, l_ulong addr, l_ulong data)
308 {
309
310         switch (addr) {
311         case LINUX_NT_PRSTATUS:
312                 printf("%s: NT_PRSTATUS not implemented; returning EINVAL\n",
313                     __func__);
314                 return (EINVAL);
315         default:
316                 printf("%s: PTRACE_GETREGSET request %ld not implemented; "
317                     "returning EINVAL\n", __func__, addr);
318                 return (EINVAL);
319         }
320 }
321
322 static int
323 linux_ptrace_seize(struct thread *td, pid_t pid, l_ulong addr, l_ulong data)
324 {
325
326         printf("%s: PTRACE_SEIZE not implemented; returning EINVAL\n", __func__);
327         return (EINVAL);
328 }
329
330 int
331 linux_ptrace(struct thread *td, struct linux_ptrace_args *uap)
332 {
333         void *addr;
334         pid_t pid;
335         int error, sig;
336
337         pid  = (pid_t)uap->pid;
338         addr = (void *)uap->addr;
339
340         switch (uap->req) {
341         case LINUX_PTRACE_TRACEME:
342                 error = kern_ptrace(td, PT_TRACE_ME, 0, 0, 0);
343                 break;
344         case LINUX_PTRACE_PEEKTEXT:
345         case LINUX_PTRACE_PEEKDATA:
346                 error = linux_ptrace_peek(td, pid, addr, (void *)uap->data);
347                 if (error != 0)
348                         return (error);
349                 /*
350                  * Linux expects this syscall to read 64 bits, not 32.
351                  */
352                 error = linux_ptrace_peek(td, pid,
353                     (void *)(uap->addr + 4), (void *)(uap->data + 4));
354                 break;
355         case LINUX_PTRACE_POKETEXT:
356                 error = kern_ptrace(td, PT_WRITE_I, pid, addr, uap->data);
357                 break;
358         case LINUX_PTRACE_POKEDATA:
359                 error = kern_ptrace(td, PT_WRITE_D, pid, addr, uap->data);
360                 break;
361         case LINUX_PTRACE_CONT:
362                 error = map_signum(uap->data, &sig);
363                 if (error != 0)
364                         break;
365                 error = kern_ptrace(td, PT_CONTINUE, pid, (void *)1, sig);
366                 break;
367         case LINUX_PTRACE_KILL:
368                 error = kern_ptrace(td, PT_KILL, pid, addr, uap->data);
369                 break;
370         case LINUX_PTRACE_SINGLESTEP:
371                 error = map_signum(uap->data, &sig);
372                 if (error != 0)
373                         break;
374                 error = kern_ptrace(td, PT_STEP, pid, (void *)1, sig);
375                 break;
376         case LINUX_PTRACE_GETREGS:
377                 error = linux_ptrace_getregs(td, pid, (void *)uap->data);
378                 break;
379         case LINUX_PTRACE_SETREGS:
380                 error = linux_ptrace_setregs(td, pid, (void *)uap->data);
381                 break;
382         case LINUX_PTRACE_ATTACH:
383                 error = kern_ptrace(td, PT_ATTACH, pid, addr, uap->data);
384                 break;
385         case LINUX_PTRACE_DETACH:
386                 error = map_signum(uap->data, &sig);
387                 if (error != 0)
388                         break;
389                 error = kern_ptrace(td, PT_DETACH, pid, (void *)1, sig);
390                 break;
391         case LINUX_PTRACE_SYSCALL:
392                 error = map_signum(uap->data, &sig);
393                 if (error != 0)
394                         break;
395                 error = kern_ptrace(td, PT_SYSCALL, pid, (void *)1, sig);
396                 break;
397         case LINUX_PTRACE_SETOPTIONS:
398                 error = linux_ptrace_setoptions(td, pid, uap->data);
399                 break;
400         case LINUX_PTRACE_GETREGSET:
401                 error = linux_ptrace_getregset(td, pid, uap->addr, uap->data);
402                 break;
403         case LINUX_PTRACE_SEIZE:
404                 error = linux_ptrace_seize(td, pid, uap->addr, uap->data);
405                 break;
406         default:
407                 printf("%s: ptrace(%ld, ...) not implemented; returning EINVAL\n",
408                     __func__, uap->req);
409                 error = EINVAL;
410                 break;
411         }
412
413         return (error);
414 }