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