xref: /netbsd-src/sys/kern/sys_ptrace_common.c (revision 5b38d912c8c95969e4667aadbd354225a2eb73b0)
1*5b38d912Schristos /*	$NetBSD: sys_ptrace_common.c,v 1.96 2025/01/11 19:42:04 christos Exp $	*/
2a60b9909Spgoyette 
3a60b9909Spgoyette /*-
4a60b9909Spgoyette  * Copyright (c) 2008, 2009 The NetBSD Foundation, Inc.
5a60b9909Spgoyette  * All rights reserved.
6a60b9909Spgoyette  *
7a60b9909Spgoyette  * This code is derived from software contributed to The NetBSD Foundation
8a60b9909Spgoyette  * by Andrew Doran.
9a60b9909Spgoyette  *
10a60b9909Spgoyette  * Redistribution and use in source and binary forms, with or without
11a60b9909Spgoyette  * modification, are permitted provided that the following conditions
12a60b9909Spgoyette  * are met:
13a60b9909Spgoyette  * 1. Redistributions of source code must retain the above copyright
14a60b9909Spgoyette  *    notice, this list of conditions and the following disclaimer.
15a60b9909Spgoyette  * 2. Redistributions in binary form must reproduce the above copyright
16a60b9909Spgoyette  *    notice, this list of conditions and the following disclaimer in the
17a60b9909Spgoyette  *    documentation and/or other materials provided with the distribution.
18a60b9909Spgoyette  *
19a60b9909Spgoyette  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20a60b9909Spgoyette  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21a60b9909Spgoyette  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22a60b9909Spgoyette  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23a60b9909Spgoyette  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24a60b9909Spgoyette  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25a60b9909Spgoyette  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26a60b9909Spgoyette  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27a60b9909Spgoyette  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28a60b9909Spgoyette  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29a60b9909Spgoyette  * POSSIBILITY OF SUCH DAMAGE.
30a60b9909Spgoyette  */
31a60b9909Spgoyette 
32a60b9909Spgoyette /*-
33a60b9909Spgoyette  * Copyright (c) 1982, 1986, 1989, 1993
34a60b9909Spgoyette  *	The Regents of the University of California.  All rights reserved.
35a60b9909Spgoyette  * (c) UNIX System Laboratories, Inc.
36a60b9909Spgoyette  * All or some portions of this file are derived from material licensed
37a60b9909Spgoyette  * to the University of California by American Telephone and Telegraph
38a60b9909Spgoyette  * Co. or Unix System Laboratories, Inc. and are reproduced herein with
39a60b9909Spgoyette  * the permission of UNIX System Laboratories, Inc.
40a60b9909Spgoyette  *
41a60b9909Spgoyette  * This code is derived from software contributed to Berkeley by
42a60b9909Spgoyette  * Jan-Simon Pendry.
43a60b9909Spgoyette  *
44a60b9909Spgoyette  * Redistribution and use in source and binary forms, with or without
45a60b9909Spgoyette  * modification, are permitted provided that the following conditions
46a60b9909Spgoyette  * are met:
47a60b9909Spgoyette  * 1. Redistributions of source code must retain the above copyright
48a60b9909Spgoyette  *    notice, this list of conditions and the following disclaimer.
49a60b9909Spgoyette  * 2. Redistributions in binary form must reproduce the above copyright
50a60b9909Spgoyette  *    notice, this list of conditions and the following disclaimer in the
51a60b9909Spgoyette  *    documentation and/or other materials provided with the distribution.
52a60b9909Spgoyette  * 3. Neither the name of the University nor the names of its contributors
53a60b9909Spgoyette  *    may be used to endorse or promote products derived from this software
54a60b9909Spgoyette  *    without specific prior written permission.
55a60b9909Spgoyette  *
56a60b9909Spgoyette  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
57a60b9909Spgoyette  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
58a60b9909Spgoyette  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
59a60b9909Spgoyette  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
60a60b9909Spgoyette  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
61a60b9909Spgoyette  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
62a60b9909Spgoyette  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
63a60b9909Spgoyette  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
64a60b9909Spgoyette  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
65a60b9909Spgoyette  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
66a60b9909Spgoyette  * SUCH DAMAGE.
67a60b9909Spgoyette  *
68a60b9909Spgoyette  *	from: @(#)sys_process.c	8.1 (Berkeley) 6/10/93
69a60b9909Spgoyette  */
70a60b9909Spgoyette 
71a60b9909Spgoyette /*-
72a60b9909Spgoyette  * Copyright (c) 1993 Jan-Simon Pendry.
73a60b9909Spgoyette  * Copyright (c) 1994 Christopher G. Demetriou.  All rights reserved.
74a60b9909Spgoyette  *
75a60b9909Spgoyette  * This code is derived from software contributed to Berkeley by
76a60b9909Spgoyette  * Jan-Simon Pendry.
77a60b9909Spgoyette  *
78a60b9909Spgoyette  * Redistribution and use in source and binary forms, with or without
79a60b9909Spgoyette  * modification, are permitted provided that the following conditions
80a60b9909Spgoyette  * are met:
81a60b9909Spgoyette  * 1. Redistributions of source code must retain the above copyright
82a60b9909Spgoyette  *    notice, this list of conditions and the following disclaimer.
83a60b9909Spgoyette  * 2. Redistributions in binary form must reproduce the above copyright
84a60b9909Spgoyette  *    notice, this list of conditions and the following disclaimer in the
85a60b9909Spgoyette  *    documentation and/or other materials provided with the distribution.
86a60b9909Spgoyette  * 3. All advertising materials mentioning features or use of this software
87a60b9909Spgoyette  *    must display the following acknowledgement:
88a60b9909Spgoyette  *	This product includes software developed by the University of
89a60b9909Spgoyette  *	California, Berkeley and its contributors.
90a60b9909Spgoyette  * 4. Neither the name of the University nor the names of its contributors
91a60b9909Spgoyette  *    may be used to endorse or promote products derived from this software
92a60b9909Spgoyette  *    without specific prior written permission.
93a60b9909Spgoyette  *
94a60b9909Spgoyette  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
95a60b9909Spgoyette  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
96a60b9909Spgoyette  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
97a60b9909Spgoyette  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
98a60b9909Spgoyette  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
99a60b9909Spgoyette  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
100a60b9909Spgoyette  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
101a60b9909Spgoyette  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
102a60b9909Spgoyette  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
103a60b9909Spgoyette  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
104a60b9909Spgoyette  * SUCH DAMAGE.
105a60b9909Spgoyette  *
106a60b9909Spgoyette  *	from: @(#)sys_process.c	8.1 (Berkeley) 6/10/93
107a60b9909Spgoyette  */
108a60b9909Spgoyette 
109a60b9909Spgoyette #include <sys/cdefs.h>
110*5b38d912Schristos __KERNEL_RCSID(0, "$NetBSD: sys_ptrace_common.c,v 1.96 2025/01/11 19:42:04 christos Exp $");
111a60b9909Spgoyette 
112a60b9909Spgoyette #ifdef _KERNEL_OPT
113a60b9909Spgoyette #include "opt_ptrace.h"
114a60b9909Spgoyette #include "opt_ktrace.h"
115a60b9909Spgoyette #include "opt_pax.h"
1162b14b22eSchristos #include "opt_compat_netbsd32.h"
117a60b9909Spgoyette #endif
118a60b9909Spgoyette 
119d6abd869Schristos #if defined(__HAVE_COMPAT_NETBSD32) && !defined(COMPAT_NETBSD32) \
120d6abd869Schristos     && !defined(_RUMPKERNEL)
121d6abd869Schristos #define COMPAT_NETBSD32
122d6abd869Schristos #endif
123d6abd869Schristos 
124a60b9909Spgoyette #include <sys/param.h>
125a60b9909Spgoyette #include <sys/systm.h>
126a60b9909Spgoyette #include <sys/proc.h>
127a60b9909Spgoyette #include <sys/errno.h>
128a60b9909Spgoyette #include <sys/exec.h>
129a60b9909Spgoyette #include <sys/pax.h>
130a60b9909Spgoyette #include <sys/ptrace.h>
131a60b9909Spgoyette #include <sys/uio.h>
132a60b9909Spgoyette #include <sys/ras.h>
133a60b9909Spgoyette #include <sys/kmem.h>
134a60b9909Spgoyette #include <sys/kauth.h>
135a60b9909Spgoyette #include <sys/mount.h>
136a60b9909Spgoyette #include <sys/syscallargs.h>
137484b4a02Skamil #include <sys/module.h>
138a60b9909Spgoyette #include <sys/condvar.h>
139a60b9909Spgoyette #include <sys/mutex.h>
14073844747Spgoyette #include <sys/compat_stub.h>
141a60b9909Spgoyette 
142a60b9909Spgoyette #include <uvm/uvm_extern.h>
143a60b9909Spgoyette 
144a60b9909Spgoyette #include <machine/reg.h>
145a60b9909Spgoyette 
146d2ef544dSchristos #ifdef PTRACE_DEBUG
147a60b9909Spgoyette # define DPRINTF(a) uprintf a
14834fd0d8aSchristos static const char *pt_strings[] = { PT_STRINGS };
149a60b9909Spgoyette #else
150a60b9909Spgoyette # define DPRINTF(a)
151a60b9909Spgoyette #endif
152a60b9909Spgoyette 
153a60b9909Spgoyette static kauth_listener_t ptrace_listener;
154a60b9909Spgoyette static int process_auxv_offset(struct proc *, struct uio *);
155a60b9909Spgoyette 
15671c21268Skamil extern int user_va0_disable;
15771c21268Skamil 
158a60b9909Spgoyette #if 0
159a60b9909Spgoyette static int ptrace_cbref;
160a60b9909Spgoyette static kmutex_t ptrace_mtx;
161a60b9909Spgoyette static kcondvar_t ptrace_cv;
162a60b9909Spgoyette #endif
163a60b9909Spgoyette 
16428f60f0eSchristos #ifdef PT_GETREGS
16528f60f0eSchristos # define case_PT_GETREGS	case PT_GETREGS:
16628f60f0eSchristos #else
16728f60f0eSchristos # define case_PT_GETREGS
16828f60f0eSchristos #endif
16928f60f0eSchristos 
17028f60f0eSchristos #ifdef PT_SETREGS
17128f60f0eSchristos # define case_PT_SETREGS	case PT_SETREGS:
17228f60f0eSchristos #else
17328f60f0eSchristos # define case_PT_SETREGS
17428f60f0eSchristos #endif
17528f60f0eSchristos 
17628f60f0eSchristos #ifdef PT_GETFPREGS
17728f60f0eSchristos # define case_PT_GETFPREGS	case PT_GETFPREGS:
17828f60f0eSchristos #else
17928f60f0eSchristos # define case_PT_GETFPREGS
18028f60f0eSchristos #endif
18128f60f0eSchristos 
18228f60f0eSchristos #ifdef PT_SETFPREGS
18328f60f0eSchristos # define case_PT_SETFPREGS	case PT_SETFPREGS:
18428f60f0eSchristos #else
18528f60f0eSchristos # define case_PT_SETFPREGS
18628f60f0eSchristos #endif
18728f60f0eSchristos 
18828f60f0eSchristos #ifdef PT_GETDBREGS
18928f60f0eSchristos # define case_PT_GETDBREGS	case PT_GETDBREGS:
19028f60f0eSchristos #else
19128f60f0eSchristos # define case_PT_GETDBREGS
19228f60f0eSchristos #endif
19328f60f0eSchristos 
19428f60f0eSchristos #ifdef PT_SETDBREGS
19528f60f0eSchristos # define case_PT_SETDBREGS	case PT_SETDBREGS:
19628f60f0eSchristos #else
19728f60f0eSchristos # define case_PT_SETDBREGS
19828f60f0eSchristos #endif
19928f60f0eSchristos 
200a60b9909Spgoyette static int
201a60b9909Spgoyette ptrace_listener_cb(kauth_cred_t cred, kauth_action_t action, void *cookie,
202a60b9909Spgoyette     void *arg0, void *arg1, void *arg2, void *arg3)
203a60b9909Spgoyette {
204a60b9909Spgoyette 	struct proc *p;
205a60b9909Spgoyette 	int result;
206615c430aSkamil #ifdef PT_SETDBREGS
207615c430aSkamil 	extern int user_set_dbregs;
208615c430aSkamil #endif
209a60b9909Spgoyette 
210a60b9909Spgoyette 	result = KAUTH_RESULT_DEFER;
211a60b9909Spgoyette 	p = arg0;
212a60b9909Spgoyette 
213a60b9909Spgoyette #if 0
214a60b9909Spgoyette 	mutex_enter(&ptrace_mtx);
215a60b9909Spgoyette 	ptrace_cbref++;
216a60b9909Spgoyette 	mutex_exit(&ptrace_mtx);
217a60b9909Spgoyette #endif
218a60b9909Spgoyette 	if (action != KAUTH_PROCESS_PTRACE)
219a60b9909Spgoyette 		goto out;
220a60b9909Spgoyette 
221a60b9909Spgoyette 	switch ((u_long)arg1) {
222615c430aSkamil #ifdef PT_SETDBREGS
223615c430aSkamil 	case_PT_SETDBREGS
224615c430aSkamil 		if (kauth_cred_getuid(cred) != 0 && user_set_dbregs == 0) {
225615c430aSkamil 			result = KAUTH_RESULT_DENY;
226615c430aSkamil 			break;
227615c430aSkamil 		}
228615c430aSkamil #endif
229fbffadb9Smrg 		/* FALLTHROUGH */
230a60b9909Spgoyette 	case PT_TRACE_ME:
231a60b9909Spgoyette 	case PT_ATTACH:
232a60b9909Spgoyette 	case PT_WRITE_I:
233a60b9909Spgoyette 	case PT_WRITE_D:
234a60b9909Spgoyette 	case PT_READ_I:
235a60b9909Spgoyette 	case PT_READ_D:
236a60b9909Spgoyette 	case PT_IO:
23728f60f0eSchristos 	case_PT_GETREGS
23828f60f0eSchristos 	case_PT_SETREGS
23928f60f0eSchristos 	case_PT_GETFPREGS
24028f60f0eSchristos 	case_PT_SETFPREGS
24128f60f0eSchristos 	case_PT_GETDBREGS
242a60b9909Spgoyette 	case PT_SET_EVENT_MASK:
243a60b9909Spgoyette 	case PT_GET_EVENT_MASK:
244a60b9909Spgoyette 	case PT_GET_PROCESS_STATE:
245e4281b20Skamil 	case PT_SET_SIGINFO:
246e4281b20Skamil 	case PT_GET_SIGINFO:
247a60b9909Spgoyette #ifdef __HAVE_PTRACE_MACHDEP
248a60b9909Spgoyette 	PTRACE_MACHDEP_REQUEST_CASES
249a60b9909Spgoyette #endif
250a60b9909Spgoyette 		if (kauth_cred_getuid(cred) != kauth_cred_getuid(p->p_cred) ||
251a60b9909Spgoyette 		    ISSET(p->p_flag, PK_SUGID)) {
252a60b9909Spgoyette 			break;
253a60b9909Spgoyette 		}
254a60b9909Spgoyette 
255a60b9909Spgoyette 		result = KAUTH_RESULT_ALLOW;
256a60b9909Spgoyette 
257a60b9909Spgoyette 	break;
258a60b9909Spgoyette 
259a60b9909Spgoyette #ifdef PT_STEP
260a60b9909Spgoyette 	case PT_STEP:
26105ffc73cSkamil 	case PT_SETSTEP:
26205ffc73cSkamil 	case PT_CLEARSTEP:
263a60b9909Spgoyette #endif
264a60b9909Spgoyette 	case PT_CONTINUE:
265a60b9909Spgoyette 	case PT_KILL:
266a60b9909Spgoyette 	case PT_DETACH:
267a60b9909Spgoyette 	case PT_LWPINFO:
268a60b9909Spgoyette 	case PT_SYSCALL:
269a60b9909Spgoyette 	case PT_SYSCALLEMU:
270a60b9909Spgoyette 	case PT_DUMPCORE:
271f9b2093dSkamil 	case PT_RESUME:
272f9b2093dSkamil 	case PT_SUSPEND:
2732e7e73e2Skamil 	case PT_STOP:
2744f79a484Skamil 	case PT_LWPSTATUS:
2754f79a484Skamil 	case PT_LWPNEXT:
27648b46cedSkamil 	case PT_SET_SIGPASS:
27748b46cedSkamil 	case PT_GET_SIGPASS:
278a60b9909Spgoyette 		result = KAUTH_RESULT_ALLOW;
279a60b9909Spgoyette 		break;
280a60b9909Spgoyette 
281a60b9909Spgoyette 	default:
282a60b9909Spgoyette 		break;
283a60b9909Spgoyette 	}
284a60b9909Spgoyette 
285a60b9909Spgoyette  out:
286a60b9909Spgoyette #if 0
287a60b9909Spgoyette 	mutex_enter(&ptrace_mtx);
288a60b9909Spgoyette 	if (--ptrace_cbref == 0)
289a60b9909Spgoyette 		cv_broadcast(&ptrace_cv);
290a60b9909Spgoyette 	mutex_exit(&ptrace_mtx);
291a60b9909Spgoyette #endif
292a60b9909Spgoyette 
293a60b9909Spgoyette 	return result;
294a60b9909Spgoyette }
295a60b9909Spgoyette 
2962b32abf1Schristos static struct proc *
2972b32abf1Schristos ptrace_find(struct lwp *l, int req, pid_t pid)
298a60b9909Spgoyette {
2992b32abf1Schristos 	struct proc *t;
300a60b9909Spgoyette 
301a60b9909Spgoyette 	/* "A foolish consistency..." XXX */
302a60b9909Spgoyette 	if (req == PT_TRACE_ME) {
3032b32abf1Schristos 		t = l->l_proc;
304a60b9909Spgoyette 		mutex_enter(t->p_lock);
3052b32abf1Schristos 		return t;
3062b32abf1Schristos 	}
3072b32abf1Schristos 
308a60b9909Spgoyette 	/* Find the process we're supposed to be operating on. */
309a60b9909Spgoyette 	t = proc_find(pid);
3102b32abf1Schristos 	if (t == NULL)
3112b32abf1Schristos 		return NULL;
312a60b9909Spgoyette 
313a60b9909Spgoyette 	/* XXX-elad */
314a60b9909Spgoyette 	mutex_enter(t->p_lock);
3152b32abf1Schristos 	int error = kauth_authorize_process(l->l_cred, KAUTH_PROCESS_CANSEE,
316a60b9909Spgoyette 	    t, KAUTH_ARG(KAUTH_REQ_PROCESS_CANSEE_ENTRY), NULL, NULL);
317a60b9909Spgoyette 	if (error) {
318a60b9909Spgoyette 		mutex_exit(t->p_lock);
3192b32abf1Schristos 		return NULL;
320a60b9909Spgoyette 	}
3212b32abf1Schristos 	return t;
322a60b9909Spgoyette }
323a60b9909Spgoyette 
3242b32abf1Schristos static int
325e4c2eafeSmaxv ptrace_allowed(struct lwp *l, int req, struct proc *t, struct proc *p,
326e4c2eafeSmaxv     bool *locked)
3272b32abf1Schristos {
328e4c2eafeSmaxv 	*locked = false;
329e4c2eafeSmaxv 
330a60b9909Spgoyette 	/*
331a60b9909Spgoyette 	 * Grab a reference on the process to prevent it from execing or
332a60b9909Spgoyette 	 * exiting.
333a60b9909Spgoyette 	 */
3342b32abf1Schristos 	if (!rw_tryenter(&t->p_reflock, RW_READER))
335a60b9909Spgoyette 		return EBUSY;
336a60b9909Spgoyette 
337e4c2eafeSmaxv 	*locked = true;
338e4c2eafeSmaxv 
339a60b9909Spgoyette 	/* Make sure we can operate on it. */
340a60b9909Spgoyette 	switch (req) {
341a60b9909Spgoyette 	case PT_TRACE_ME:
3423e684ebdSkamil 		/*
3433e684ebdSkamil 		 * You can't say to the parent of a process to start tracing if:
3443e684ebdSkamil 		 *	(1) the parent is initproc,
3453e684ebdSkamil 		 */
3463e684ebdSkamil 		if (p->p_pptr == initproc)
3473e684ebdSkamil 			return EPERM;
3483e684ebdSkamil 
3493e684ebdSkamil 		/*
350b9541a3aSkamil 		 *	(2) the process is initproc, or
351b9541a3aSkamil 		 */
352b9541a3aSkamil 		if (p == initproc)
353b9541a3aSkamil 			return EPERM;
354b9541a3aSkamil 
355b9541a3aSkamil 		/*
356b9541a3aSkamil 		 *	(3) the child is already traced.
3573e684ebdSkamil 		 */
3583e684ebdSkamil 		if (ISSET(p->p_slflag, PSL_TRACED))
3593e684ebdSkamil 			return EBUSY;
3603e684ebdSkamil 
3612b32abf1Schristos 		return 0;
362a60b9909Spgoyette 
363a60b9909Spgoyette 	case PT_ATTACH:
364a60b9909Spgoyette 		/*
365a60b9909Spgoyette 		 * You can't attach to a process if:
366a60b9909Spgoyette 		 *	(1) it's the process that's doing the attaching,
367a60b9909Spgoyette 		 */
368edd69790Skamil 		if (t == p)
3692b32abf1Schristos 			return EINVAL;
370a60b9909Spgoyette 
371a60b9909Spgoyette 		/*
372b9541a3aSkamil 		 *	(2) it's a system process,
373a60b9909Spgoyette 		 */
3742b32abf1Schristos 		if (t->p_flag & PK_SYSTEM)
3752b32abf1Schristos 			return EPERM;
376a60b9909Spgoyette 
377a60b9909Spgoyette 		/*
378b9541a3aSkamil 		 *	(3) the tracer is initproc,
379b9541a3aSkamil 		 */
380b9541a3aSkamil 		if (p == initproc)
381b9541a3aSkamil 			return EPERM;
382b9541a3aSkamil 
383b9541a3aSkamil 		/*
384be7413daSkamil 		 *	(4) it's already being traced,
385a60b9909Spgoyette 		 */
3862b32abf1Schristos 		if (ISSET(t->p_slflag, PSL_TRACED))
3872b32abf1Schristos 			return EBUSY;
388a60b9909Spgoyette 
389a60b9909Spgoyette 		/*
390be7413daSkamil 		 *	(5) it's a vfork(2)ed parent of the current process, or
391be7413daSkamil 		 */
392be7413daSkamil 		if (ISSET(p->p_lflag, PL_PPWAIT) && p->p_pptr == t)
393be7413daSkamil 			return EPERM;
394be7413daSkamil 
395be7413daSkamil 		/*
396be7413daSkamil 		 * 	(6) the tracer is chrooted, and its root directory is
397a60b9909Spgoyette 		 * 	    not at or above the root directory of the tracee
398a60b9909Spgoyette 		 */
399a60b9909Spgoyette 		mutex_exit(t->p_lock);	/* XXXSMP */
4002b32abf1Schristos 		int tmp = proc_isunder(t, l);
401a60b9909Spgoyette 		mutex_enter(t->p_lock);	/* XXXSMP */
4022b32abf1Schristos 		if (!tmp)
4032b32abf1Schristos 			return EPERM;
4042b32abf1Schristos 		return 0;
405a60b9909Spgoyette 
406a60b9909Spgoyette 	case PT_READ_I:
407a60b9909Spgoyette 	case PT_READ_D:
408a60b9909Spgoyette 	case PT_WRITE_I:
409a60b9909Spgoyette 	case PT_WRITE_D:
410a60b9909Spgoyette 	case PT_IO:
4119a6383f0Skamil 	case PT_SET_SIGINFO:
4129a6383f0Skamil 	case PT_GET_SIGINFO:
41328f60f0eSchristos 	case_PT_GETREGS
41428f60f0eSchristos 	case_PT_SETREGS
41528f60f0eSchristos 	case_PT_GETFPREGS
41628f60f0eSchristos 	case_PT_SETFPREGS
41728f60f0eSchristos 	case_PT_GETDBREGS
41828f60f0eSchristos 	case_PT_SETDBREGS
419a60b9909Spgoyette #ifdef __HAVE_PTRACE_MACHDEP
420a60b9909Spgoyette 	PTRACE_MACHDEP_REQUEST_CASES
421a60b9909Spgoyette #endif
422a60b9909Spgoyette 		/*
423a60b9909Spgoyette 		 * You can't read/write the memory or registers of a process
424a60b9909Spgoyette 		 * if the tracer is chrooted, and its root directory is not at
425a60b9909Spgoyette 		 * or above the root directory of the tracee.
426a60b9909Spgoyette 		 */
427a60b9909Spgoyette 		mutex_exit(t->p_lock);	/* XXXSMP */
428a60b9909Spgoyette 		tmp = proc_isunder(t, l);
429a60b9909Spgoyette 		mutex_enter(t->p_lock);	/* XXXSMP */
4302b32abf1Schristos 		if (!tmp)
4312b32abf1Schristos 			return EPERM;
432a60b9909Spgoyette 		/*FALLTHROUGH*/
433a60b9909Spgoyette 
434a60b9909Spgoyette 	case PT_CONTINUE:
435a60b9909Spgoyette 	case PT_KILL:
436a60b9909Spgoyette 	case PT_DETACH:
437a60b9909Spgoyette 	case PT_LWPINFO:
438a60b9909Spgoyette 	case PT_SYSCALL:
439a60b9909Spgoyette 	case PT_SYSCALLEMU:
440a60b9909Spgoyette 	case PT_DUMPCORE:
441a60b9909Spgoyette #ifdef PT_STEP
442a60b9909Spgoyette 	case PT_STEP:
44305ffc73cSkamil 	case PT_SETSTEP:
44405ffc73cSkamil 	case PT_CLEARSTEP:
445a60b9909Spgoyette #endif
446a60b9909Spgoyette 	case PT_SET_EVENT_MASK:
447a60b9909Spgoyette 	case PT_GET_EVENT_MASK:
448a60b9909Spgoyette 	case PT_GET_PROCESS_STATE:
449f9b2093dSkamil 	case PT_RESUME:
450f9b2093dSkamil 	case PT_SUSPEND:
4512e7e73e2Skamil 	case PT_STOP:
4524f79a484Skamil 	case PT_LWPSTATUS:
4534f79a484Skamil 	case PT_LWPNEXT:
45448b46cedSkamil 	case PT_SET_SIGPASS:
45548b46cedSkamil 	case PT_GET_SIGPASS:
456a60b9909Spgoyette 		/*
457a60b9909Spgoyette 		 * You can't do what you want to the process if:
458a60b9909Spgoyette 		 *	(1) It's not being traced at all,
459a60b9909Spgoyette 		 */
4602b32abf1Schristos 		if (!ISSET(t->p_slflag, PSL_TRACED))
4612b32abf1Schristos 			return EPERM;
462a60b9909Spgoyette 
463a60b9909Spgoyette 		/*
464a69b333eSkamil 		 *	(2) it's not being traced by _you_, or
465a60b9909Spgoyette 		 */
466a60b9909Spgoyette 		if (t->p_pptr != p) {
467a60b9909Spgoyette 			DPRINTF(("parent %d != %d\n", t->p_pptr->p_pid,
468a60b9909Spgoyette 			    p->p_pid));
4692b32abf1Schristos 			return EBUSY;
470a60b9909Spgoyette 		}
471a60b9909Spgoyette 
472a60b9909Spgoyette 		/*
473a69b333eSkamil 		 *	(3) it's not currently stopped.
4742e7e73e2Skamil 		 *
4752e7e73e2Skamil 		 *	As an exception allow PT_KILL and PT_STOP here.
476a60b9909Spgoyette 		 */
4772e7e73e2Skamil 		if (req != PT_KILL && req != PT_STOP &&
4782e7e73e2Skamil 		    (t->p_stat != SSTOP || !t->p_waited /* XXXSMP */)) {
479a60b9909Spgoyette 			DPRINTF(("stat %d flag %d\n", t->p_stat,
480a60b9909Spgoyette 			    !t->p_waited));
4812b32abf1Schristos 			return EBUSY;
482a60b9909Spgoyette 		}
4832b32abf1Schristos 		return 0;
484a60b9909Spgoyette 
485a60b9909Spgoyette 	default:			/* It was not a legal request. */
4862b32abf1Schristos 		return EINVAL;
4872b32abf1Schristos 	}
488a60b9909Spgoyette }
489a60b9909Spgoyette 
4902b32abf1Schristos static int
4912b32abf1Schristos ptrace_needs_hold(int req)
4922b32abf1Schristos {
493a60b9909Spgoyette 	switch (req) {
494a60b9909Spgoyette #ifdef PT_STEP
495a60b9909Spgoyette 	case PT_STEP:
496a60b9909Spgoyette #endif
497a60b9909Spgoyette 	case PT_CONTINUE:
498a60b9909Spgoyette 	case PT_DETACH:
499a60b9909Spgoyette 	case PT_KILL:
500a60b9909Spgoyette 	case PT_SYSCALL:
501a60b9909Spgoyette 	case PT_SYSCALLEMU:
502a60b9909Spgoyette 	case PT_ATTACH:
503a60b9909Spgoyette 	case PT_TRACE_ME:
5042b32abf1Schristos 	case PT_GET_SIGINFO:
5052b32abf1Schristos 	case PT_SET_SIGINFO:
5062e7e73e2Skamil 	case PT_STOP:
5072b32abf1Schristos 		return 1;
508a60b9909Spgoyette 	default:
5092b32abf1Schristos 		return 0;
5102b32abf1Schristos 	}
5112b32abf1Schristos }
5122b32abf1Schristos 
5132b32abf1Schristos static int
514f818d5c4Schristos ptrace_get_siginfo(struct proc *t, struct ptrace_methods *ptm, void *addr,
515f818d5c4Schristos     size_t data)
5162b32abf1Schristos {
5172b32abf1Schristos 	struct ptrace_siginfo psi;
5182b32abf1Schristos 
5192e300bd3Smaxv 	memset(&psi, 0, sizeof(psi));
5202b32abf1Schristos 	psi.psi_siginfo._info = t->p_sigctx.ps_info;
5212b32abf1Schristos 	psi.psi_lwpid = t->p_sigctx.ps_lwp;
522d2ef544dSchristos 	DPRINTF(("%s: lwp=%d signal=%d\n", __func__, psi.psi_lwpid,
523d2ef544dSchristos 	    psi.psi_siginfo.si_signo));
5242b32abf1Schristos 
525f818d5c4Schristos 	return ptm->ptm_copyout_siginfo(&psi, addr, data);
5262b32abf1Schristos }
5272b32abf1Schristos 
5282b32abf1Schristos static int
529f818d5c4Schristos ptrace_set_siginfo(struct proc *t, struct lwp **lt, struct ptrace_methods *ptm,
530f818d5c4Schristos     void *addr, size_t data)
5312b32abf1Schristos {
5322b32abf1Schristos 	struct ptrace_siginfo psi;
5332b32abf1Schristos 
534f818d5c4Schristos 	int error = ptm->ptm_copyin_siginfo(&psi, addr, data);
5352b32abf1Schristos 	if (error)
5362b32abf1Schristos 		return error;
5372b32abf1Schristos 
5382b32abf1Schristos 	/* Check that the data is a valid signal number or zero. */
5392b32abf1Schristos 	if (psi.psi_siginfo.si_signo < 0 || psi.psi_siginfo.si_signo >= NSIG)
5402b32abf1Schristos 		return EINVAL;
5412b32abf1Schristos 
5422b32abf1Schristos 	t->p_sigctx.ps_faked = true;
5432b32abf1Schristos 	t->p_sigctx.ps_info = psi.psi_siginfo._info;
5442b32abf1Schristos 	t->p_sigctx.ps_lwp = psi.psi_lwpid;
545d2ef544dSchristos 	DPRINTF(("%s: lwp=%d signal=%d\n", __func__, psi.psi_lwpid,
546d2ef544dSchristos 	    psi.psi_siginfo.si_signo));
5472b32abf1Schristos 	return 0;
5482b32abf1Schristos }
5492b32abf1Schristos 
5502b32abf1Schristos static int
55148b46cedSkamil ptrace_get_sigpass(struct proc *t, void *addr, size_t data)
55248b46cedSkamil {
55348b46cedSkamil 	sigset_t set;
55448b46cedSkamil 
55548b46cedSkamil 	if (data > sizeof(set) || data <= 0) {
55648b46cedSkamil 		DPRINTF(("%s: invalid data: %zu < %zu <= 0\n",
55748b46cedSkamil 		        __func__, sizeof(set), data));
55848b46cedSkamil 		return EINVAL;
55948b46cedSkamil 	}
56048b46cedSkamil 
56148b46cedSkamil 	set = t->p_sigctx.ps_sigpass;
56248b46cedSkamil 
56348b46cedSkamil 	return copyout(&set, addr, data);
56448b46cedSkamil }
56548b46cedSkamil 
56648b46cedSkamil static int
56748b46cedSkamil ptrace_set_sigpass(struct proc *t, void *addr, size_t data)
56848b46cedSkamil {
56948b46cedSkamil 	sigset_t set;
57048b46cedSkamil 	int error;
57148b46cedSkamil 
57248b46cedSkamil 	if (data > sizeof(set) || data <= 0) {
57348b46cedSkamil 		DPRINTF(("%s: invalid data: %zu < %zu <= 0\n",
57448b46cedSkamil 		        __func__, sizeof(set), data));
57548b46cedSkamil 		return EINVAL;
57648b46cedSkamil 	}
57748b46cedSkamil 
57848b46cedSkamil 	memset(&set, 0, sizeof(set));
57948b46cedSkamil 
58048b46cedSkamil 	if ((error = copyin(addr, &set, data)))
58148b46cedSkamil 		return error;
58248b46cedSkamil 
58348b46cedSkamil 	/* We catch SIGSTOP and cannot intercept SIGKILL. */
58448b46cedSkamil 	sigminusset(&sigcantmask, &set);
58548b46cedSkamil 
58648b46cedSkamil 	t->p_sigctx.ps_sigpass = set;
58748b46cedSkamil 
58848b46cedSkamil 	return 0;
58948b46cedSkamil }
59048b46cedSkamil 
59148b46cedSkamil static int
5922b32abf1Schristos ptrace_get_event_mask(struct proc *t, void *addr, size_t data)
5932b32abf1Schristos {
5942b32abf1Schristos 	struct ptrace_event pe;
5952b32abf1Schristos 
5962b32abf1Schristos 	if (data != sizeof(pe)) {
5972b32abf1Schristos 		DPRINTF(("%s: %zu != %zu\n", __func__, data, sizeof(pe)));
5982b32abf1Schristos 		return EINVAL;
5992b32abf1Schristos 	}
6002b32abf1Schristos 	memset(&pe, 0, sizeof(pe));
6012b32abf1Schristos 	pe.pe_set_event = ISSET(t->p_slflag, PSL_TRACEFORK) ?
6022b32abf1Schristos 	    PTRACE_FORK : 0;
6032b32abf1Schristos 	pe.pe_set_event |= ISSET(t->p_slflag, PSL_TRACEVFORK) ?
6042b32abf1Schristos 	    PTRACE_VFORK : 0;
6052b32abf1Schristos 	pe.pe_set_event |= ISSET(t->p_slflag, PSL_TRACEVFORK_DONE) ?
6062b32abf1Schristos 	    PTRACE_VFORK_DONE : 0;
6072b32abf1Schristos 	pe.pe_set_event |= ISSET(t->p_slflag, PSL_TRACELWP_CREATE) ?
6082b32abf1Schristos 	    PTRACE_LWP_CREATE : 0;
6092b32abf1Schristos 	pe.pe_set_event |= ISSET(t->p_slflag, PSL_TRACELWP_EXIT) ?
6102b32abf1Schristos 	    PTRACE_LWP_EXIT : 0;
6114c91c5e8Skamil 	pe.pe_set_event |= ISSET(t->p_slflag, PSL_TRACEPOSIX_SPAWN) ?
6124c91c5e8Skamil 	    PTRACE_POSIX_SPAWN : 0;
613d2ef544dSchristos 	DPRINTF(("%s: lwp=%d event=%#x\n", __func__,
614d2ef544dSchristos 	    t->p_sigctx.ps_lwp, pe.pe_set_event));
6152b32abf1Schristos 	return copyout(&pe, addr, sizeof(pe));
6162b32abf1Schristos }
6172b32abf1Schristos 
6182b32abf1Schristos static int
6192b32abf1Schristos ptrace_set_event_mask(struct proc *t, void *addr, size_t data)
6202b32abf1Schristos {
6212b32abf1Schristos 	struct ptrace_event pe;
6222b32abf1Schristos 	int error;
6232b32abf1Schristos 
6242b32abf1Schristos 	if (data != sizeof(pe)) {
6252b32abf1Schristos 		DPRINTF(("%s: %zu != %zu\n", __func__, data, sizeof(pe)));
6262b32abf1Schristos 		return EINVAL;
6272b32abf1Schristos 	}
6282b32abf1Schristos 	if ((error = copyin(addr, &pe, sizeof(pe))) != 0)
6292b32abf1Schristos 		return error;
6302b32abf1Schristos 
631d2ef544dSchristos 	DPRINTF(("%s: lwp=%d event=%#x\n", __func__,
632d2ef544dSchristos 	    t->p_sigctx.ps_lwp, pe.pe_set_event));
6332b32abf1Schristos 	if (pe.pe_set_event & PTRACE_FORK)
6342b32abf1Schristos 		SET(t->p_slflag, PSL_TRACEFORK);
6352b32abf1Schristos 	else
6362b32abf1Schristos 		CLR(t->p_slflag, PSL_TRACEFORK);
637385d9c89Skamil 
6382b32abf1Schristos 	if (pe.pe_set_event & PTRACE_VFORK)
6392b32abf1Schristos 		SET(t->p_slflag, PSL_TRACEVFORK);
6402b32abf1Schristos 	else
6412b32abf1Schristos 		CLR(t->p_slflag, PSL_TRACEVFORK);
642385d9c89Skamil 
6432b32abf1Schristos 	if (pe.pe_set_event & PTRACE_VFORK_DONE)
6442b32abf1Schristos 		SET(t->p_slflag, PSL_TRACEVFORK_DONE);
6452b32abf1Schristos 	else
6462b32abf1Schristos 		CLR(t->p_slflag, PSL_TRACEVFORK_DONE);
647385d9c89Skamil 
6482b32abf1Schristos 	if (pe.pe_set_event & PTRACE_LWP_CREATE)
6492b32abf1Schristos 		SET(t->p_slflag, PSL_TRACELWP_CREATE);
6502b32abf1Schristos 	else
6512b32abf1Schristos 		CLR(t->p_slflag, PSL_TRACELWP_CREATE);
652385d9c89Skamil 
6532b32abf1Schristos 	if (pe.pe_set_event & PTRACE_LWP_EXIT)
6542b32abf1Schristos 		SET(t->p_slflag, PSL_TRACELWP_EXIT);
6552b32abf1Schristos 	else
6562b32abf1Schristos 		CLR(t->p_slflag, PSL_TRACELWP_EXIT);
6574c91c5e8Skamil 
6584c91c5e8Skamil 	if (pe.pe_set_event & PTRACE_POSIX_SPAWN)
6594c91c5e8Skamil 		SET(t->p_slflag, PSL_TRACEPOSIX_SPAWN);
6604c91c5e8Skamil 	else
6614c91c5e8Skamil 		CLR(t->p_slflag, PSL_TRACEPOSIX_SPAWN);
6624c91c5e8Skamil 
6632b32abf1Schristos 	return 0;
6642b32abf1Schristos }
6652b32abf1Schristos 
6662b32abf1Schristos static int
6672b32abf1Schristos ptrace_get_process_state(struct proc *t, void *addr, size_t data)
6682b32abf1Schristos {
669c1b81814Skamil 	struct _ksiginfo *si;
6702b32abf1Schristos 	struct ptrace_state ps;
6712b32abf1Schristos 
6722b32abf1Schristos 	if (data != sizeof(ps)) {
6732b32abf1Schristos 		DPRINTF(("%s: %zu != %zu\n", __func__, data, sizeof(ps)));
6742b32abf1Schristos 		return EINVAL;
6752b32abf1Schristos 	}
6762b32abf1Schristos 
6775e4bbc49Skamil 	if (t->p_sigctx.ps_info._signo != SIGTRAP ||
6785e4bbc49Skamil 	    (t->p_sigctx.ps_info._code != TRAP_CHLD &&
6795e4bbc49Skamil 	        t->p_sigctx.ps_info._code != TRAP_LWP)) {
680c1b81814Skamil 		memset(&ps, 0, sizeof(ps));
681c1b81814Skamil 	} else {
682c1b81814Skamil 		si = &t->p_sigctx.ps_info;
68396755fb8Skamil 
68496755fb8Skamil 		KASSERT(si->_reason._ptrace_state._pe_report_event > 0);
68596755fb8Skamil 		KASSERT(si->_reason._ptrace_state._option._pe_other_pid > 0);
68696755fb8Skamil 
687c1b81814Skamil 		ps.pe_report_event = si->_reason._ptrace_state._pe_report_event;
6885e4bbc49Skamil 
6895e4bbc49Skamil 		CTASSERT(sizeof(ps.pe_other_pid) == sizeof(ps.pe_lwp));
6905e4bbc49Skamil 		ps.pe_other_pid =
691c1b81814Skamil 			si->_reason._ptrace_state._option._pe_other_pid;
692c1b81814Skamil 	}
6935e4bbc49Skamil 
694d2ef544dSchristos 	DPRINTF(("%s: lwp=%d event=%#x pid=%d lwp=%d\n", __func__,
695d2ef544dSchristos 	    t->p_sigctx.ps_lwp, ps.pe_report_event,
696d2ef544dSchristos 	    ps.pe_other_pid, ps.pe_lwp));
6972b32abf1Schristos 	return copyout(&ps, addr, sizeof(ps));
6982b32abf1Schristos }
6992b32abf1Schristos 
7002b32abf1Schristos static int
7012b32abf1Schristos ptrace_lwpinfo(struct proc *t, struct lwp **lt, void *addr, size_t data)
7022b32abf1Schristos {
7032b32abf1Schristos 	struct ptrace_lwpinfo pl;
7042b32abf1Schristos 
7052b32abf1Schristos 	if (data != sizeof(pl)) {
7062b32abf1Schristos 		DPRINTF(("%s: %zu != %zu\n", __func__, data, sizeof(pl)));
7072b32abf1Schristos 		return EINVAL;
7082b32abf1Schristos 	}
7092b32abf1Schristos 	int error = copyin(addr, &pl, sizeof(pl));
7102b32abf1Schristos 	if (error)
7112b32abf1Schristos 		return error;
7122b32abf1Schristos 
7132b32abf1Schristos 	lwpid_t tmp = pl.pl_lwpid;
7142b32abf1Schristos 	lwp_delref(*lt);
7152b32abf1Schristos 	mutex_enter(t->p_lock);
7162b32abf1Schristos 	if (tmp == 0)
7172b32abf1Schristos 		*lt = lwp_find_first(t);
7182b32abf1Schristos 	else {
7192b32abf1Schristos 		*lt = lwp_find(t, tmp);
7202b32abf1Schristos 		if (*lt == NULL) {
7212b32abf1Schristos 			mutex_exit(t->p_lock);
7222b32abf1Schristos 			return ESRCH;
7232b32abf1Schristos 		}
7242b32abf1Schristos 		*lt = LIST_NEXT(*lt, l_sibling);
7252b32abf1Schristos 	}
7262b32abf1Schristos 
727bd8a92f7Skamil 	while (*lt != NULL && (!lwp_alive(*lt) ||
728bd8a92f7Skamil 	       ((*lt)->l_flag & LW_SYSTEM) != 0))
7292b32abf1Schristos 		*lt = LIST_NEXT(*lt, l_sibling);
7302b32abf1Schristos 
7312b32abf1Schristos 	pl.pl_lwpid = 0;
7322b32abf1Schristos 	pl.pl_event = 0;
7332b32abf1Schristos 	if (*lt) {
7342b32abf1Schristos 		lwp_addref(*lt);
7352b32abf1Schristos 		pl.pl_lwpid = (*lt)->l_lid;
7362b32abf1Schristos 
7372b32abf1Schristos 		if ((*lt)->l_flag & LW_WSUSPEND)
7382b32abf1Schristos 			pl.pl_event = PL_EVENT_SUSPENDED;
7392b32abf1Schristos 		/*
7402b32abf1Schristos 		 * If we match the lwp, or it was sent to every lwp,
7412b32abf1Schristos 		 * we set PL_EVENT_SIGNAL.
7422b32abf1Schristos 		 * XXX: ps_lwp == 0 means everyone and noone, so
7432b32abf1Schristos 		 * check ps_signo too.
7442b32abf1Schristos 		 */
7452b32abf1Schristos 		else if ((*lt)->l_lid == t->p_sigctx.ps_lwp
7462b32abf1Schristos 			 || (t->p_sigctx.ps_lwp == 0 &&
747d2ef544dSchristos 			     t->p_sigctx.ps_info._signo)) {
748d2ef544dSchristos 			DPRINTF(("%s: lwp=%d siglwp=%d signo %d\n", __func__,
749d2ef544dSchristos 			    pl.pl_lwpid, t->p_sigctx.ps_lwp,
750d2ef544dSchristos 			    t->p_sigctx.ps_info._signo));
7512b32abf1Schristos 			pl.pl_event = PL_EVENT_SIGNAL;
7522b32abf1Schristos 		}
753d2ef544dSchristos 	}
7542b32abf1Schristos 	mutex_exit(t->p_lock);
755d2ef544dSchristos 	DPRINTF(("%s: lwp=%d event=%#x\n", __func__,
756d2ef544dSchristos 	    pl.pl_lwpid, pl.pl_event));
7572b32abf1Schristos 
7582b32abf1Schristos 	return copyout(&pl, addr, sizeof(pl));
7592b32abf1Schristos }
7602b32abf1Schristos 
7614f79a484Skamil static int
7624f79a484Skamil ptrace_lwpstatus(struct proc *t, struct ptrace_methods *ptm, struct lwp **lt,
7634f79a484Skamil     void *addr, size_t data, bool next)
7644f79a484Skamil {
7654f79a484Skamil 	struct ptrace_lwpstatus pls;
7664f79a484Skamil 	struct lwp *l;
7674f79a484Skamil 	int error;
7684f79a484Skamil 
7694f79a484Skamil 	if (data > sizeof(pls) || data < sizeof(lwpid_t)) {
7704f79a484Skamil 		DPRINTF(("%s: invalid data: %zu < %zu < %zu\n",
7714f79a484Skamil 		        __func__, sizeof(lwpid_t), data, sizeof(pls)));
7724f79a484Skamil 		return EINVAL;
7734f79a484Skamil 	}
77499c26a8aSmaxv 	error = copyin(addr, &pls.pl_lwpid, sizeof(lwpid_t));
7754f79a484Skamil 	if (error)
7764f79a484Skamil 		return error;
7774f79a484Skamil 
7784f79a484Skamil 	if (next) {
7794f79a484Skamil 		lwp_delref(*lt);
7804f79a484Skamil 		lwpid_t tmp = pls.pl_lwpid;
7814f79a484Skamil 		mutex_enter(t->p_lock);
7824f79a484Skamil 		if (tmp == 0)
7834f79a484Skamil 			*lt = lwp_find_first(t);
7844f79a484Skamil 		else {
7854f79a484Skamil 			*lt = lwp_find(t, tmp);
7864f79a484Skamil 			if (*lt == NULL) {
7874f79a484Skamil 				mutex_exit(t->p_lock);
7884f79a484Skamil 				return ESRCH;
7894f79a484Skamil 			}
7904f79a484Skamil 			*lt = LIST_NEXT(*lt, l_sibling);
7914f79a484Skamil 		}
7924f79a484Skamil 
793bd8a92f7Skamil 		while (*lt != NULL && (!lwp_alive(*lt) ||
794bd8a92f7Skamil 		       ((*lt)->l_flag & LW_SYSTEM) != 0))
7954f79a484Skamil 			*lt = LIST_NEXT(*lt, l_sibling);
7964f79a484Skamil 
7974f79a484Skamil 		if (*lt == NULL) {
7984f79a484Skamil 			memset(&pls, 0, sizeof(pls));
7994f79a484Skamil 			mutex_exit(t->p_lock);
8004f79a484Skamil 			goto out;
8014f79a484Skamil 		}
8024f79a484Skamil 		lwp_addref(*lt);
8034f79a484Skamil 		mutex_exit(t->p_lock);
8044f79a484Skamil 
8054f79a484Skamil 		pls.pl_lwpid = (*lt)->l_lid;
8064f79a484Skamil 	} else {
8074f79a484Skamil 		if ((error = ptrace_update_lwp(t, lt, pls.pl_lwpid)) != 0)
8084f79a484Skamil 			return error;
8094f79a484Skamil 	}
8104f79a484Skamil 
8114f79a484Skamil 	l = *lt;
8124f79a484Skamil 
8134f79a484Skamil 	ptrace_read_lwpstatus(l, &pls);
8144f79a484Skamil 
8154f79a484Skamil out:
81634fd0d8aSchristos 	DPRINTF(("%s: lwp=%d sigpend=%02x%02x%02x%02x "
81734fd0d8aSchristos 	    "sigmask=%02x%02x%02x%02x name='%s' private=%p\n",
81834fd0d8aSchristos 	    __func__, pls.pl_lwpid,
8194f79a484Skamil 	    pls.pl_sigpend.__bits[0], pls.pl_sigpend.__bits[1],
8204f79a484Skamil 	    pls.pl_sigpend.__bits[2], pls.pl_sigpend.__bits[3],
8214f79a484Skamil 	    pls.pl_sigmask.__bits[0], pls.pl_sigmask.__bits[1],
8224f79a484Skamil 	    pls.pl_sigmask.__bits[2], pls.pl_sigmask.__bits[3],
8234f79a484Skamil 	    pls.pl_name, pls.pl_private));
8244f79a484Skamil 
8254f79a484Skamil 	return ptm->ptm_copyout_lwpstatus(&pls, addr, data);
8264f79a484Skamil }
8274f79a484Skamil 
8282b32abf1Schristos static int
8292b32abf1Schristos ptrace_startstop(struct proc *t, struct lwp **lt, int rq, void *addr,
8302b32abf1Schristos     size_t data)
8312b32abf1Schristos {
8322b32abf1Schristos 	int error;
8332b32abf1Schristos 
8342b32abf1Schristos 	if ((error = ptrace_update_lwp(t, lt, data)) != 0)
8352b32abf1Schristos 		return error;
8362b32abf1Schristos 
837d2ef544dSchristos 	DPRINTF(("%s: lwp=%d request=%d\n", __func__, (*lt)->l_lid, rq));
8382b32abf1Schristos 	lwp_lock(*lt);
8392b32abf1Schristos 	if (rq == PT_SUSPEND)
840a35a4fe3Skamil 		(*lt)->l_flag |= LW_DBGSUSPEND;
841a35a4fe3Skamil 	else {
842a35a4fe3Skamil 		(*lt)->l_flag &= ~LW_DBGSUSPEND;
843a35a4fe3Skamil 		if ((*lt)->l_flag != LSSUSPENDED)
844a35a4fe3Skamil 			(*lt)->l_stat = LSSTOP;
845a35a4fe3Skamil 	}
8462b32abf1Schristos 	lwp_unlock(*lt);
8472b32abf1Schristos 	return 0;
8482b32abf1Schristos }
8492b32abf1Schristos 
85028f60f0eSchristos #ifdef PT_REGISTERS
8512b32abf1Schristos static int
8522b32abf1Schristos ptrace_uio_dir(int req)
8532b32abf1Schristos {
8542b32abf1Schristos 	switch (req) {
85528f60f0eSchristos 	case_PT_GETREGS
85628f60f0eSchristos 	case_PT_GETFPREGS
85728f60f0eSchristos 	case_PT_GETDBREGS
8582b32abf1Schristos 		return UIO_READ;
85928f60f0eSchristos 	case_PT_SETREGS
86028f60f0eSchristos 	case_PT_SETFPREGS
86128f60f0eSchristos 	case_PT_SETDBREGS
8622b32abf1Schristos 		return UIO_WRITE;
8632b32abf1Schristos 	default:
8642b32abf1Schristos 		return -1;
8652b32abf1Schristos 	}
8662b32abf1Schristos }
8672b32abf1Schristos 
8682b32abf1Schristos static int
8692b32abf1Schristos ptrace_regs(struct lwp *l, struct lwp **lt, int rq, struct ptrace_methods *ptm,
8702b32abf1Schristos     void *addr, size_t data)
8712b32abf1Schristos {
8722b32abf1Schristos 	int error;
8736df93363Srin 	struct proc *p, *t;
8742b32abf1Schristos 	struct vmspace *vm;
8752b32abf1Schristos 
8766df93363Srin 	p = l->l_proc;		/* tracer */
8776df93363Srin 	t = (*lt)->l_proc;	/* traced */
8786df93363Srin 
8792b32abf1Schristos 	if ((error = ptrace_update_lwp(t, lt, data)) != 0)
8802b32abf1Schristos 		return error;
8812b32abf1Schristos 
8822b32abf1Schristos 	int dir = ptrace_uio_dir(rq);
8832b32abf1Schristos 	size_t size;
8842b32abf1Schristos 	int (*func)(struct lwp *, struct lwp *, struct uio *);
8852b32abf1Schristos 
886d2ef544dSchristos 	DPRINTF(("%s: lwp=%d request=%d\n", __func__, l->l_lid, rq));
887d2ef544dSchristos 
8882b32abf1Schristos 	switch (rq) {
889*5b38d912Schristos #if defined(PT_REGS)
89028f60f0eSchristos 	case_PT_GETREGS
89128f60f0eSchristos 	case_PT_SETREGS
8922b32abf1Schristos 		if (!process_validregs(*lt))
8932b32abf1Schristos 			return EINVAL;
8946df93363Srin 		size = PROC_REGSZ(p);
8952b32abf1Schristos 		func = ptm->ptm_doregs;
896a60b9909Spgoyette 		break;
8972b32abf1Schristos #endif
898*5b38d912Schristos #if defined(PT_FPREGS)
89928f60f0eSchristos 	case_PT_GETFPREGS
90028f60f0eSchristos 	case_PT_SETFPREGS
9012b32abf1Schristos 		if (!process_validfpregs(*lt))
9022b32abf1Schristos 			return EINVAL;
9036df93363Srin 		size = PROC_FPREGSZ(p);
9042b32abf1Schristos 		func = ptm->ptm_dofpregs;
9052b32abf1Schristos 		break;
9062b32abf1Schristos #endif
907*5b38d912Schristos #if defined(PT_DBREGS)
90828f60f0eSchristos 	case_PT_GETDBREGS
90928f60f0eSchristos 	case_PT_SETDBREGS
9102b32abf1Schristos 		if (!process_validdbregs(*lt))
9112b32abf1Schristos 			return EINVAL;
9126df93363Srin 		size = PROC_DBREGSZ(p);
9132b32abf1Schristos 		func = ptm->ptm_dodbregs;
9142b32abf1Schristos 		break;
9152b32abf1Schristos #endif
9162b32abf1Schristos 	default:
9172b32abf1Schristos 		return EINVAL;
9182b32abf1Schristos 	}
9192b32abf1Schristos 
920bac82b91Schristos 	error = proc_vmspace_getref(l->l_proc, &vm);
9212b32abf1Schristos 	if (error)
9222b32abf1Schristos 		return error;
9232b32abf1Schristos 
9242b32abf1Schristos 	struct uio uio;
9252b32abf1Schristos 	struct iovec iov;
9262b32abf1Schristos 
9272b32abf1Schristos 	iov.iov_base = addr;
9282b32abf1Schristos 	iov.iov_len = size;
9292b32abf1Schristos 	uio.uio_iov = &iov;
9302b32abf1Schristos 	uio.uio_iovcnt = 1;
9312b32abf1Schristos 	uio.uio_offset = 0;
9322b32abf1Schristos 	uio.uio_resid = iov.iov_len;
9332b32abf1Schristos 	uio.uio_rw = dir;
9342b32abf1Schristos 	uio.uio_vmspace = vm;
9352b32abf1Schristos 
9362b32abf1Schristos 	error = (*func)(l, *lt, &uio);
9372b32abf1Schristos 	uvmspace_free(vm);
9382b32abf1Schristos 	return error;
9392b32abf1Schristos }
94028f60f0eSchristos #endif
94128f60f0eSchristos 
94228f60f0eSchristos static int
9432e7e73e2Skamil ptrace_sendsig(struct lwp *l, int req, struct proc *t, struct lwp *lt, int signo, int resume_all)
94428f60f0eSchristos {
94528f60f0eSchristos 	ksiginfo_t ksi;
94628f60f0eSchristos 
94728f60f0eSchristos 	/* Finally, deliver the requested signal (or none). */
94828f60f0eSchristos 	if (t->p_stat == SSTOP) {
94928f60f0eSchristos 		/*
95028f60f0eSchristos 		 * Unstop the process.  If it needs to take a
95128f60f0eSchristos 		 * signal, make all efforts to ensure that at
95228f60f0eSchristos 		 * an LWP runs to see it.
95328f60f0eSchristos 		 */
95428f60f0eSchristos 		t->p_xsig = signo;
9557f2cb72eSkamil 
9567f2cb72eSkamil 		/*
95720c051d7Skamil 		 * signo > 0 check prevents a potential panic, as
9587f2cb72eSkamil 		 * sigismember(&...,0) is invalid check and signo
9597f2cb72eSkamil 		 * can be equal to 0 as a special case of no-signal.
9607f2cb72eSkamil 		 */
9617f2cb72eSkamil 		if (signo > 0 && sigismember(&stopsigmask, signo)) {
9627f2cb72eSkamil 			t->p_waited = 0;
9637f2cb72eSkamil 			child_psignal(t, 0);
9647f2cb72eSkamil 		} else if (resume_all)
96528f60f0eSchristos 			proc_unstop(t);
96628f60f0eSchristos 		else
96728f60f0eSchristos 			lwp_unstop(lt);
9685e50d45eSchristos 
96941f5d30eSchristos 		if (req != PT_KILL || signo != SIGKILL)
97028f60f0eSchristos 			return 0;
97128f60f0eSchristos 	}
97228f60f0eSchristos 
9732e7e73e2Skamil 	KASSERT(req == PT_KILL || req == PT_STOP || req == PT_ATTACH);
97428f60f0eSchristos 
9752e7e73e2Skamil 	KSI_INIT(&ksi);
9762e7e73e2Skamil 	ksi.ksi_signo = signo;
9772e7e73e2Skamil 	ksi.ksi_code = SI_USER;
9782e7e73e2Skamil 	ksi.ksi_pid = l->l_proc->p_pid;
9792e7e73e2Skamil 	ksi.ksi_uid = kauth_cred_geteuid(l->l_cred);
9802e7e73e2Skamil 
9812e7e73e2Skamil 	t->p_sigctx.ps_faked = false;
9822e7e73e2Skamil 
9832e7e73e2Skamil 	DPRINTF(("%s: pid=%d.%d signal=%d resume_all=%d\n", __func__, t->p_pid,
9842e7e73e2Skamil 	    lt->l_lid, signo, resume_all));
9852e7e73e2Skamil 
9862e7e73e2Skamil 	return kpsignal2(t, &ksi);
98728f60f0eSchristos }
98828f60f0eSchristos 
98928f60f0eSchristos static int
99028f60f0eSchristos ptrace_dumpcore(struct lwp *lt, char *path, size_t len)
99128f60f0eSchristos {
99228f60f0eSchristos 	int error;
99328f60f0eSchristos 	if (path != NULL) {
99428f60f0eSchristos 
99528f60f0eSchristos 		if (len >= MAXPATHLEN)
99628f60f0eSchristos 			return EINVAL;
99728f60f0eSchristos 
99828f60f0eSchristos 		char *src = path;
99928f60f0eSchristos 		path = kmem_alloc(len + 1, KM_SLEEP);
100028f60f0eSchristos 		error = copyin(src, path, len);
100128f60f0eSchristos 		if (error)
100228f60f0eSchristos 			goto out;
100328f60f0eSchristos 		path[len] = '\0';
100428f60f0eSchristos 	}
1005d2ef544dSchristos 	DPRINTF(("%s: lwp=%d\n", __func__, lt->l_lid));
100686069f28Spgoyette 	MODULE_HOOK_CALL(coredump_hook, (lt, path), 0, error);
100728f60f0eSchristos out:
100828f60f0eSchristos 	if (path)
100928f60f0eSchristos 		kmem_free(path, len + 1);
101028f60f0eSchristos 	return error;
101128f60f0eSchristos }
101228f60f0eSchristos 
101328f60f0eSchristos static int
101428f60f0eSchristos ptrace_doio(struct lwp *l, struct proc *t, struct lwp *lt,
1015d3ef0ca6Schristos     struct ptrace_io_desc *piod, void *addr, bool sysspace)
101628f60f0eSchristos {
101728f60f0eSchristos 	struct uio uio;
101828f60f0eSchristos 	struct iovec iov;
101928f60f0eSchristos 	int error, tmp;
102028f60f0eSchristos 
102128f60f0eSchristos 	error = 0;
102228f60f0eSchristos 	iov.iov_base = piod->piod_addr;
102328f60f0eSchristos 	iov.iov_len = piod->piod_len;
102428f60f0eSchristos 	uio.uio_iov = &iov;
102528f60f0eSchristos 	uio.uio_iovcnt = 1;
102628f60f0eSchristos 	uio.uio_offset = (off_t)(unsigned long)piod->piod_offs;
102728f60f0eSchristos 	uio.uio_resid = piod->piod_len;
102828f60f0eSchristos 
1029d2ef544dSchristos 	DPRINTF(("%s: lwp=%d request=%d\n", __func__, l->l_lid, piod->piod_op));
1030d2ef544dSchristos 
103128f60f0eSchristos 	switch (piod->piod_op) {
103228f60f0eSchristos 	case PIOD_READ_D:
103328f60f0eSchristos 	case PIOD_READ_I:
103428f60f0eSchristos 		uio.uio_rw = UIO_READ;
103528f60f0eSchristos 		break;
103628f60f0eSchristos 	case PIOD_WRITE_D:
103728f60f0eSchristos 	case PIOD_WRITE_I:
103828f60f0eSchristos 		/*
103928f60f0eSchristos 		 * Can't write to a RAS
104028f60f0eSchristos 		 */
104128f60f0eSchristos 		if (ras_lookup(t, addr) != (void *)-1) {
104228f60f0eSchristos 			return EACCES;
104328f60f0eSchristos 		}
104428f60f0eSchristos 		uio.uio_rw = UIO_WRITE;
104528f60f0eSchristos 		break;
104628f60f0eSchristos 	case PIOD_READ_AUXV:
104728f60f0eSchristos 		uio.uio_rw = UIO_READ;
104828f60f0eSchristos 		tmp = t->p_execsw->es_arglen;
104928f60f0eSchristos 		if (uio.uio_offset > tmp)
105028f60f0eSchristos 			return EIO;
105128f60f0eSchristos 		if (uio.uio_resid > tmp - uio.uio_offset)
105228f60f0eSchristos 			uio.uio_resid = tmp - uio.uio_offset;
105328f60f0eSchristos 		piod->piod_len = iov.iov_len = uio.uio_resid;
105428f60f0eSchristos 		error = process_auxv_offset(t, &uio);
105528f60f0eSchristos 		break;
105628f60f0eSchristos 	default:
105728f60f0eSchristos 		error = EINVAL;
105828f60f0eSchristos 		break;
105928f60f0eSchristos 	}
1060d3ef0ca6Schristos 
106128f60f0eSchristos 	if (error)
106228f60f0eSchristos 		return error;
106328f60f0eSchristos 
1064d3ef0ca6Schristos 	if (sysspace) {
1065d3ef0ca6Schristos 		uio.uio_vmspace = vmspace_kernel();
1066d3ef0ca6Schristos 	} else {
1067d3ef0ca6Schristos 		error = proc_vmspace_getref(l->l_proc, &uio.uio_vmspace);
1068d3ef0ca6Schristos 		if (error)
106928f60f0eSchristos 			return error;
107028f60f0eSchristos 	}
1071d3ef0ca6Schristos 
1072d3ef0ca6Schristos 	error = process_domem(l, lt, &uio);
1073d3ef0ca6Schristos 	if (!sysspace)
1074d3ef0ca6Schristos 		uvmspace_free(uio.uio_vmspace);
1075d3ef0ca6Schristos 	if (error)
1076d3ef0ca6Schristos 		return error;
107728f60f0eSchristos 	piod->piod_len -= uio.uio_resid;
107828f60f0eSchristos 	return 0;
107928f60f0eSchristos }
10802b32abf1Schristos 
10812b32abf1Schristos int
10822b32abf1Schristos do_ptrace(struct ptrace_methods *ptm, struct lwp *l, int req, pid_t pid,
10832b32abf1Schristos     void *addr, int data, register_t *retval)
10842b32abf1Schristos {
10852b32abf1Schristos 	struct proc *p = l->l_proc;
10862b32abf1Schristos 	struct lwp *lt = NULL;
10872b32abf1Schristos 	struct lwp *lt2;
10882b32abf1Schristos 	struct proc *t;				/* target process */
10892b32abf1Schristos 	struct ptrace_io_desc piod;
10902b32abf1Schristos 	int error, write, tmp, pheld;
10912b32abf1Schristos 	int signo = 0;
10922b32abf1Schristos 	int resume_all;
1093e4c2eafeSmaxv 	bool locked;
10942b32abf1Schristos 	error = 0;
10952b32abf1Schristos 
109634fd0d8aSchristos 	DPRINTF(("%s: tracer=%d tracee=%d req=%s(%d) addr=%p data=%d\n",
109734fd0d8aSchristos 	    __func__, p->p_pid, pid,
109834fd0d8aSchristos 	    (u_int)req < __arraycount(pt_strings) ? pt_strings[req] : "???",
109934fd0d8aSchristos 	    req, addr, data));
11002b32abf1Schristos 	/*
11012b32abf1Schristos 	 * If attaching or detaching, we need to get a write hold on the
11022b32abf1Schristos 	 * proclist lock so that we can re-parent the target process.
11032b32abf1Schristos 	 */
11040eaaa024Sad 	mutex_enter(&proc_lock);
11052b32abf1Schristos 
11062b32abf1Schristos 	t = ptrace_find(l, req, pid);
11072b32abf1Schristos 	if (t == NULL) {
11080eaaa024Sad 		mutex_exit(&proc_lock);
11092b32abf1Schristos 		return ESRCH;
11102b32abf1Schristos 	}
11112b32abf1Schristos 
11122b32abf1Schristos 	pheld = 1;
1113e4c2eafeSmaxv 	if ((error = ptrace_allowed(l, req, t, p, &locked)) != 0)
11142b32abf1Schristos 		goto out;
11152b32abf1Schristos 
11162b32abf1Schristos 	if ((error = kauth_authorize_process(l->l_cred,
11172b32abf1Schristos 	    KAUTH_PROCESS_PTRACE, t, KAUTH_ARG(req), NULL, NULL)) != 0)
11182b32abf1Schristos 		goto out;
11192b32abf1Schristos 
11202b32abf1Schristos 	if ((lt = lwp_find_first(t)) == NULL) {
11212b32abf1Schristos 	    error = ESRCH;
11222b32abf1Schristos 	    goto out;
11232b32abf1Schristos 	}
11242b32abf1Schristos 
11252b32abf1Schristos 	/* Do single-step fixup if needed. */
11262b32abf1Schristos 	FIX_SSTEP(t);
11272b32abf1Schristos 	KASSERT(lt != NULL);
11282b32abf1Schristos 	lwp_addref(lt);
11292b32abf1Schristos 
11302b32abf1Schristos 	/*
11312b32abf1Schristos 	 * Which locks do we need held? XXX Ugly.
11322b32abf1Schristos 	 */
11332b32abf1Schristos 	if ((pheld = ptrace_needs_hold(req)) == 0) {
11342b32abf1Schristos 		mutex_exit(t->p_lock);
11350eaaa024Sad 		mutex_exit(&proc_lock);
1136a60b9909Spgoyette 	}
1137a60b9909Spgoyette 
1138a60b9909Spgoyette 	/* Now do the operation. */
1139a60b9909Spgoyette 	write = 0;
1140a60b9909Spgoyette 	*retval = 0;
1141a60b9909Spgoyette 	tmp = 0;
1142a60b9909Spgoyette 	resume_all = 1;
1143a60b9909Spgoyette 
1144a60b9909Spgoyette 	switch (req) {
1145a60b9909Spgoyette 	case PT_TRACE_ME:
1146a60b9909Spgoyette 		/* Just set the trace flag. */
1147a60b9909Spgoyette 		SET(t->p_slflag, PSL_TRACED);
1148a60b9909Spgoyette 		t->p_opptr = t->p_pptr;
1149a60b9909Spgoyette 		break;
1150a60b9909Spgoyette 
11518f414e64Skamil 	/*
11528f414e64Skamil 	 * The I and D separate address space has been inherited from PDP-11.
11538f414e64Skamil 	 * The 16-bit UNIX started with a single address space per program,
11548f414e64Skamil 	 * but was extended to two 16-bit (2 x 64kb) address spaces.
11558f414e64Skamil 	 *
11568f414e64Skamil 	 * We no longer maintain this feature in maintained architectures, but
11576584ea56Sandvar 	 * we keep the API for backward compatibility. Currently the I and D
11588f414e64Skamil 	 * operations are exactly the same and not distinguished in debuggers.
11598f414e64Skamil 	 */
11608f414e64Skamil 	case PT_WRITE_I:
1161a60b9909Spgoyette 	case PT_WRITE_D:
1162a60b9909Spgoyette 		write = 1;
1163a60b9909Spgoyette 		tmp = data;
1164a60b9909Spgoyette 		/* FALLTHROUGH */
11658f414e64Skamil 	case PT_READ_I:
1166a60b9909Spgoyette 	case PT_READ_D:
1167d3ef0ca6Schristos 		piod.piod_addr = &tmp;
1168d3ef0ca6Schristos 		piod.piod_len = sizeof(tmp);
1169d3ef0ca6Schristos 		piod.piod_offs = addr;
1170d3ef0ca6Schristos 		piod.piod_op = write ? PIOD_WRITE_D : PIOD_READ_D;
1171d3ef0ca6Schristos 		if ((error = ptrace_doio(l, t, lt, &piod, addr, true)) != 0)
1172d3ef0ca6Schristos 			break;
117371c21268Skamil 		/*
117471c21268Skamil 		 * For legacy reasons we treat here two results as success:
117571c21268Skamil 		 *  - incomplete transfer  piod.piod_len < sizeof(tmp)
117671c21268Skamil 		 *  - no transfer          piod.piod_len == 0
117771c21268Skamil 		 *
117871c21268Skamil 		 * This means that there is no way to determine whether
117971c21268Skamil 		 * transfer operation was performed in PT_WRITE and PT_READ
118071c21268Skamil 		 * calls.
118171c21268Skamil 		 */
1182a60b9909Spgoyette 		if (!write)
1183a60b9909Spgoyette 			*retval = tmp;
1184a60b9909Spgoyette 		break;
1185a60b9909Spgoyette 
1186a60b9909Spgoyette 	case PT_IO:
1187f818d5c4Schristos 		if ((error = ptm->ptm_copyin_piod(&piod, addr, data)) != 0)
1188a60b9909Spgoyette 			break;
1189c3c12ae2Skamil 		if (piod.piod_len < 1) {
1190c3c12ae2Skamil 			error = EINVAL;
1191c3c12ae2Skamil 			break;
1192c3c12ae2Skamil 		}
1193d3ef0ca6Schristos 		if ((error = ptrace_doio(l, t, lt, &piod, addr, false)) != 0)
1194a60b9909Spgoyette 			break;
119571c21268Skamil 		/*
119671c21268Skamil 		 * For legacy reasons we treat here two results as success:
119771c21268Skamil 		 *  - incomplete transfer  piod.piod_len < sizeof(tmp)
119871c21268Skamil 		 *  - no transfer          piod.piod_len == 0
119971c21268Skamil 		 */
1200a0a9a3b4Schristos 		error = ptm->ptm_copyout_piod(&piod, addr, data);
1201a60b9909Spgoyette 		break;
1202a60b9909Spgoyette 
1203a60b9909Spgoyette 	case PT_DUMPCORE:
120428f60f0eSchristos 		error = ptrace_dumpcore(lt, addr, data);
1205a60b9909Spgoyette 		break;
1206a60b9909Spgoyette 
1207a60b9909Spgoyette #ifdef PT_STEP
1208a60b9909Spgoyette 	case PT_STEP:
1209a60b9909Spgoyette 		/*
1210a60b9909Spgoyette 		 * From the 4.4BSD PRM:
1211a60b9909Spgoyette 		 * "Execution continues as in request PT_CONTINUE; however
1212a60b9909Spgoyette 		 * as soon as possible after execution of at least one
1213a60b9909Spgoyette 		 * instruction, execution stops again. [ ... ]"
1214a60b9909Spgoyette 		 */
1215a60b9909Spgoyette #endif
1216a60b9909Spgoyette 	case PT_CONTINUE:
1217a60b9909Spgoyette 	case PT_SYSCALL:
1218a60b9909Spgoyette 	case PT_DETACH:
1219a60b9909Spgoyette 		if (req == PT_SYSCALL) {
1220a60b9909Spgoyette 			if (!ISSET(t->p_slflag, PSL_SYSCALL)) {
1221a60b9909Spgoyette 				SET(t->p_slflag, PSL_SYSCALL);
1222a60b9909Spgoyette #ifdef __HAVE_SYSCALL_INTERN
1223a60b9909Spgoyette 				(*t->p_emul->e_syscall_intern)(t);
1224a60b9909Spgoyette #endif
1225a60b9909Spgoyette 			}
1226a60b9909Spgoyette 		} else {
1227a60b9909Spgoyette 			if (ISSET(t->p_slflag, PSL_SYSCALL)) {
1228a60b9909Spgoyette 				CLR(t->p_slflag, PSL_SYSCALL);
1229a60b9909Spgoyette #ifdef __HAVE_SYSCALL_INTERN
1230a60b9909Spgoyette 				(*t->p_emul->e_syscall_intern)(t);
1231a60b9909Spgoyette #endif
1232a60b9909Spgoyette 			}
1233a60b9909Spgoyette 		}
1234a60b9909Spgoyette 		t->p_trace_enabled = trace_is_enabled(t);
1235a60b9909Spgoyette 
1236a60b9909Spgoyette 		/*
1237a60b9909Spgoyette 		 * Pick up the LWPID, if supplied.  There are two cases:
1238a60b9909Spgoyette 		 * data < 0 : step or continue single thread, lwp = -data
1239a60b9909Spgoyette 		 * data > 0 in PT_STEP : step this thread, continue others
1240a60b9909Spgoyette 		 * For operations other than PT_STEP, data > 0 means
1241a60b9909Spgoyette 		 * data is the signo to deliver to the process.
1242a60b9909Spgoyette 		 */
1243a60b9909Spgoyette 		tmp = data;
1244a60b9909Spgoyette 		if (tmp >= 0) {
1245a60b9909Spgoyette #ifdef PT_STEP
1246a60b9909Spgoyette 			if (req == PT_STEP)
1247a60b9909Spgoyette 				signo = 0;
1248a60b9909Spgoyette 			else
1249a60b9909Spgoyette #endif
1250a60b9909Spgoyette 			{
1251a60b9909Spgoyette 				signo = tmp;
1252a60b9909Spgoyette 				tmp = 0;	/* don't search for LWP */
1253a60b9909Spgoyette 			}
1254c18c9a67Skamil 		} else if (tmp == INT_MIN) {
1255c18c9a67Skamil 			error = ESRCH;
1256c18c9a67Skamil 			break;
1257c18c9a67Skamil 		} else {
1258a60b9909Spgoyette 			tmp = -tmp;
1259c18c9a67Skamil 		}
1260a60b9909Spgoyette 
1261a60b9909Spgoyette 		if (tmp > 0) {
1262a60b9909Spgoyette 			if (req == PT_DETACH) {
1263a60b9909Spgoyette 				error = EINVAL;
1264a60b9909Spgoyette 				break;
1265a60b9909Spgoyette 			}
1266a60b9909Spgoyette 			lwp_delref2 (lt);
1267a60b9909Spgoyette 			lt = lwp_find(t, tmp);
1268a60b9909Spgoyette 			if (lt == NULL) {
1269a60b9909Spgoyette 				error = ESRCH;
1270a60b9909Spgoyette 				break;
1271a60b9909Spgoyette 			}
1272a60b9909Spgoyette 			lwp_addref(lt);
1273a60b9909Spgoyette 			resume_all = 0;
1274a60b9909Spgoyette 			signo = 0;
1275a60b9909Spgoyette 		}
1276a60b9909Spgoyette 
1277a60b9909Spgoyette 		/*
1278a60b9909Spgoyette 		 * From the 4.4BSD PRM:
1279a60b9909Spgoyette 		 * "The data argument is taken as a signal number and the
1280a60b9909Spgoyette 		 * child's execution continues at location addr as if it
1281a60b9909Spgoyette 		 * incurred that signal.  Normally the signal number will
1282a60b9909Spgoyette 		 * be either 0 to indicate that the signal that caused the
1283a60b9909Spgoyette 		 * stop should be ignored, or that value fetched out of
1284a60b9909Spgoyette 		 * the process's image indicating which signal caused
1285a60b9909Spgoyette 		 * the stop.  If addr is (int *)1 then execution continues
1286a60b9909Spgoyette 		 * from where it stopped."
1287a60b9909Spgoyette 		 */
1288a60b9909Spgoyette 
1289a60b9909Spgoyette 		/* Check that the data is a valid signal number or zero. */
1290a60b9909Spgoyette 		if (signo < 0 || signo >= NSIG) {
1291a60b9909Spgoyette 			error = EINVAL;
1292a60b9909Spgoyette 			break;
1293a60b9909Spgoyette 		}
1294a60b9909Spgoyette 
1295f9b2093dSkamil 		/* Prevent process deadlock */
1296f9b2093dSkamil 		if (resume_all) {
1297f9b2093dSkamil #ifdef PT_STEP
1298f9b2093dSkamil 			if (req == PT_STEP) {
1299a35a4fe3Skamil 				if (lt->l_flag &
1300a35a4fe3Skamil 				    (LW_WSUSPEND | LW_DBGSUSPEND)) {
1301f9b2093dSkamil 					error = EDEADLK;
1302f9b2093dSkamil 					break;
1303f9b2093dSkamil 				}
1304f9b2093dSkamil 			} else
1305f9b2093dSkamil #endif
1306f9b2093dSkamil 			{
1307f9b2093dSkamil 				error = EDEADLK;
1308f9b2093dSkamil 				LIST_FOREACH(lt2, &t->p_lwps, l_sibling) {
1309a35a4fe3Skamil 					if ((lt2->l_flag &
1310a35a4fe3Skamil 					    (LW_WSUSPEND | LW_DBGSUSPEND)) == 0
1311a35a4fe3Skamil 					    ) {
1312f9b2093dSkamil 						error = 0;
1313f9b2093dSkamil 						break;
1314f9b2093dSkamil 					}
1315f9b2093dSkamil 				}
1316f9b2093dSkamil 				if (error != 0)
1317f9b2093dSkamil 					break;
1318f9b2093dSkamil 			}
1319f9b2093dSkamil 		} else {
132060274cddSkamil 			if (lt->l_flag & (LW_WSUSPEND | LW_DBGSUSPEND)) {
1321f9b2093dSkamil 				error = EDEADLK;
1322f9b2093dSkamil 				break;
1323f9b2093dSkamil 			}
1324f9b2093dSkamil 		}
1325f9b2093dSkamil 
1326ff3a41b3Skamil 		/*
1327815185c6Skamil 		 * Reject setting program counter to 0x0 if VA0 is disabled.
1328ff3a41b3Skamil 		 *
132971c21268Skamil 		 * Not all kernels implement this feature to set Program
133071c21268Skamil 		 * Counter in one go in PT_CONTINUE and similar operations.
133171c21268Skamil 		 * This causes portability issues as passing address 0x0
133271c21268Skamil 		 * on these kernels is no-operation, but can cause failure
133371c21268Skamil 		 * in most cases on NetBSD.
1334ff3a41b3Skamil 		 */
133571c21268Skamil 		if (user_va0_disable && addr == 0) {
1336ff3a41b3Skamil 			error = EINVAL;
1337ff3a41b3Skamil 			break;
1338ff3a41b3Skamil 		}
1339ff3a41b3Skamil 
1340a60b9909Spgoyette 		/* If the address parameter is not (int *)1, set the pc. */
1341a60b9909Spgoyette 		if ((int *)addr != (int *)1) {
1342a60b9909Spgoyette 			error = process_set_pc(lt, addr);
1343a60b9909Spgoyette 			if (error != 0)
1344a60b9909Spgoyette 				break;
1345a60b9909Spgoyette 		}
1346a60b9909Spgoyette #ifdef PT_STEP
1347a60b9909Spgoyette 		/*
1348a60b9909Spgoyette 		 * Arrange for a single-step, if that's requested and possible.
1349a60b9909Spgoyette 		 * More precisely, set the single step status as requested for
1350a60b9909Spgoyette 		 * the requested thread, and clear it for other threads.
1351a60b9909Spgoyette 		 */
1352a60b9909Spgoyette 		LIST_FOREACH(lt2, &t->p_lwps, l_sibling) {
13530b0d79c7Skamil 			error = process_sstep(lt2,
13540b0d79c7Skamil 			    ISSET(lt2->l_pflag, LP_SINGLESTEP));
13550b0d79c7Skamil 			if (error)
13560b0d79c7Skamil 				break;
1357a60b9909Spgoyette 		}
13580b0d79c7Skamil 		if (error)
13590b0d79c7Skamil 			break;
136005ffc73cSkamil 		error = process_sstep(lt,
136105ffc73cSkamil 		    ISSET(lt->l_pflag, LP_SINGLESTEP) || req == PT_STEP);
1362a60b9909Spgoyette 		if (error)
1363a60b9909Spgoyette 			break;
1364a60b9909Spgoyette #endif
1365a60b9909Spgoyette 		if (req == PT_DETACH) {
13660d3a6b1dSkamil 			CLR(t->p_slflag,
13670d3a6b1dSkamil 			    PSL_TRACED|PSL_TRACEDCHILD|PSL_SYSCALL);
1368a60b9909Spgoyette 
136948b46cedSkamil 			/* clear sigpass mask */
137048b46cedSkamil 			sigemptyset(&t->p_sigctx.ps_sigpass);
137148b46cedSkamil 
1372a60b9909Spgoyette 			/* give process back to original parent or init */
1373a60b9909Spgoyette 			if (t->p_opptr != t->p_pptr) {
1374a60b9909Spgoyette 				struct proc *pp = t->p_opptr;
1375a60b9909Spgoyette 				proc_reparent(t, pp ? pp : initproc);
1376a60b9909Spgoyette 			}
1377a60b9909Spgoyette 
1378a60b9909Spgoyette 			/* not being traced any more */
1379a60b9909Spgoyette 			t->p_opptr = NULL;
138005ffc73cSkamil 
138105ffc73cSkamil 			/* clear single step */
138205ffc73cSkamil 			LIST_FOREACH(lt2, &t->p_lwps, l_sibling) {
138305ffc73cSkamil 				CLR(lt2->l_pflag, LP_SINGLESTEP);
138405ffc73cSkamil 			}
138505ffc73cSkamil 			CLR(lt->l_pflag, LP_SINGLESTEP);
1386a60b9909Spgoyette 		}
1387a60b9909Spgoyette 	sendsig:
13882e7e73e2Skamil 		error = ptrace_sendsig(l, req, t, lt, signo, resume_all);
1389a60b9909Spgoyette 		break;
1390a60b9909Spgoyette 
1391a60b9909Spgoyette 	case PT_SYSCALLEMU:
1392a60b9909Spgoyette 		if (!ISSET(t->p_slflag, PSL_SYSCALL) || t->p_stat != SSTOP) {
1393a60b9909Spgoyette 			error = EINVAL;
1394a60b9909Spgoyette 			break;
1395a60b9909Spgoyette 		}
1396a60b9909Spgoyette 		SET(t->p_slflag, PSL_SYSCALLEMU);
1397a60b9909Spgoyette 		break;
1398a60b9909Spgoyette 
139905ffc73cSkamil #ifdef PT_STEP
140005ffc73cSkamil 	case PT_SETSTEP:
140105ffc73cSkamil 		write = 1;
140205ffc73cSkamil 
1403fbffadb9Smrg 		/* FALLTHROUGH */
140405ffc73cSkamil 	case PT_CLEARSTEP:
140505ffc73cSkamil 		/* write = 0 done above. */
14062b32abf1Schristos 		if ((error = ptrace_update_lwp(t, &lt, data)) != 0)
140705ffc73cSkamil 			break;
140805ffc73cSkamil 
14092b32abf1Schristos 		if (write)
141005ffc73cSkamil 			SET(lt->l_pflag, LP_SINGLESTEP);
141105ffc73cSkamil 		else
141205ffc73cSkamil 			CLR(lt->l_pflag, LP_SINGLESTEP);
141305ffc73cSkamil 		break;
141405ffc73cSkamil #endif
141505ffc73cSkamil 
1416a60b9909Spgoyette 	case PT_KILL:
1417a60b9909Spgoyette 		/* just send the process a KILL signal. */
1418a60b9909Spgoyette 		signo = SIGKILL;
1419a60b9909Spgoyette 		goto sendsig;	/* in PT_CONTINUE, above. */
1420a60b9909Spgoyette 
14212e7e73e2Skamil 	case PT_STOP:
14222e7e73e2Skamil 		/* just send the process a STOP signal. */
14232e7e73e2Skamil 		signo = SIGSTOP;
14242e7e73e2Skamil 		goto sendsig;	/* in PT_CONTINUE, above. */
14252e7e73e2Skamil 
1426a60b9909Spgoyette 	case PT_ATTACH:
1427a60b9909Spgoyette 		/*
1428a60b9909Spgoyette 		 * Go ahead and set the trace flag.
1429a60b9909Spgoyette 		 * Save the old parent (it's reset in
1430a60b9909Spgoyette 		 *   _DETACH, and also in kern_exit.c:wait4()
1431a60b9909Spgoyette 		 * Reparent the process so that the tracing
1432a60b9909Spgoyette 		 *   proc gets to see all the action.
1433a60b9909Spgoyette 		 * Stop the target.
1434a60b9909Spgoyette 		 */
14359b5ab015Schristos 		proc_changeparent(t, p);
1436f1999451Schristos 		signo = SIGSTOP;
1437a60b9909Spgoyette 		goto sendsig;
1438a60b9909Spgoyette 
1439a60b9909Spgoyette 	case PT_GET_EVENT_MASK:
14402b32abf1Schristos 		error = ptrace_get_event_mask(t, addr, data);
1441a60b9909Spgoyette 		break;
1442a60b9909Spgoyette 
1443a60b9909Spgoyette 	case PT_SET_EVENT_MASK:
14442b32abf1Schristos 		error = ptrace_set_event_mask(t, addr, data);
1445a60b9909Spgoyette 		break;
1446a60b9909Spgoyette 
1447a60b9909Spgoyette 	case PT_GET_PROCESS_STATE:
14482b32abf1Schristos 		error = ptrace_get_process_state(t, addr, data);
1449a60b9909Spgoyette 		break;
1450a60b9909Spgoyette 
1451a60b9909Spgoyette 	case PT_LWPINFO:
14522b32abf1Schristos 		error = ptrace_lwpinfo(t, &lt, addr, data);
1453a60b9909Spgoyette 		break;
1454a60b9909Spgoyette 
1455e4281b20Skamil 	case PT_SET_SIGINFO:
1456f818d5c4Schristos 		error = ptrace_set_siginfo(t, &lt, ptm, addr, data);
1457e4281b20Skamil 		break;
1458e4281b20Skamil 
1459e4281b20Skamil 	case PT_GET_SIGINFO:
1460f818d5c4Schristos 		error = ptrace_get_siginfo(t, ptm, addr, data);
1461e4281b20Skamil 		break;
1462e4281b20Skamil 
1463f9b2093dSkamil 	case PT_RESUME:
1464f9b2093dSkamil 	case PT_SUSPEND:
14652b32abf1Schristos 		error = ptrace_startstop(t, &lt, req, addr, data);
1466f9b2093dSkamil 		break;
1467f9b2093dSkamil 
14684f79a484Skamil 	case PT_LWPSTATUS:
14694f79a484Skamil 		error = ptrace_lwpstatus(t, ptm, &lt, addr, data, false);
14704f79a484Skamil 		break;
14714f79a484Skamil 
14724f79a484Skamil 	case PT_LWPNEXT:
14734f79a484Skamil 		error = ptrace_lwpstatus(t, ptm, &lt, addr, data, true);
14744f79a484Skamil 		break;
14754f79a484Skamil 
147648b46cedSkamil 	case PT_SET_SIGPASS:
147748b46cedSkamil 		error = ptrace_set_sigpass(t, addr, data);
147848b46cedSkamil 		break;
147948b46cedSkamil 
148048b46cedSkamil 	case PT_GET_SIGPASS:
148148b46cedSkamil 		error = ptrace_get_sigpass(t, addr, data);
148248b46cedSkamil 		break;
148348b46cedSkamil 
148428f60f0eSchristos #ifdef PT_REGISTERS
148528f60f0eSchristos 	case_PT_SETREGS
148628f60f0eSchristos 	case_PT_GETREGS
148728f60f0eSchristos 	case_PT_SETFPREGS
148828f60f0eSchristos 	case_PT_GETFPREGS
148928f60f0eSchristos 	case_PT_SETDBREGS
149028f60f0eSchristos 	case_PT_GETDBREGS
14912b32abf1Schristos 		error = ptrace_regs(l, &lt, req, ptm, addr, data);
1492241cf91dSkamil 		break;
1493241cf91dSkamil #endif
1494241cf91dSkamil 
1495a60b9909Spgoyette #ifdef __HAVE_PTRACE_MACHDEP
1496a60b9909Spgoyette 	PTRACE_MACHDEP_REQUEST_CASES
1497f4ced9b4Smgorny 		error = ptrace_machdep_dorequest(l, &lt, req, addr, data);
1498a60b9909Spgoyette 		break;
1499a60b9909Spgoyette #endif
1500a60b9909Spgoyette 	}
1501a60b9909Spgoyette 
15022b32abf1Schristos out:
1503a60b9909Spgoyette 	if (pheld) {
1504a60b9909Spgoyette 		mutex_exit(t->p_lock);
15050eaaa024Sad 		mutex_exit(&proc_lock);
1506a60b9909Spgoyette 	}
1507a60b9909Spgoyette 	if (lt != NULL)
1508a60b9909Spgoyette 		lwp_delref(lt);
1509e4c2eafeSmaxv 	if (locked)
1510a60b9909Spgoyette 		rw_exit(&t->p_reflock);
1511a60b9909Spgoyette 
1512a60b9909Spgoyette 	return error;
1513a60b9909Spgoyette }
1514a60b9909Spgoyette 
1515a60b9909Spgoyette static int
1516a60b9909Spgoyette process_auxv_offset(struct proc *p, struct uio *uio)
1517a60b9909Spgoyette {
1518a60b9909Spgoyette 	struct ps_strings pss;
1519a60b9909Spgoyette 	int error;
1520a60b9909Spgoyette 	off_t off = (off_t)p->p_psstrp;
1521a60b9909Spgoyette 
1522a60b9909Spgoyette 	if ((error = copyin_psstrings(p, &pss)) != 0)
1523a60b9909Spgoyette 		return error;
1524a60b9909Spgoyette 
1525a60b9909Spgoyette 	if (pss.ps_envstr == NULL)
1526a60b9909Spgoyette 		return EIO;
1527a60b9909Spgoyette 
152882833002Srin #ifdef COMPAT_NETBSD32
152982833002Srin 	if (p->p_flag & PK_32)
153082833002Srin 		uio->uio_offset += (off_t)((vaddr_t)pss.ps_envstr +
153182833002Srin 		    sizeof(uint32_t) * (pss.ps_nenvstr + 1));
153282833002Srin 	else
153382833002Srin #endif
153482833002Srin 		uio->uio_offset += (off_t)(vaddr_t)(pss.ps_envstr +
153582833002Srin 		    pss.ps_nenvstr + 1);
153682833002Srin 
1537a60b9909Spgoyette #ifdef __MACHINE_STACK_GROWS_UP
1538a60b9909Spgoyette 	if (uio->uio_offset < off)
1539a60b9909Spgoyette 		return EIO;
1540a60b9909Spgoyette #else
1541a60b9909Spgoyette 	if (uio->uio_offset > off)
1542a60b9909Spgoyette 		return EIO;
1543a60b9909Spgoyette 	if ((uio->uio_offset + uio->uio_resid) > off)
1544a60b9909Spgoyette 		uio->uio_resid = off - uio->uio_offset;
1545a60b9909Spgoyette #endif
1546a60b9909Spgoyette 	return 0;
1547a60b9909Spgoyette }
1548484b4a02Skamil 
1549484b4a02Skamil MODULE(MODULE_CLASS_EXEC, ptrace_common, NULL);
1550484b4a02Skamil 
1551484b4a02Skamil static int
1552355e5900Spgoyette ptrace_common_init(void)
1553355e5900Spgoyette {
1554355e5900Spgoyette 
1555e78beab4Spgoyette #if 0
1556e78beab4Spgoyette 	mutex_init(&ptrace_mtx, MUTEX_DEFAULT, IPL_NONE);
1557e78beab4Spgoyette 	cv_init(&ptrace_cv, "ptracecb");
1558e78beab4Spgoyette 	ptrace_cbref = 0;
1559e78beab4Spgoyette #endif
1560e78beab4Spgoyette 	ptrace_listener = kauth_listen_scope(KAUTH_SCOPE_PROCESS,
1561e78beab4Spgoyette 	    ptrace_listener_cb, NULL);
1562355e5900Spgoyette 	return 0;
1563355e5900Spgoyette }
1564355e5900Spgoyette 
1565355e5900Spgoyette static int
1566355e5900Spgoyette ptrace_common_fini(void)
1567355e5900Spgoyette {
1568355e5900Spgoyette 
1569e78beab4Spgoyette 	kauth_unlisten_scope(ptrace_listener);
1570e78beab4Spgoyette 
1571e78beab4Spgoyette #if 0
1572e78beab4Spgoyette 	/* Make sure no-one is executing our kauth listener */
1573e78beab4Spgoyette 
1574e78beab4Spgoyette 	mutex_enter(&ptrace_mtx);
1575e78beab4Spgoyette 	while (ptrace_cbref != 0)
1576e78beab4Spgoyette 		cv_wait(&ptrace_cv, &ptrace_mtx);
1577e78beab4Spgoyette 	mutex_exit(&ptrace_mtx);
1578e78beab4Spgoyette 	mutex_destroy(&ptrace_mtx);
1579e78beab4Spgoyette 	cv_destroy(&ptrace_cv);
1580e78beab4Spgoyette #endif
1581e78beab4Spgoyette 
1582355e5900Spgoyette 	return 0;
1583355e5900Spgoyette }
1584355e5900Spgoyette 
1585355e5900Spgoyette static int
1586484b4a02Skamil ptrace_common_modcmd(modcmd_t cmd, void *arg)
1587484b4a02Skamil {
1588484b4a02Skamil         int error;
1589484b4a02Skamil 
1590484b4a02Skamil         switch (cmd) {
1591484b4a02Skamil         case MODULE_CMD_INIT:
1592355e5900Spgoyette                 error = ptrace_common_init();
1593484b4a02Skamil                 break;
1594484b4a02Skamil         case MODULE_CMD_FINI:
1595355e5900Spgoyette                 error = ptrace_common_fini();
1596484b4a02Skamil                 break;
1597484b4a02Skamil         default:
1598484b4a02Skamil 		ptrace_hooks();
1599484b4a02Skamil                 error = ENOTTY;
1600484b4a02Skamil                 break;
1601484b4a02Skamil         }
1602484b4a02Skamil         return error;
1603484b4a02Skamil }
1604