xref: /freebsd/sys/i386/linux/linux_ptrace_machdep.c (revision 4dfd612286ec2ecc7a05f3d4fd65a58f4b05f74c)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2001 Alexander Kabaev
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31 
32 #include "opt_cpu.h"
33 
34 #include <sys/param.h>
35 #include <sys/lock.h>
36 #include <sys/mutex.h>
37 #include <sys/proc.h>
38 #include <sys/ptrace.h>
39 #include <sys/syscallsubr.h>
40 #include <sys/systm.h>
41 
42 #include <machine/md_var.h>
43 #include <machine/pcb.h>
44 
45 #include <i386/linux/linux.h>
46 #include <i386/linux/linux_proto.h>
47 #include <compat/linux/linux_signal.h>
48 
49 /*
50  *   Linux ptrace requests numbers. Mostly identical to FreeBSD,
51  *   except for MD ones and PT_ATTACH/PT_DETACH.
52  */
53 #define	PTRACE_TRACEME		0
54 #define	PTRACE_PEEKTEXT		1
55 #define	PTRACE_PEEKDATA		2
56 #define	PTRACE_PEEKUSR		3
57 #define	PTRACE_POKETEXT		4
58 #define	PTRACE_POKEDATA		5
59 #define	PTRACE_POKEUSR		6
60 #define	PTRACE_CONT		7
61 #define	PTRACE_KILL		8
62 #define	PTRACE_SINGLESTEP	9
63 
64 #define PTRACE_ATTACH		16
65 #define PTRACE_DETACH		17
66 
67 #define	LINUX_PTRACE_SYSCALL	24
68 
69 #define PTRACE_GETREGS		12
70 #define PTRACE_SETREGS		13
71 #define PTRACE_GETFPREGS	14
72 #define PTRACE_SETFPREGS	15
73 #define PTRACE_GETFPXREGS	18
74 #define PTRACE_SETFPXREGS	19
75 
76 #define PTRACE_SETOPTIONS	21
77 
78 /*
79  * Linux keeps debug registers at the following
80  * offset in the user struct
81  */
82 #define LINUX_DBREG_OFFSET	252
83 #define LINUX_DBREG_SIZE	(8*sizeof(l_int))
84 
85 static __inline int
86 map_signum(int signum)
87 {
88 
89 	signum = linux_to_bsd_signal(signum);
90 	return ((signum == SIGSTOP)? 0 : signum);
91 }
92 
93 struct linux_pt_reg {
94 	l_long	ebx;
95 	l_long	ecx;
96 	l_long	edx;
97 	l_long	esi;
98 	l_long	edi;
99 	l_long	ebp;
100 	l_long	eax;
101 	l_int	xds;
102 	l_int	xes;
103 	l_int	xfs;
104 	l_int	xgs;
105 	l_long	orig_eax;
106 	l_long	eip;
107 	l_int	xcs;
108 	l_long	eflags;
109 	l_long	esp;
110 	l_int	xss;
111 };
112 
113 /*
114  *   Translate i386 ptrace registers between Linux and FreeBSD formats.
115  *   The translation is pretty straighforward, for all registers, but
116  *   orig_eax on Linux side and r_trapno and r_err in FreeBSD
117  */
118 static void
119 map_regs_to_linux(struct reg *bsd_r, struct linux_pt_reg *linux_r)
120 {
121 	linux_r->ebx = bsd_r->r_ebx;
122 	linux_r->ecx = bsd_r->r_ecx;
123 	linux_r->edx = bsd_r->r_edx;
124 	linux_r->esi = bsd_r->r_esi;
125 	linux_r->edi = bsd_r->r_edi;
126 	linux_r->ebp = bsd_r->r_ebp;
127 	linux_r->eax = bsd_r->r_eax;
128 	linux_r->xds = bsd_r->r_ds;
129 	linux_r->xes = bsd_r->r_es;
130 	linux_r->xfs = bsd_r->r_fs;
131 	linux_r->xgs = bsd_r->r_gs;
132 	linux_r->orig_eax = bsd_r->r_eax;
133 	linux_r->eip = bsd_r->r_eip;
134 	linux_r->xcs = bsd_r->r_cs;
135 	linux_r->eflags = bsd_r->r_eflags;
136 	linux_r->esp = bsd_r->r_esp;
137 	linux_r->xss = bsd_r->r_ss;
138 }
139 
140 static void
141 map_regs_from_linux(struct reg *bsd_r, struct linux_pt_reg *linux_r)
142 {
143 	bsd_r->r_ebx = linux_r->ebx;
144 	bsd_r->r_ecx = linux_r->ecx;
145 	bsd_r->r_edx = linux_r->edx;
146 	bsd_r->r_esi = linux_r->esi;
147 	bsd_r->r_edi = linux_r->edi;
148 	bsd_r->r_ebp = linux_r->ebp;
149 	bsd_r->r_eax = linux_r->eax;
150 	bsd_r->r_ds  = linux_r->xds;
151 	bsd_r->r_es  = linux_r->xes;
152 	bsd_r->r_fs  = linux_r->xfs;
153 	bsd_r->r_gs  = linux_r->xgs;
154 	bsd_r->r_eip = linux_r->eip;
155 	bsd_r->r_cs  = linux_r->xcs;
156 	bsd_r->r_eflags = linux_r->eflags;
157 	bsd_r->r_esp = linux_r->esp;
158 	bsd_r->r_ss = linux_r->xss;
159 }
160 
161 struct linux_pt_fpreg {
162 	l_long cwd;
163 	l_long swd;
164 	l_long twd;
165 	l_long fip;
166 	l_long fcs;
167 	l_long foo;
168 	l_long fos;
169 	l_long st_space[2*10];
170 };
171 
172 static void
173 map_fpregs_to_linux(struct fpreg *bsd_r, struct linux_pt_fpreg *linux_r)
174 {
175 	linux_r->cwd = bsd_r->fpr_env[0];
176 	linux_r->swd = bsd_r->fpr_env[1];
177 	linux_r->twd = bsd_r->fpr_env[2];
178 	linux_r->fip = bsd_r->fpr_env[3];
179 	linux_r->fcs = bsd_r->fpr_env[4];
180 	linux_r->foo = bsd_r->fpr_env[5];
181 	linux_r->fos = bsd_r->fpr_env[6];
182 	bcopy(bsd_r->fpr_acc, linux_r->st_space, sizeof(linux_r->st_space));
183 }
184 
185 static void
186 map_fpregs_from_linux(struct fpreg *bsd_r, struct linux_pt_fpreg *linux_r)
187 {
188 	bsd_r->fpr_env[0] = linux_r->cwd;
189 	bsd_r->fpr_env[1] = linux_r->swd;
190 	bsd_r->fpr_env[2] = linux_r->twd;
191 	bsd_r->fpr_env[3] = linux_r->fip;
192 	bsd_r->fpr_env[4] = linux_r->fcs;
193 	bsd_r->fpr_env[5] = linux_r->foo;
194 	bsd_r->fpr_env[6] = linux_r->fos;
195 	bcopy(bsd_r->fpr_acc, linux_r->st_space, sizeof(bsd_r->fpr_acc));
196 }
197 
198 struct linux_pt_fpxreg {
199 	l_ushort	cwd;
200 	l_ushort	swd;
201 	l_ushort	twd;
202 	l_ushort	fop;
203 	l_long		fip;
204 	l_long		fcs;
205 	l_long		foo;
206 	l_long		fos;
207 	l_long		mxcsr;
208 	l_long		reserved;
209 	l_long		st_space[32];
210 	l_long		xmm_space[32];
211 	l_long		padding[56];
212 };
213 
214 static int
215 linux_proc_read_fpxregs(struct thread *td, struct linux_pt_fpxreg *fpxregs)
216 {
217 
218 	PROC_LOCK_ASSERT(td->td_proc, MA_OWNED);
219 	if (cpu_fxsr == 0 || (td->td_proc->p_flag & P_INMEM) == 0)
220 		return (EIO);
221 	bcopy(&get_pcb_user_save_td(td)->sv_xmm, fpxregs, sizeof(*fpxregs));
222 	return (0);
223 }
224 
225 static int
226 linux_proc_write_fpxregs(struct thread *td, struct linux_pt_fpxreg *fpxregs)
227 {
228 
229 	PROC_LOCK_ASSERT(td->td_proc, MA_OWNED);
230 	if (cpu_fxsr == 0 || (td->td_proc->p_flag & P_INMEM) == 0)
231 		return (EIO);
232 	bcopy(fpxregs, &get_pcb_user_save_td(td)->sv_xmm, sizeof(*fpxregs));
233 	return (0);
234 }
235 
236 int
237 linux_ptrace(struct thread *td, struct linux_ptrace_args *uap)
238 {
239 	union {
240 		struct linux_pt_reg	reg;
241 		struct linux_pt_fpreg	fpreg;
242 		struct linux_pt_fpxreg	fpxreg;
243 	} r;
244 	union {
245 		struct reg		bsd_reg;
246 		struct fpreg		bsd_fpreg;
247 		struct dbreg		bsd_dbreg;
248 	} u;
249 	void *addr;
250 	pid_t pid;
251 	int error, req;
252 
253 	error = 0;
254 
255 	/* by default, just copy data intact */
256 	req  = uap->req;
257 	pid  = (pid_t)uap->pid;
258 	addr = (void *)uap->addr;
259 
260 	switch (req) {
261 	case PTRACE_TRACEME:
262 	case PTRACE_POKETEXT:
263 	case PTRACE_POKEDATA:
264 	case PTRACE_KILL:
265 		error = kern_ptrace(td, req, pid, addr, uap->data);
266 		break;
267 	case PTRACE_PEEKTEXT:
268 	case PTRACE_PEEKDATA: {
269 		/* need to preserve return value */
270 		int rval = td->td_retval[0];
271 		error = kern_ptrace(td, req, pid, addr, 0);
272 		if (error == 0)
273 			error = copyout(td->td_retval, (void *)uap->data,
274 			    sizeof(l_int));
275 		td->td_retval[0] = rval;
276 		break;
277 	}
278 	case PTRACE_DETACH:
279 		error = kern_ptrace(td, PT_DETACH, pid, (void *)1,
280 		     map_signum(uap->data));
281 		break;
282 	case PTRACE_SINGLESTEP:
283 	case PTRACE_CONT:
284 		error = kern_ptrace(td, req, pid, (void *)1,
285 		     map_signum(uap->data));
286 		break;
287 	case PTRACE_ATTACH:
288 		error = kern_ptrace(td, PT_ATTACH, pid, addr, uap->data);
289 		break;
290 	case PTRACE_GETREGS:
291 		/* Linux is using data where FreeBSD is using addr */
292 		error = kern_ptrace(td, PT_GETREGS, pid, &u.bsd_reg, 0);
293 		if (error == 0) {
294 			map_regs_to_linux(&u.bsd_reg, &r.reg);
295 			error = copyout(&r.reg, (void *)uap->data,
296 			    sizeof(r.reg));
297 		}
298 		break;
299 	case PTRACE_SETREGS:
300 		/* Linux is using data where FreeBSD is using addr */
301 		error = copyin((void *)uap->data, &r.reg, sizeof(r.reg));
302 		if (error == 0) {
303 			map_regs_from_linux(&u.bsd_reg, &r.reg);
304 			error = kern_ptrace(td, PT_SETREGS, pid, &u.bsd_reg, 0);
305 		}
306 		break;
307 	case PTRACE_GETFPREGS:
308 		/* Linux is using data where FreeBSD is using addr */
309 		error = kern_ptrace(td, PT_GETFPREGS, pid, &u.bsd_fpreg, 0);
310 		if (error == 0) {
311 			map_fpregs_to_linux(&u.bsd_fpreg, &r.fpreg);
312 			error = copyout(&r.fpreg, (void *)uap->data,
313 			    sizeof(r.fpreg));
314 		}
315 		break;
316 	case PTRACE_SETFPREGS:
317 		/* Linux is using data where FreeBSD is using addr */
318 		error = copyin((void *)uap->data, &r.fpreg, sizeof(r.fpreg));
319 		if (error == 0) {
320 			map_fpregs_from_linux(&u.bsd_fpreg, &r.fpreg);
321 			error = kern_ptrace(td, PT_SETFPREGS, pid,
322 			    &u.bsd_fpreg, 0);
323 		}
324 		break;
325 	case PTRACE_SETFPXREGS:
326 		error = copyin((void *)uap->data, &r.fpxreg, sizeof(r.fpxreg));
327 		if (error)
328 			break;
329 		/* FALL THROUGH */
330 	case PTRACE_GETFPXREGS: {
331 		struct proc *p;
332 		struct thread *td2;
333 
334 		if (sizeof(struct linux_pt_fpxreg) != sizeof(struct savexmm)) {
335 			static int once = 0;
336 			if (!once) {
337 				printf("linux: savexmm != linux_pt_fpxreg\n");
338 				once = 1;
339 			}
340 			error = EIO;
341 			break;
342 		}
343 
344 		if ((p = pfind(uap->pid)) == NULL) {
345 			error = ESRCH;
346 			break;
347 		}
348 
349 		/* Exiting processes can't be debugged. */
350 		if ((p->p_flag & P_WEXIT) != 0) {
351 			error = ESRCH;
352 			goto fail;
353 		}
354 
355 		if ((error = p_candebug(td, p)) != 0)
356 			goto fail;
357 
358 		/* System processes can't be debugged. */
359 		if ((p->p_flag & P_SYSTEM) != 0) {
360 			error = EINVAL;
361 			goto fail;
362 		}
363 
364 		/* not being traced... */
365 		if ((p->p_flag & P_TRACED) == 0) {
366 			error = EPERM;
367 			goto fail;
368 		}
369 
370 		/* not being traced by YOU */
371 		if (p->p_pptr != td->td_proc) {
372 			error = EBUSY;
373 			goto fail;
374 		}
375 
376 		/* not currently stopped */
377 		if (!P_SHOULDSTOP(p) || (p->p_flag & P_WAITED) == 0) {
378 			error = EBUSY;
379 			goto fail;
380 		}
381 
382 		if (req == PTRACE_GETFPXREGS) {
383 			_PHOLD(p);	/* may block */
384 			td2 = FIRST_THREAD_IN_PROC(p);
385 			error = linux_proc_read_fpxregs(td2, &r.fpxreg);
386 			_PRELE(p);
387 			PROC_UNLOCK(p);
388 			if (error == 0)
389 				error = copyout(&r.fpxreg, (void *)uap->data,
390 				    sizeof(r.fpxreg));
391 		} else {
392 			/* clear dangerous bits exactly as Linux does*/
393 			r.fpxreg.mxcsr &= 0xffbf;
394 			_PHOLD(p);	/* may block */
395 			td2 = FIRST_THREAD_IN_PROC(p);
396 			error = linux_proc_write_fpxregs(td2, &r.fpxreg);
397 			_PRELE(p);
398 			PROC_UNLOCK(p);
399 		}
400 		break;
401 
402 	fail:
403 		PROC_UNLOCK(p);
404 		break;
405 	}
406 	case PTRACE_PEEKUSR:
407 	case PTRACE_POKEUSR: {
408 		error = EIO;
409 
410 		/* check addr for alignment */
411 		if (uap->addr < 0 || uap->addr & (sizeof(l_int) - 1))
412 			break;
413 		/*
414 		 * Allow Linux programs to access register values in
415 		 * user struct. We simulate this through PT_GET/SETREGS
416 		 * as necessary.
417 		 */
418 		if (uap->addr < sizeof(struct linux_pt_reg)) {
419 			error = kern_ptrace(td, PT_GETREGS, pid, &u.bsd_reg, 0);
420 			if (error != 0)
421 				break;
422 
423 			map_regs_to_linux(&u.bsd_reg, &r.reg);
424 			if (req == PTRACE_PEEKUSR) {
425 				error = copyout((char *)&r.reg + uap->addr,
426 				    (void *)uap->data, sizeof(l_int));
427 				break;
428 			}
429 
430 			*(l_int *)((char *)&r.reg + uap->addr) =
431 			    (l_int)uap->data;
432 
433 			map_regs_from_linux(&u.bsd_reg, &r.reg);
434 			error = kern_ptrace(td, PT_SETREGS, pid, &u.bsd_reg, 0);
435 		}
436 
437 		/*
438 		 * Simulate debug registers access
439 		 */
440 		if (uap->addr >= LINUX_DBREG_OFFSET &&
441 		    uap->addr <= LINUX_DBREG_OFFSET + LINUX_DBREG_SIZE) {
442 			error = kern_ptrace(td, PT_GETDBREGS, pid, &u.bsd_dbreg,
443 			    0);
444 			if (error != 0)
445 				break;
446 
447 			uap->addr -= LINUX_DBREG_OFFSET;
448 			if (req == PTRACE_PEEKUSR) {
449 				error = copyout((char *)&u.bsd_dbreg +
450 				    uap->addr, (void *)uap->data,
451 				    sizeof(l_int));
452 				break;
453 			}
454 
455 			*(l_int *)((char *)&u.bsd_dbreg + uap->addr) =
456 			     uap->data;
457 			error = kern_ptrace(td, PT_SETDBREGS, pid,
458 			    &u.bsd_dbreg, 0);
459 		}
460 
461 		break;
462 	}
463 	case LINUX_PTRACE_SYSCALL:
464 		/* fall through */
465 	default:
466 		printf("linux: ptrace(%u, ...) not implemented\n",
467 		    (unsigned int)uap->req);
468 		error = EINVAL;
469 		break;
470 	}
471 
472 	return (error);
473 }
474