xref: /freebsd-12.1/sys/amd64/linux/linux_ptrace.c (revision 132f90c6)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2017 Edward Tomasz Napierala <[email protected]>
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
map_signum(int lsig,int * bsigp)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
map_regs_to_linux(struct reg * b_reg,struct linux_pt_reg * l_reg)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
map_regs_from_linux(struct reg * b_reg,struct linux_pt_reg * l_reg)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
linux_ptrace_peek(struct thread * td,pid_t pid,void * addr,void * data)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
linux_ptrace_setoptions(struct thread * td,pid_t pid,l_ulong data)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
linux_ptrace_getregs(struct thread * td,pid_t pid,void * data)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
linux_ptrace_setregs(struct thread * td,pid_t pid,void * data)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
linux_ptrace_getregset(struct thread * td,pid_t pid,l_ulong addr,l_ulong data)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
linux_ptrace_seize(struct thread * td,pid_t pid,l_ulong addr,l_ulong data)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
linux_ptrace(struct thread * td,struct linux_ptrace_args * uap)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 }
417