xref: /freebsd-13.1/sys/amd64/linux/linux_ptrace.c (revision a5d8db32)
1 /*-
2  * Copyright (c) 2017 Edward Tomasz Napierala <[email protected]>
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 }
415