1/*	$NetBSD: linux_ptrace.c,v 1.34 2022/09/05 14:14:42 tsutsui Exp $ */
2
3/*-
4 * Copyright (c) 1999, 2001 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Matthias Scheler and Emmanuel Dreyfus.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include <sys/cdefs.h>
33__KERNEL_RCSID(0, "$NetBSD: linux_ptrace.c,v 1.34 2022/09/05 14:14:42 tsutsui Exp $");
34
35#include <sys/param.h>
36#include <sys/mount.h>
37#include <sys/proc.h>
38#include <sys/ptrace.h>
39#include <sys/systm.h>
40#include <sys/syscallargs.h>
41#include <uvm/uvm_extern.h>
42
43#include <machine/reg.h>
44
45#include <compat/linux/common/linux_types.h>
46#include <compat/linux/common/linux_ptrace.h>
47#include <compat/linux/common/linux_signal.h>
48
49#include <compat/linux/common/linux_util.h>
50#include <compat/linux/common/linux_machdep.h>
51#include <compat/linux/common/linux_emuldata.h>
52#include <compat/linux/common/linux_exec.h>	/* for emul_linux */
53
54#include <compat/linux/linux_syscallargs.h>
55
56#include <lib/libkern/libkern.h>	/* for offsetof() */
57
58/*
59 * From Linux's include/asm-ppc/ptrace.h.
60 * structure used for storing reg context:  defined in linux_machdep.h
61 * structure used for storing floating point context:
62 * Linux just uses a double fpr[32], no struct
63 */
64
65/*
66 * user struct for linux process - this is used for Linux ptrace emulation
67 * most of it is junk only used by gdb
68 *
69 * From Linux's include/asm-ppc/user.h
70 *
71 * XXX u_ar0 was a struct reg in Linux/powerpc
72 * Can't find out what a struct regs is for Linux/powerpc,
73 * so we use a struct pt_regs instead. don't know if this is right.
74 */
75
76struct linux_user {
77	struct linux_pt_regs regs;
78#define lusr_startgdb regs
79	size_t u_tsize;
80	size_t u_dsize;
81	size_t u_ssize;
82	unsigned long start_code;
83	unsigned long start_data;
84	unsigned long start_stack;
85	long int signal;
86	struct linux_pt_regs *u_ar0;		/* help gdb find registers */
87	unsigned long magic;
88	char u_comm[32];
89#define lu_comm_end u_comm[31]
90};
91
92#define LUSR_OFF(member) offsetof(struct linux_user, member)
93#define LUSR_REG_OFF(member) offsetof(struct linux_pt_regs, member)
94
95int linux_ptrace_disabled = 1;	/* bitrotted */
96
97/* XXX Check me! (From NetBSD/i386) */
98int
99linux_sys_ptrace_arch(struct lwp *l, const struct linux_sys_ptrace_args *uap,
100    register_t *retval)
101{
102	/* {
103		syscallarg(int) request;
104		syscallarg(int) pid;
105		syscallarg(int) addr;
106		syscallarg(int) data;
107	} */
108	struct proc *p = l->l_proc, *t;
109	struct lwp *lt;
110	struct reg *regs = NULL;
111	struct fpreg *fpregs = NULL;
112	struct linux_pt_regs *linux_regs = NULL;
113	double *linux_fpreg = NULL;  /* it's an array, not a struct */
114	int request, error, addr, i;
115
116	if (linux_ptrace_disabled)
117		return ENOSYS;
118
119	error = 0;
120	request = SCARG(uap, request);
121
122	switch (request) {
123	case LINUX_PTRACE_PEEKUSR:
124	case LINUX_PTRACE_POKEUSR:
125		regs = kmem_alloc(sizeof(struct reg), KM_SLEEP);
126		break;
127	case LINUX_PTRACE_GETREGS:
128	case LINUX_PTRACE_SETREGS:
129		regs = kmem_alloc(sizeof(struct reg), KM_SLEEP);
130		linux_regs = kmem_alloc(sizeof(*linux_regs), KM_SLEEP);
131		if (request == LINUX_PTRACE_SETREGS) {
132			error = copyin((void *)SCARG(uap, data), linux_regs,
133			    sizeof(struct linux_pt_regs));
134			if (error) {
135				goto out;
136			}
137		}
138		break;
139	case LINUX_PTRACE_GETFPREGS:
140	case LINUX_PTRACE_SETFPREGS:
141		fpregs = kmem_alloc(sizeof(struct fpreg), KM_SLEEP);
142		linux_fpreg = kmem_alloc(32 * sizeof(double), KM_SLEEP);
143		if (request == LINUX_PTRACE_SETFPREGS) {
144			error = copyin((void *)SCARG(uap, data), linux_fpreg,
145			    32 * sizeof(double));
146			if (error) {
147				goto out;
148			}
149		}
150		break;
151	default:
152		error = EIO;
153		goto out;
154	}
155
156	/* Find the process we are supposed to be operating on. */
157	mutex_enter(&proc_lock);
158	if ((t = proc_find(SCARG(uap, pid))) == NULL) {
159		mutex_exit(&proc_lock);
160		error = ESRCH;
161		goto out;
162	}
163	mutex_enter(t->p_lock);
164
165	/*
166	 * You cannot do what you want to the process if:
167	 * 1. It is not being traced at all,
168	 */
169	if (!ISSET(t->p_slflag, PSL_TRACED)) {
170		mutex_exit(t->p_lock);
171		mutex_exit(&proc_lock);
172		error = EPERM;
173		goto out;
174	}
175	/*
176	 * 2. It is not being traced by _you_, or
177	 * 3. It is not currently stopped.
178	 */
179	if (t->p_pptr != p || t->p_stat != SSTOP || !t->p_waited) {
180		mutex_exit(t->p_lock);
181		mutex_exit(&proc_lock);
182		error = EBUSY;
183		goto out;
184	}
185	mutex_exit(&proc_lock);
186	/* XXX: ptrace needs revamp for multi-threading support. */
187	if (t->p_nlwps > 1) {
188		mutex_exit(t->p_lock);
189		error = ENOSYS;
190		goto out;
191	}
192	lt = LIST_FIRST(&t->p_lwps);
193	*retval = 0;
194
195	switch (request) {
196	case LINUX_PTRACE_GETREGS:
197		error = process_read_regs(lt, regs);
198		mutex_exit(t->p_lock);
199		if (error) {
200			break;
201		}
202		memset(linux_regs, 0, sizeof(*linux_regs));
203		for (i = 0; i <= 31; i++) {
204			linux_regs->lgpr[i] = regs->fixreg[i];
205		}
206		linux_regs->lnip = regs->pc;
207		linux_regs->lmsr = 0;
208		/* XXX Is that right? */
209		linux_regs->lorig_gpr3 = regs->fixreg[3];
210		linux_regs->lctr = regs->ctr;
211		linux_regs->llink = regs->lr;
212		linux_regs->lxer = regs->xer;
213		linux_regs->lccr = regs->cr;
214		linux_regs->lmq = 0;
215		linux_regs->ltrap = 0;
216		linux_regs->ldar = 0;
217		linux_regs->ldsisr = 0;
218		linux_regs->lresult = 0;
219
220		error = copyout(linux_regs, (void *)SCARG(uap, data),
221		    sizeof(struct linux_pt_regs));
222		break;
223
224	case LINUX_PTRACE_SETREGS:
225		for (i = 0; i <= 31; i++) {
226			regs->fixreg[i] = linux_regs->lgpr[i];
227		}
228		regs->lr = linux_regs->llink;
229		regs->cr = linux_regs->lccr;
230		regs->xer = linux_regs->lxer;
231		regs->ctr = linux_regs->lctr;
232		regs->pc = linux_regs->lnip; /* XXX */
233
234		error = process_write_regs(lt, regs);
235		mutex_exit(t->p_lock);
236		break;
237
238	case LINUX_PTRACE_GETFPREGS:
239		error = process_read_fpregs(lt, fpregs, NULL);
240		mutex_exit(t->p_lock);
241		if (error) {
242			break;
243		}
244		/* Zero the contents if NetBSD fpreg structure is smaller */
245		if (sizeof(struct fpreg) < (32 * sizeof(double))) {
246			memset(linux_fpreg, '\0', (32 * sizeof(double)));
247		}
248		memcpy(linux_fpreg, fpregs,
249		    uimin(32 * sizeof(double), sizeof(struct fpreg)));
250		error = copyout(linux_fpreg, (void *)SCARG(uap, data),
251		    32 * sizeof(double));
252		break;
253
254	case LINUX_PTRACE_SETFPREGS:
255		memset(fpregs, '\0', sizeof(struct fpreg));
256		memcpy(fpregs, linux_fpreg,
257		    uimin(32 * sizeof(double), sizeof(struct fpreg)));
258		error = process_write_fpregs(lt, fpregs, sizeof fpregs);
259		mutex_exit(t->p_lock);
260		break;
261
262	case LINUX_PTRACE_PEEKUSR:
263		addr = SCARG(uap, addr);
264		error = process_read_regs(lt, regs);
265		mutex_exit(t->p_lock);
266		if (error) {
267			break;
268		}
269		error = 0;
270		if ((addr < LUSR_OFF(lusr_startgdb)) ||
271		    (addr > LUSR_OFF(lu_comm_end)))
272		 	error = 1;
273		else if (addr == LUSR_OFF(u_tsize))
274			*retval = p->p_vmspace->vm_tsize;
275		else if (addr == LUSR_OFF(u_dsize))
276			*retval = p->p_vmspace->vm_dsize;
277		else if (addr == LUSR_OFF(u_ssize))
278			*retval = p->p_vmspace->vm_ssize;
279		else if (addr == LUSR_OFF(start_code))
280			*retval = (register_t) p->p_vmspace->vm_taddr;
281		else if (addr == LUSR_OFF(start_stack))
282			*retval = (register_t) p->p_vmspace->vm_minsaddr;
283		else if ((addr >= LUSR_REG_OFF(lpt_regs_fixreg_begin)) &&
284		    (addr <= LUSR_REG_OFF(lpt_regs_fixreg_end)))
285			*retval = regs->fixreg[addr / sizeof (register_t)];
286		else if (addr == LUSR_REG_OFF(lnip))
287			*retval = regs->pc;
288		else if (addr == LUSR_REG_OFF(lctr))
289			*retval = regs->ctr;
290		else if (addr == LUSR_REG_OFF(llink))
291			*retval = regs->lr;
292		else if (addr == LUSR_REG_OFF(lxer))
293			*retval = regs->xer;
294		else if (addr == LUSR_REG_OFF(lccr))
295			*retval = regs->cr;
296		else if (addr == LUSR_OFF(signal))
297			error = 1;
298		else if (addr == LUSR_OFF(magic))
299			error = 1;
300		else if (addr == LUSR_OFF(u_comm))
301			error = 1;
302		else {
303#ifdef DEBUG_LINUX
304			printf("linux_ptrace: unsupported address: %d\n", addr);
305#endif
306			error = 1;
307		}
308		if (error) {
309			break;
310		}
311		error = copyout (retval, (void *)SCARG(uap, data),
312		    sizeof(*retval));
313		*retval = SCARG(uap, data);
314		break;
315
316	case  LINUX_PTRACE_POKEUSR: /* XXX Not tested */
317		addr = SCARG(uap, addr);
318		error = process_read_regs(lt, regs);
319		if (error) {
320			mutex_exit(t->p_lock);
321			break;
322		}
323		error = 0;
324		if ((addr < LUSR_OFF(lusr_startgdb)) ||
325		    (addr > LUSR_OFF(lu_comm_end)))
326		 	error = 1;
327		else if (addr == LUSR_OFF(u_tsize))
328			error = 1;
329		else if (addr == LUSR_OFF(u_dsize))
330			error = 1;
331		else if (addr == LUSR_OFF(u_ssize))
332			error = 1;
333		else if (addr == LUSR_OFF(start_code))
334			error = 1;
335		else if (addr == LUSR_OFF(start_stack))
336			error = 1;
337		else if ((addr >= LUSR_REG_OFF(lpt_regs_fixreg_begin)) &&
338		    (addr <= LUSR_REG_OFF(lpt_regs_fixreg_end)))
339			regs->fixreg[addr / sizeof (register_t)] =
340			    (register_t)SCARG(uap, data);
341		else if (addr == LUSR_REG_OFF(lnip))
342			regs->pc = (register_t)SCARG(uap, data);
343		else if (addr == LUSR_REG_OFF(lctr))
344			regs->ctr = (register_t)SCARG(uap, data);
345		else if (addr == LUSR_REG_OFF(llink))
346			regs->lr = (register_t)SCARG(uap, data);
347		else if (addr == LUSR_REG_OFF(lxer))
348			regs->xer = (register_t)SCARG(uap, data);
349		else if (addr == LUSR_REG_OFF(lccr))
350			regs->cr = (register_t)SCARG(uap, data);
351		else if (addr == LUSR_OFF(signal))
352			error = 1;
353		else if (addr == LUSR_OFF(magic))
354			error = 1;
355		else if (addr == LUSR_OFF(u_comm))
356			error = 1;
357		else {
358#ifdef DEBUG_LINUX
359			printf("linux_ptrace: unsupported address: %d\n", addr);
360#endif
361			error = 1;
362		}
363		error = process_write_regs(lt,regs);
364		mutex_exit(t->p_lock);
365		break;
366	default:
367		mutex_exit(t->p_lock);
368		break;
369	}
370out:
371	if (regs)
372		kmem_free(regs, sizeof(*regs));
373	if (fpregs)
374		kmem_free(fpregs, sizeof(*fpregs));
375	if (linux_regs)
376		kmem_free(linux_regs, sizeof(*linux_regs));
377	if (linux_fpreg)
378		kmem_free(linux_fpreg, sizeof(*linux_fpreg));
379	return error;
380}
381