2021-08-09 23:49:08 +03:00
|
|
|
/* $NetBSD: sys_ptrace_common.c,v 1.92 2021/08/09 20:49:10 andvar Exp $ */
|
2016-11-02 03:11:59 +03:00
|
|
|
|
|
|
|
/*-
|
|
|
|
* Copyright (c) 2008, 2009 The NetBSD Foundation, Inc.
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* This code is derived from software contributed to The NetBSD Foundation
|
|
|
|
* by Andrew Doran.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
|
|
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
|
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
|
|
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*-
|
|
|
|
* Copyright (c) 1982, 1986, 1989, 1993
|
|
|
|
* The Regents of the University of California. All rights reserved.
|
|
|
|
* (c) UNIX System Laboratories, Inc.
|
|
|
|
* All or some portions of this file are derived from material licensed
|
|
|
|
* to the University of California by American Telephone and Telegraph
|
|
|
|
* Co. or Unix System Laboratories, Inc. and are reproduced herein with
|
|
|
|
* the permission of UNIX System Laboratories, Inc.
|
|
|
|
*
|
|
|
|
* This code is derived from software contributed to Berkeley by
|
|
|
|
* Jan-Simon Pendry.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
* 3. Neither the name of the University nor the names of its contributors
|
|
|
|
* may be used to endorse or promote products derived from this software
|
|
|
|
* without specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
|
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
|
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
|
|
* SUCH DAMAGE.
|
|
|
|
*
|
|
|
|
* from: @(#)sys_process.c 8.1 (Berkeley) 6/10/93
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*-
|
|
|
|
* Copyright (c) 1993 Jan-Simon Pendry.
|
|
|
|
* Copyright (c) 1994 Christopher G. Demetriou. All rights reserved.
|
|
|
|
*
|
|
|
|
* This code is derived from software contributed to Berkeley by
|
|
|
|
* Jan-Simon Pendry.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
|
|
* must display the following acknowledgement:
|
|
|
|
* This product includes software developed by the University of
|
|
|
|
* California, Berkeley and its contributors.
|
|
|
|
* 4. Neither the name of the University nor the names of its contributors
|
|
|
|
* may be used to endorse or promote products derived from this software
|
|
|
|
* without specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
|
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
|
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
|
|
* SUCH DAMAGE.
|
|
|
|
*
|
|
|
|
* from: @(#)sys_process.c 8.1 (Berkeley) 6/10/93
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <sys/cdefs.h>
|
2021-08-09 23:49:08 +03:00
|
|
|
__KERNEL_RCSID(0, "$NetBSD: sys_ptrace_common.c,v 1.92 2021/08/09 20:49:10 andvar Exp $");
|
2016-11-02 03:11:59 +03:00
|
|
|
|
|
|
|
#ifdef _KERNEL_OPT
|
|
|
|
#include "opt_ptrace.h"
|
|
|
|
#include "opt_ktrace.h"
|
|
|
|
#include "opt_pax.h"
|
2017-12-07 18:21:34 +03:00
|
|
|
#include "opt_compat_netbsd32.h"
|
2016-11-02 03:11:59 +03:00
|
|
|
#endif
|
|
|
|
|
2017-12-08 18:54:40 +03:00
|
|
|
#if defined(__HAVE_COMPAT_NETBSD32) && !defined(COMPAT_NETBSD32) \
|
|
|
|
&& !defined(_RUMPKERNEL)
|
|
|
|
#define COMPAT_NETBSD32
|
|
|
|
#endif
|
|
|
|
|
2016-11-02 03:11:59 +03:00
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/systm.h>
|
|
|
|
#include <sys/proc.h>
|
|
|
|
#include <sys/errno.h>
|
|
|
|
#include <sys/exec.h>
|
|
|
|
#include <sys/pax.h>
|
|
|
|
#include <sys/ptrace.h>
|
|
|
|
#include <sys/uio.h>
|
|
|
|
#include <sys/ras.h>
|
|
|
|
#include <sys/kmem.h>
|
|
|
|
#include <sys/kauth.h>
|
|
|
|
#include <sys/mount.h>
|
|
|
|
#include <sys/syscallargs.h>
|
2020-10-19 17:52:19 +03:00
|
|
|
#include <sys/module.h>
|
2016-11-02 03:11:59 +03:00
|
|
|
#include <sys/condvar.h>
|
|
|
|
#include <sys/mutex.h>
|
2019-11-10 17:20:50 +03:00
|
|
|
#include <sys/compat_stub.h>
|
2016-11-02 03:11:59 +03:00
|
|
|
|
|
|
|
#include <uvm/uvm_extern.h>
|
|
|
|
|
|
|
|
#include <machine/reg.h>
|
|
|
|
|
2018-06-23 06:32:48 +03:00
|
|
|
# ifdef PTRACE_DEBUG
|
2016-11-02 03:11:59 +03:00
|
|
|
# define DPRINTF(a) uprintf a
|
|
|
|
# else
|
|
|
|
# define DPRINTF(a)
|
|
|
|
# endif
|
|
|
|
|
|
|
|
static kauth_listener_t ptrace_listener;
|
|
|
|
static int process_auxv_offset(struct proc *, struct uio *);
|
|
|
|
|
2019-05-25 06:20:43 +03:00
|
|
|
extern int user_va0_disable;
|
|
|
|
|
2016-11-02 03:11:59 +03:00
|
|
|
#if 0
|
|
|
|
static int ptrace_cbref;
|
|
|
|
static kmutex_t ptrace_mtx;
|
|
|
|
static kcondvar_t ptrace_cv;
|
|
|
|
#endif
|
|
|
|
|
2017-12-17 18:43:27 +03:00
|
|
|
#ifdef PT_GETREGS
|
|
|
|
# define case_PT_GETREGS case PT_GETREGS:
|
|
|
|
#else
|
|
|
|
# define case_PT_GETREGS
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef PT_SETREGS
|
|
|
|
# define case_PT_SETREGS case PT_SETREGS:
|
|
|
|
#else
|
|
|
|
# define case_PT_SETREGS
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef PT_GETFPREGS
|
|
|
|
# define case_PT_GETFPREGS case PT_GETFPREGS:
|
|
|
|
#else
|
|
|
|
# define case_PT_GETFPREGS
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef PT_SETFPREGS
|
|
|
|
# define case_PT_SETFPREGS case PT_SETFPREGS:
|
|
|
|
#else
|
|
|
|
# define case_PT_SETFPREGS
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef PT_GETDBREGS
|
|
|
|
# define case_PT_GETDBREGS case PT_GETDBREGS:
|
|
|
|
#else
|
|
|
|
# define case_PT_GETDBREGS
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef PT_SETDBREGS
|
|
|
|
# define case_PT_SETDBREGS case PT_SETDBREGS:
|
|
|
|
#else
|
|
|
|
# define case_PT_SETDBREGS
|
|
|
|
#endif
|
|
|
|
|
2016-11-02 03:11:59 +03:00
|
|
|
static int
|
|
|
|
ptrace_listener_cb(kauth_cred_t cred, kauth_action_t action, void *cookie,
|
|
|
|
void *arg0, void *arg1, void *arg2, void *arg3)
|
|
|
|
{
|
|
|
|
struct proc *p;
|
|
|
|
int result;
|
2018-04-08 17:46:32 +03:00
|
|
|
#ifdef PT_SETDBREGS
|
|
|
|
extern int user_set_dbregs;
|
|
|
|
#endif
|
2016-11-02 03:11:59 +03:00
|
|
|
|
|
|
|
result = KAUTH_RESULT_DEFER;
|
|
|
|
p = arg0;
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
mutex_enter(&ptrace_mtx);
|
|
|
|
ptrace_cbref++;
|
|
|
|
mutex_exit(&ptrace_mtx);
|
|
|
|
#endif
|
|
|
|
if (action != KAUTH_PROCESS_PTRACE)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
switch ((u_long)arg1) {
|
2018-04-08 17:46:32 +03:00
|
|
|
#ifdef PT_SETDBREGS
|
|
|
|
case_PT_SETDBREGS
|
|
|
|
if (kauth_cred_getuid(cred) != 0 && user_set_dbregs == 0) {
|
|
|
|
result = KAUTH_RESULT_DENY;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
#endif
|
2019-02-03 06:19:25 +03:00
|
|
|
/* FALLTHROUGH */
|
2016-11-02 03:11:59 +03:00
|
|
|
case PT_TRACE_ME:
|
|
|
|
case PT_ATTACH:
|
|
|
|
case PT_WRITE_I:
|
|
|
|
case PT_WRITE_D:
|
|
|
|
case PT_READ_I:
|
|
|
|
case PT_READ_D:
|
|
|
|
case PT_IO:
|
2017-12-17 18:43:27 +03:00
|
|
|
case_PT_GETREGS
|
|
|
|
case_PT_SETREGS
|
|
|
|
case_PT_GETFPREGS
|
|
|
|
case_PT_SETFPREGS
|
|
|
|
case_PT_GETDBREGS
|
2016-11-02 03:11:59 +03:00
|
|
|
case PT_SET_EVENT_MASK:
|
|
|
|
case PT_GET_EVENT_MASK:
|
|
|
|
case PT_GET_PROCESS_STATE:
|
2017-01-07 01:53:17 +03:00
|
|
|
case PT_SET_SIGINFO:
|
|
|
|
case PT_GET_SIGINFO:
|
2016-11-02 03:11:59 +03:00
|
|
|
#ifdef __HAVE_PTRACE_MACHDEP
|
|
|
|
PTRACE_MACHDEP_REQUEST_CASES
|
|
|
|
#endif
|
|
|
|
if (kauth_cred_getuid(cred) != kauth_cred_getuid(p->p_cred) ||
|
|
|
|
ISSET(p->p_flag, PK_SUGID)) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
result = KAUTH_RESULT_ALLOW;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
#ifdef PT_STEP
|
|
|
|
case PT_STEP:
|
2017-04-08 03:25:49 +03:00
|
|
|
case PT_SETSTEP:
|
|
|
|
case PT_CLEARSTEP:
|
2016-11-02 03:11:59 +03:00
|
|
|
#endif
|
|
|
|
case PT_CONTINUE:
|
|
|
|
case PT_KILL:
|
|
|
|
case PT_DETACH:
|
|
|
|
case PT_LWPINFO:
|
|
|
|
case PT_SYSCALL:
|
|
|
|
case PT_SYSCALLEMU:
|
|
|
|
case PT_DUMPCORE:
|
2017-02-23 02:43:43 +03:00
|
|
|
case PT_RESUME:
|
|
|
|
case PT_SUSPEND:
|
2019-10-09 16:19:43 +03:00
|
|
|
case PT_STOP:
|
2019-12-24 17:50:59 +03:00
|
|
|
case PT_LWPSTATUS:
|
|
|
|
case PT_LWPNEXT:
|
2020-05-14 16:32:15 +03:00
|
|
|
case PT_SET_SIGPASS:
|
|
|
|
case PT_GET_SIGPASS:
|
2016-11-02 03:11:59 +03:00
|
|
|
result = KAUTH_RESULT_ALLOW;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
#if 0
|
|
|
|
mutex_enter(&ptrace_mtx);
|
|
|
|
if (--ptrace_cbref == 0)
|
|
|
|
cv_broadcast(&ptrace_cv);
|
|
|
|
mutex_exit(&ptrace_mtx);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2017-12-17 07:35:21 +03:00
|
|
|
static struct proc *
|
|
|
|
ptrace_find(struct lwp *l, int req, pid_t pid)
|
2016-11-02 03:11:59 +03:00
|
|
|
{
|
2017-12-17 07:35:21 +03:00
|
|
|
struct proc *t;
|
2016-11-02 03:11:59 +03:00
|
|
|
|
|
|
|
/* "A foolish consistency..." XXX */
|
|
|
|
if (req == PT_TRACE_ME) {
|
2017-12-17 07:35:21 +03:00
|
|
|
t = l->l_proc;
|
2016-11-02 03:11:59 +03:00
|
|
|
mutex_enter(t->p_lock);
|
2017-12-17 07:35:21 +03:00
|
|
|
return t;
|
|
|
|
}
|
2016-11-02 03:11:59 +03:00
|
|
|
|
2017-12-17 07:35:21 +03:00
|
|
|
/* Find the process we're supposed to be operating on. */
|
|
|
|
t = proc_find(pid);
|
|
|
|
if (t == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* XXX-elad */
|
|
|
|
mutex_enter(t->p_lock);
|
|
|
|
int error = kauth_authorize_process(l->l_cred, KAUTH_PROCESS_CANSEE,
|
|
|
|
t, KAUTH_ARG(KAUTH_REQ_PROCESS_CANSEE_ENTRY), NULL, NULL);
|
|
|
|
if (error) {
|
|
|
|
mutex_exit(t->p_lock);
|
|
|
|
return NULL;
|
2016-11-02 03:11:59 +03:00
|
|
|
}
|
2017-12-17 07:35:21 +03:00
|
|
|
return t;
|
|
|
|
}
|
2016-11-02 03:11:59 +03:00
|
|
|
|
2017-12-17 07:35:21 +03:00
|
|
|
static int
|
2019-06-29 14:37:17 +03:00
|
|
|
ptrace_allowed(struct lwp *l, int req, struct proc *t, struct proc *p,
|
|
|
|
bool *locked)
|
2017-12-17 07:35:21 +03:00
|
|
|
{
|
2019-06-29 14:37:17 +03:00
|
|
|
*locked = false;
|
|
|
|
|
2016-11-02 03:11:59 +03:00
|
|
|
/*
|
|
|
|
* Grab a reference on the process to prevent it from execing or
|
|
|
|
* exiting.
|
|
|
|
*/
|
2017-12-17 07:35:21 +03:00
|
|
|
if (!rw_tryenter(&t->p_reflock, RW_READER))
|
2016-11-02 03:11:59 +03:00
|
|
|
return EBUSY;
|
|
|
|
|
2019-06-29 14:37:17 +03:00
|
|
|
*locked = true;
|
|
|
|
|
2016-11-02 03:11:59 +03:00
|
|
|
/* Make sure we can operate on it. */
|
|
|
|
switch (req) {
|
2017-12-17 18:43:27 +03:00
|
|
|
case PT_TRACE_ME:
|
Harden the NetBSD PT_TRACE_ME operation
You can't say to the parent of a process to start tracing if:
(1) the parent is initproc,
(2) the child is already traced.
Rationale:
(1) - It has a side effect of being an anti-debugger functionality,
as we cannot kill initproc (PID1) and reset the traced flag.
- initproc is not a debugger, raising debugging events from a child
to initproc can result in at least a stopped/hanging process
in the system.
(2) - It does not make sense to be simultanously traced by two debuggers.
- It does not make sense to be traced twice by the same debugger.
Permit enable tracing for a parent that has been chroot(8)ed, as this is
harmless and the parent is already monitoring for child signals.
The same semantics exist in FreeBSD.
If you are looking for an antidebugging trick for old NetBSD (pre 8.0)
or other popular kernels, here is an example:
$ cat antidebug.c
#include <sys/types.h>
#include <sys/ptrace.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
int
main(int argc, char **argv)
{
pid_t child;
int rv;
int n = 0;
child = fork();
if (child == 0) {
while (getppid() != 1)
continue;
rv = ptrace(PT_TRACE_ME, 0, 0, 0);
if (rv != 0)
abort();
printf("Try to detach to me with a debugger!! ");
printf("haha My PID is %d\n", getpid());
while (1) {
printf("%d\n", n++);
sleep(1);
}
}
exit(0);
}
A developer is no longer able to attach GDB, strace or LLDB to this program
without killing the initproc (your favourite system daemon).. this action
would be fatal for the operation of the whole Operating System stability.
Examples from a current non-NetBSD popular kernel:
$ ps -o ppid= -p 17904
1
$ strace -p 17904
strace: attach: ptrace(PTRACE_SEIZE, 17904): Operation not permitted
$ gdb -p 17904
[...]
Attaching to process 17904
warning: process 17904 is already traced by process 1
ptrace: Operation not permitted.
(gdb)
$ lldb-3.9 -p 17904
(lldb) process attach --pid 17904
error: attach failed: unable to attach
On NetBSD 8.0 and newer it is now guaranteed to have an option to kill
a malevolent (fake?) debugger and attach with a new tracer to the process.
Sponsored by <The NetBSD Foundation>
2018-04-29 07:28:09 +03:00
|
|
|
/*
|
|
|
|
* You can't say to the parent of a process to start tracing if:
|
|
|
|
* (1) the parent is initproc,
|
|
|
|
*/
|
|
|
|
if (p->p_pptr == initproc)
|
|
|
|
return EPERM;
|
|
|
|
|
|
|
|
/*
|
2018-05-01 17:09:53 +03:00
|
|
|
* (2) the process is initproc, or
|
|
|
|
*/
|
|
|
|
if (p == initproc)
|
|
|
|
return EPERM;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* (3) the child is already traced.
|
Harden the NetBSD PT_TRACE_ME operation
You can't say to the parent of a process to start tracing if:
(1) the parent is initproc,
(2) the child is already traced.
Rationale:
(1) - It has a side effect of being an anti-debugger functionality,
as we cannot kill initproc (PID1) and reset the traced flag.
- initproc is not a debugger, raising debugging events from a child
to initproc can result in at least a stopped/hanging process
in the system.
(2) - It does not make sense to be simultanously traced by two debuggers.
- It does not make sense to be traced twice by the same debugger.
Permit enable tracing for a parent that has been chroot(8)ed, as this is
harmless and the parent is already monitoring for child signals.
The same semantics exist in FreeBSD.
If you are looking for an antidebugging trick for old NetBSD (pre 8.0)
or other popular kernels, here is an example:
$ cat antidebug.c
#include <sys/types.h>
#include <sys/ptrace.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
int
main(int argc, char **argv)
{
pid_t child;
int rv;
int n = 0;
child = fork();
if (child == 0) {
while (getppid() != 1)
continue;
rv = ptrace(PT_TRACE_ME, 0, 0, 0);
if (rv != 0)
abort();
printf("Try to detach to me with a debugger!! ");
printf("haha My PID is %d\n", getpid());
while (1) {
printf("%d\n", n++);
sleep(1);
}
}
exit(0);
}
A developer is no longer able to attach GDB, strace or LLDB to this program
without killing the initproc (your favourite system daemon).. this action
would be fatal for the operation of the whole Operating System stability.
Examples from a current non-NetBSD popular kernel:
$ ps -o ppid= -p 17904
1
$ strace -p 17904
strace: attach: ptrace(PTRACE_SEIZE, 17904): Operation not permitted
$ gdb -p 17904
[...]
Attaching to process 17904
warning: process 17904 is already traced by process 1
ptrace: Operation not permitted.
(gdb)
$ lldb-3.9 -p 17904
(lldb) process attach --pid 17904
error: attach failed: unable to attach
On NetBSD 8.0 and newer it is now guaranteed to have an option to kill
a malevolent (fake?) debugger and attach with a new tracer to the process.
Sponsored by <The NetBSD Foundation>
2018-04-29 07:28:09 +03:00
|
|
|
*/
|
|
|
|
if (ISSET(p->p_slflag, PSL_TRACED))
|
|
|
|
return EBUSY;
|
|
|
|
|
2017-12-17 07:35:21 +03:00
|
|
|
return 0;
|
2016-11-02 03:11:59 +03:00
|
|
|
|
2017-12-17 18:43:27 +03:00
|
|
|
case PT_ATTACH:
|
2016-11-02 03:11:59 +03:00
|
|
|
/*
|
|
|
|
* You can't attach to a process if:
|
|
|
|
* (1) it's the process that's doing the attaching,
|
|
|
|
*/
|
2018-05-31 02:54:03 +03:00
|
|
|
if (t == p)
|
2017-12-17 07:35:21 +03:00
|
|
|
return EINVAL;
|
2016-11-02 03:11:59 +03:00
|
|
|
|
|
|
|
/*
|
2018-05-01 17:09:53 +03:00
|
|
|
* (2) it's a system process,
|
2016-11-02 03:11:59 +03:00
|
|
|
*/
|
2017-12-17 07:35:21 +03:00
|
|
|
if (t->p_flag & PK_SYSTEM)
|
|
|
|
return EPERM;
|
2016-11-02 03:11:59 +03:00
|
|
|
|
|
|
|
/*
|
2018-05-01 17:09:53 +03:00
|
|
|
* (3) the tracer is initproc,
|
|
|
|
*/
|
|
|
|
if (p == initproc)
|
|
|
|
return EPERM;
|
|
|
|
|
|
|
|
/*
|
2018-05-30 02:34:18 +03:00
|
|
|
* (4) it's already being traced,
|
2016-11-02 03:11:59 +03:00
|
|
|
*/
|
2017-12-17 07:35:21 +03:00
|
|
|
if (ISSET(t->p_slflag, PSL_TRACED))
|
|
|
|
return EBUSY;
|
2016-11-02 03:11:59 +03:00
|
|
|
|
|
|
|
/*
|
2018-05-30 02:34:18 +03:00
|
|
|
* (5) it's a vfork(2)ed parent of the current process, or
|
|
|
|
*/
|
|
|
|
if (ISSET(p->p_lflag, PL_PPWAIT) && p->p_pptr == t)
|
|
|
|
return EPERM;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* (6) the tracer is chrooted, and its root directory is
|
2016-11-02 03:11:59 +03:00
|
|
|
* not at or above the root directory of the tracee
|
|
|
|
*/
|
|
|
|
mutex_exit(t->p_lock); /* XXXSMP */
|
2017-12-17 07:35:21 +03:00
|
|
|
int tmp = proc_isunder(t, l);
|
2016-11-02 03:11:59 +03:00
|
|
|
mutex_enter(t->p_lock); /* XXXSMP */
|
2017-12-17 07:35:21 +03:00
|
|
|
if (!tmp)
|
|
|
|
return EPERM;
|
|
|
|
return 0;
|
2016-11-02 03:11:59 +03:00
|
|
|
|
2017-12-17 18:43:27 +03:00
|
|
|
case PT_READ_I:
|
|
|
|
case PT_READ_D:
|
|
|
|
case PT_WRITE_I:
|
|
|
|
case PT_WRITE_D:
|
|
|
|
case PT_IO:
|
|
|
|
case PT_SET_SIGINFO:
|
|
|
|
case PT_GET_SIGINFO:
|
|
|
|
case_PT_GETREGS
|
|
|
|
case_PT_SETREGS
|
|
|
|
case_PT_GETFPREGS
|
|
|
|
case_PT_SETFPREGS
|
|
|
|
case_PT_GETDBREGS
|
|
|
|
case_PT_SETDBREGS
|
2016-11-02 03:11:59 +03:00
|
|
|
#ifdef __HAVE_PTRACE_MACHDEP
|
|
|
|
PTRACE_MACHDEP_REQUEST_CASES
|
|
|
|
#endif
|
|
|
|
/*
|
|
|
|
* You can't read/write the memory or registers of a process
|
|
|
|
* if the tracer is chrooted, and its root directory is not at
|
|
|
|
* or above the root directory of the tracee.
|
|
|
|
*/
|
|
|
|
mutex_exit(t->p_lock); /* XXXSMP */
|
|
|
|
tmp = proc_isunder(t, l);
|
|
|
|
mutex_enter(t->p_lock); /* XXXSMP */
|
2017-12-17 07:35:21 +03:00
|
|
|
if (!tmp)
|
|
|
|
return EPERM;
|
2016-11-02 03:11:59 +03:00
|
|
|
/*FALLTHROUGH*/
|
|
|
|
|
2017-12-17 18:43:27 +03:00
|
|
|
case PT_CONTINUE:
|
|
|
|
case PT_KILL:
|
|
|
|
case PT_DETACH:
|
|
|
|
case PT_LWPINFO:
|
|
|
|
case PT_SYSCALL:
|
|
|
|
case PT_SYSCALLEMU:
|
|
|
|
case PT_DUMPCORE:
|
2016-11-02 03:11:59 +03:00
|
|
|
#ifdef PT_STEP
|
2017-12-17 18:43:27 +03:00
|
|
|
case PT_STEP:
|
|
|
|
case PT_SETSTEP:
|
|
|
|
case PT_CLEARSTEP:
|
2016-11-02 03:11:59 +03:00
|
|
|
#endif
|
2017-12-17 18:43:27 +03:00
|
|
|
case PT_SET_EVENT_MASK:
|
|
|
|
case PT_GET_EVENT_MASK:
|
|
|
|
case PT_GET_PROCESS_STATE:
|
|
|
|
case PT_RESUME:
|
|
|
|
case PT_SUSPEND:
|
2019-10-09 16:19:43 +03:00
|
|
|
case PT_STOP:
|
2019-12-24 17:50:59 +03:00
|
|
|
case PT_LWPSTATUS:
|
|
|
|
case PT_LWPNEXT:
|
2020-05-14 16:32:15 +03:00
|
|
|
case PT_SET_SIGPASS:
|
|
|
|
case PT_GET_SIGPASS:
|
2016-11-02 03:11:59 +03:00
|
|
|
/*
|
|
|
|
* You can't do what you want to the process if:
|
|
|
|
* (1) It's not being traced at all,
|
|
|
|
*/
|
2017-12-17 07:35:21 +03:00
|
|
|
if (!ISSET(t->p_slflag, PSL_TRACED))
|
|
|
|
return EPERM;
|
2016-11-02 03:11:59 +03:00
|
|
|
|
|
|
|
/*
|
2017-08-28 03:46:06 +03:00
|
|
|
* (2) it's not being traced by _you_, or
|
2016-11-02 03:11:59 +03:00
|
|
|
*/
|
|
|
|
if (t->p_pptr != p) {
|
|
|
|
DPRINTF(("parent %d != %d\n", t->p_pptr->p_pid,
|
|
|
|
p->p_pid));
|
2017-12-17 07:35:21 +03:00
|
|
|
return EBUSY;
|
2016-11-02 03:11:59 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2017-08-28 03:46:06 +03:00
|
|
|
* (3) it's not currently stopped.
|
2019-10-09 16:19:43 +03:00
|
|
|
*
|
|
|
|
* As an exception allow PT_KILL and PT_STOP here.
|
2016-11-02 03:11:59 +03:00
|
|
|
*/
|
2019-10-09 16:19:43 +03:00
|
|
|
if (req != PT_KILL && req != PT_STOP &&
|
|
|
|
(t->p_stat != SSTOP || !t->p_waited /* XXXSMP */)) {
|
2016-11-02 03:11:59 +03:00
|
|
|
DPRINTF(("stat %d flag %d\n", t->p_stat,
|
|
|
|
!t->p_waited));
|
2017-12-17 07:35:21 +03:00
|
|
|
return EBUSY;
|
2016-11-02 03:11:59 +03:00
|
|
|
}
|
2017-12-17 07:35:21 +03:00
|
|
|
return 0;
|
2016-11-02 03:11:59 +03:00
|
|
|
|
|
|
|
default: /* It was not a legal request. */
|
2017-12-17 07:35:21 +03:00
|
|
|
return EINVAL;
|
2016-11-02 03:11:59 +03:00
|
|
|
}
|
2017-12-17 07:35:21 +03:00
|
|
|
}
|
2016-11-02 03:11:59 +03:00
|
|
|
|
2017-12-17 07:35:21 +03:00
|
|
|
static int
|
|
|
|
ptrace_needs_hold(int req)
|
|
|
|
{
|
2016-11-02 03:11:59 +03:00
|
|
|
switch (req) {
|
|
|
|
#ifdef PT_STEP
|
|
|
|
case PT_STEP:
|
|
|
|
#endif
|
|
|
|
case PT_CONTINUE:
|
|
|
|
case PT_DETACH:
|
|
|
|
case PT_KILL:
|
|
|
|
case PT_SYSCALL:
|
|
|
|
case PT_SYSCALLEMU:
|
|
|
|
case PT_ATTACH:
|
|
|
|
case PT_TRACE_ME:
|
2017-12-17 07:35:21 +03:00
|
|
|
case PT_GET_SIGINFO:
|
|
|
|
case PT_SET_SIGINFO:
|
2019-10-09 16:19:43 +03:00
|
|
|
case PT_STOP:
|
2017-12-17 07:35:21 +03:00
|
|
|
return 1;
|
|
|
|
default:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2017-12-17 23:59:27 +03:00
|
|
|
ptrace_get_siginfo(struct proc *t, struct ptrace_methods *ptm, void *addr,
|
|
|
|
size_t data)
|
2017-12-17 07:35:21 +03:00
|
|
|
{
|
|
|
|
struct ptrace_siginfo psi;
|
|
|
|
|
2018-11-29 14:45:52 +03:00
|
|
|
memset(&psi, 0, sizeof(psi));
|
2017-12-17 07:35:21 +03:00
|
|
|
psi.psi_siginfo._info = t->p_sigctx.ps_info;
|
|
|
|
psi.psi_lwpid = t->p_sigctx.ps_lwp;
|
2018-06-23 06:32:48 +03:00
|
|
|
DPRINTF(("%s: lwp=%d signal=%d\n", __func__, psi.psi_lwpid,
|
|
|
|
psi.psi_siginfo.si_signo));
|
2017-12-17 07:35:21 +03:00
|
|
|
|
2017-12-17 23:59:27 +03:00
|
|
|
return ptm->ptm_copyout_siginfo(&psi, addr, data);
|
2017-12-17 07:35:21 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2017-12-17 23:59:27 +03:00
|
|
|
ptrace_set_siginfo(struct proc *t, struct lwp **lt, struct ptrace_methods *ptm,
|
|
|
|
void *addr, size_t data)
|
2017-12-17 07:35:21 +03:00
|
|
|
{
|
|
|
|
struct ptrace_siginfo psi;
|
|
|
|
|
2017-12-17 23:59:27 +03:00
|
|
|
int error = ptm->ptm_copyin_siginfo(&psi, addr, data);
|
2017-12-17 07:35:21 +03:00
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
|
|
|
|
/* Check that the data is a valid signal number or zero. */
|
|
|
|
if (psi.psi_siginfo.si_signo < 0 || psi.psi_siginfo.si_signo >= NSIG)
|
|
|
|
return EINVAL;
|
|
|
|
|
|
|
|
t->p_sigctx.ps_faked = true;
|
|
|
|
t->p_sigctx.ps_info = psi.psi_siginfo._info;
|
|
|
|
t->p_sigctx.ps_lwp = psi.psi_lwpid;
|
2018-06-23 06:32:48 +03:00
|
|
|
DPRINTF(("%s: lwp=%d signal=%d\n", __func__, psi.psi_lwpid,
|
|
|
|
psi.psi_siginfo.si_signo));
|
2017-12-17 07:35:21 +03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-05-14 16:32:15 +03:00
|
|
|
static int
|
|
|
|
ptrace_get_sigpass(struct proc *t, void *addr, size_t data)
|
|
|
|
{
|
|
|
|
sigset_t set;
|
|
|
|
|
|
|
|
if (data > sizeof(set) || data <= 0) {
|
|
|
|
DPRINTF(("%s: invalid data: %zu < %zu <= 0\n",
|
|
|
|
__func__, sizeof(set), data));
|
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
set = t->p_sigctx.ps_sigpass;
|
|
|
|
|
|
|
|
return copyout(&set, addr, data);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
ptrace_set_sigpass(struct proc *t, void *addr, size_t data)
|
|
|
|
{
|
|
|
|
sigset_t set;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
if (data > sizeof(set) || data <= 0) {
|
|
|
|
DPRINTF(("%s: invalid data: %zu < %zu <= 0\n",
|
|
|
|
__func__, sizeof(set), data));
|
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(&set, 0, sizeof(set));
|
|
|
|
|
|
|
|
if ((error = copyin(addr, &set, data)))
|
|
|
|
return error;
|
|
|
|
|
|
|
|
/* We catch SIGSTOP and cannot intercept SIGKILL. */
|
|
|
|
sigminusset(&sigcantmask, &set);
|
|
|
|
|
|
|
|
t->p_sigctx.ps_sigpass = set;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-12-17 07:35:21 +03:00
|
|
|
static int
|
|
|
|
ptrace_get_event_mask(struct proc *t, void *addr, size_t data)
|
|
|
|
{
|
|
|
|
struct ptrace_event pe;
|
|
|
|
|
|
|
|
if (data != sizeof(pe)) {
|
|
|
|
DPRINTF(("%s: %zu != %zu\n", __func__, data, sizeof(pe)));
|
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
memset(&pe, 0, sizeof(pe));
|
|
|
|
pe.pe_set_event = ISSET(t->p_slflag, PSL_TRACEFORK) ?
|
|
|
|
PTRACE_FORK : 0;
|
|
|
|
pe.pe_set_event |= ISSET(t->p_slflag, PSL_TRACEVFORK) ?
|
|
|
|
PTRACE_VFORK : 0;
|
|
|
|
pe.pe_set_event |= ISSET(t->p_slflag, PSL_TRACEVFORK_DONE) ?
|
|
|
|
PTRACE_VFORK_DONE : 0;
|
|
|
|
pe.pe_set_event |= ISSET(t->p_slflag, PSL_TRACELWP_CREATE) ?
|
|
|
|
PTRACE_LWP_CREATE : 0;
|
|
|
|
pe.pe_set_event |= ISSET(t->p_slflag, PSL_TRACELWP_EXIT) ?
|
|
|
|
PTRACE_LWP_EXIT : 0;
|
2019-06-12 02:18:55 +03:00
|
|
|
pe.pe_set_event |= ISSET(t->p_slflag, PSL_TRACEPOSIX_SPAWN) ?
|
|
|
|
PTRACE_POSIX_SPAWN : 0;
|
2018-06-23 06:32:48 +03:00
|
|
|
DPRINTF(("%s: lwp=%d event=%#x\n", __func__,
|
|
|
|
t->p_sigctx.ps_lwp, pe.pe_set_event));
|
2017-12-17 07:35:21 +03:00
|
|
|
return copyout(&pe, addr, sizeof(pe));
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
ptrace_set_event_mask(struct proc *t, void *addr, size_t data)
|
|
|
|
{
|
|
|
|
struct ptrace_event pe;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
if (data != sizeof(pe)) {
|
|
|
|
DPRINTF(("%s: %zu != %zu\n", __func__, data, sizeof(pe)));
|
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
if ((error = copyin(addr, &pe, sizeof(pe))) != 0)
|
|
|
|
return error;
|
|
|
|
|
2018-06-23 06:32:48 +03:00
|
|
|
DPRINTF(("%s: lwp=%d event=%#x\n", __func__,
|
|
|
|
t->p_sigctx.ps_lwp, pe.pe_set_event));
|
2017-12-17 07:35:21 +03:00
|
|
|
if (pe.pe_set_event & PTRACE_FORK)
|
|
|
|
SET(t->p_slflag, PSL_TRACEFORK);
|
|
|
|
else
|
|
|
|
CLR(t->p_slflag, PSL_TRACEFORK);
|
2018-05-01 19:37:23 +03:00
|
|
|
|
2017-12-17 07:35:21 +03:00
|
|
|
if (pe.pe_set_event & PTRACE_VFORK)
|
|
|
|
SET(t->p_slflag, PSL_TRACEVFORK);
|
|
|
|
else
|
|
|
|
CLR(t->p_slflag, PSL_TRACEVFORK);
|
2018-05-01 19:37:23 +03:00
|
|
|
|
2017-12-17 07:35:21 +03:00
|
|
|
if (pe.pe_set_event & PTRACE_VFORK_DONE)
|
|
|
|
SET(t->p_slflag, PSL_TRACEVFORK_DONE);
|
|
|
|
else
|
|
|
|
CLR(t->p_slflag, PSL_TRACEVFORK_DONE);
|
2018-05-01 19:37:23 +03:00
|
|
|
|
2017-12-17 07:35:21 +03:00
|
|
|
if (pe.pe_set_event & PTRACE_LWP_CREATE)
|
|
|
|
SET(t->p_slflag, PSL_TRACELWP_CREATE);
|
|
|
|
else
|
|
|
|
CLR(t->p_slflag, PSL_TRACELWP_CREATE);
|
2018-05-01 19:37:23 +03:00
|
|
|
|
2017-12-17 07:35:21 +03:00
|
|
|
if (pe.pe_set_event & PTRACE_LWP_EXIT)
|
|
|
|
SET(t->p_slflag, PSL_TRACELWP_EXIT);
|
|
|
|
else
|
|
|
|
CLR(t->p_slflag, PSL_TRACELWP_EXIT);
|
2019-06-12 02:18:55 +03:00
|
|
|
|
|
|
|
if (pe.pe_set_event & PTRACE_POSIX_SPAWN)
|
|
|
|
SET(t->p_slflag, PSL_TRACEPOSIX_SPAWN);
|
|
|
|
else
|
|
|
|
CLR(t->p_slflag, PSL_TRACEPOSIX_SPAWN);
|
|
|
|
|
2017-12-17 07:35:21 +03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
ptrace_get_process_state(struct proc *t, void *addr, size_t data)
|
|
|
|
{
|
2019-10-01 21:44:22 +03:00
|
|
|
struct _ksiginfo *si;
|
2017-12-17 07:35:21 +03:00
|
|
|
struct ptrace_state ps;
|
|
|
|
|
|
|
|
if (data != sizeof(ps)) {
|
|
|
|
DPRINTF(("%s: %zu != %zu\n", __func__, data, sizeof(ps)));
|
|
|
|
return EINVAL;
|
|
|
|
}
|
2019-10-01 00:13:33 +03:00
|
|
|
|
|
|
|
if (t->p_sigctx.ps_info._signo != SIGTRAP ||
|
|
|
|
(t->p_sigctx.ps_info._code != TRAP_CHLD &&
|
|
|
|
t->p_sigctx.ps_info._code != TRAP_LWP)) {
|
2019-10-01 21:44:22 +03:00
|
|
|
memset(&ps, 0, sizeof(ps));
|
|
|
|
} else {
|
|
|
|
si = &t->p_sigctx.ps_info;
|
2019-10-04 02:11:11 +03:00
|
|
|
|
|
|
|
KASSERT(si->_reason._ptrace_state._pe_report_event > 0);
|
|
|
|
KASSERT(si->_reason._ptrace_state._option._pe_other_pid > 0);
|
|
|
|
|
2019-10-01 21:44:22 +03:00
|
|
|
ps.pe_report_event = si->_reason._ptrace_state._pe_report_event;
|
|
|
|
|
|
|
|
CTASSERT(sizeof(ps.pe_other_pid) == sizeof(ps.pe_lwp));
|
|
|
|
ps.pe_other_pid =
|
|
|
|
si->_reason._ptrace_state._option._pe_other_pid;
|
2017-12-17 07:35:21 +03:00
|
|
|
}
|
2019-10-01 00:13:33 +03:00
|
|
|
|
2018-06-23 06:32:48 +03:00
|
|
|
DPRINTF(("%s: lwp=%d event=%#x pid=%d lwp=%d\n", __func__,
|
|
|
|
t->p_sigctx.ps_lwp, ps.pe_report_event,
|
|
|
|
ps.pe_other_pid, ps.pe_lwp));
|
2017-12-17 07:35:21 +03:00
|
|
|
return copyout(&ps, addr, sizeof(ps));
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
ptrace_lwpinfo(struct proc *t, struct lwp **lt, void *addr, size_t data)
|
|
|
|
{
|
|
|
|
struct ptrace_lwpinfo pl;
|
|
|
|
|
|
|
|
if (data != sizeof(pl)) {
|
|
|
|
DPRINTF(("%s: %zu != %zu\n", __func__, data, sizeof(pl)));
|
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
int error = copyin(addr, &pl, sizeof(pl));
|
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
|
|
|
|
lwpid_t tmp = pl.pl_lwpid;
|
|
|
|
lwp_delref(*lt);
|
|
|
|
mutex_enter(t->p_lock);
|
|
|
|
if (tmp == 0)
|
|
|
|
*lt = lwp_find_first(t);
|
|
|
|
else {
|
|
|
|
*lt = lwp_find(t, tmp);
|
|
|
|
if (*lt == NULL) {
|
|
|
|
mutex_exit(t->p_lock);
|
|
|
|
return ESRCH;
|
|
|
|
}
|
|
|
|
*lt = LIST_NEXT(*lt, l_sibling);
|
|
|
|
}
|
|
|
|
|
2020-01-03 03:37:29 +03:00
|
|
|
while (*lt != NULL && (!lwp_alive(*lt) ||
|
|
|
|
((*lt)->l_flag & LW_SYSTEM) != 0))
|
2017-12-17 07:35:21 +03:00
|
|
|
*lt = LIST_NEXT(*lt, l_sibling);
|
|
|
|
|
|
|
|
pl.pl_lwpid = 0;
|
|
|
|
pl.pl_event = 0;
|
|
|
|
if (*lt) {
|
|
|
|
lwp_addref(*lt);
|
|
|
|
pl.pl_lwpid = (*lt)->l_lid;
|
|
|
|
|
|
|
|
if ((*lt)->l_flag & LW_WSUSPEND)
|
|
|
|
pl.pl_event = PL_EVENT_SUSPENDED;
|
|
|
|
/*
|
|
|
|
* If we match the lwp, or it was sent to every lwp,
|
|
|
|
* we set PL_EVENT_SIGNAL.
|
|
|
|
* XXX: ps_lwp == 0 means everyone and noone, so
|
|
|
|
* check ps_signo too.
|
|
|
|
*/
|
|
|
|
else if ((*lt)->l_lid == t->p_sigctx.ps_lwp
|
|
|
|
|| (t->p_sigctx.ps_lwp == 0 &&
|
2018-06-23 06:32:48 +03:00
|
|
|
t->p_sigctx.ps_info._signo)) {
|
|
|
|
DPRINTF(("%s: lwp=%d siglwp=%d signo %d\n", __func__,
|
|
|
|
pl.pl_lwpid, t->p_sigctx.ps_lwp,
|
|
|
|
t->p_sigctx.ps_info._signo));
|
2017-12-17 07:35:21 +03:00
|
|
|
pl.pl_event = PL_EVENT_SIGNAL;
|
2018-06-23 06:32:48 +03:00
|
|
|
}
|
2017-12-17 07:35:21 +03:00
|
|
|
}
|
|
|
|
mutex_exit(t->p_lock);
|
2018-06-23 06:32:48 +03:00
|
|
|
DPRINTF(("%s: lwp=%d event=%#x\n", __func__,
|
|
|
|
pl.pl_lwpid, pl.pl_event));
|
2017-12-17 07:35:21 +03:00
|
|
|
|
|
|
|
return copyout(&pl, addr, sizeof(pl));
|
|
|
|
}
|
|
|
|
|
2019-12-24 17:50:59 +03:00
|
|
|
static int
|
|
|
|
ptrace_lwpstatus(struct proc *t, struct ptrace_methods *ptm, struct lwp **lt,
|
|
|
|
void *addr, size_t data, bool next)
|
|
|
|
{
|
|
|
|
struct ptrace_lwpstatus pls;
|
|
|
|
struct lwp *l;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
if (data > sizeof(pls) || data < sizeof(lwpid_t)) {
|
|
|
|
DPRINTF(("%s: invalid data: %zu < %zu < %zu\n",
|
|
|
|
__func__, sizeof(lwpid_t), data, sizeof(pls)));
|
|
|
|
return EINVAL;
|
|
|
|
}
|
2020-02-22 12:24:05 +03:00
|
|
|
error = copyin(addr, &pls.pl_lwpid, sizeof(lwpid_t));
|
2019-12-24 17:50:59 +03:00
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
|
|
|
|
if (next) {
|
|
|
|
lwp_delref(*lt);
|
|
|
|
lwpid_t tmp = pls.pl_lwpid;
|
|
|
|
mutex_enter(t->p_lock);
|
|
|
|
if (tmp == 0)
|
|
|
|
*lt = lwp_find_first(t);
|
|
|
|
else {
|
|
|
|
*lt = lwp_find(t, tmp);
|
|
|
|
if (*lt == NULL) {
|
|
|
|
mutex_exit(t->p_lock);
|
|
|
|
return ESRCH;
|
|
|
|
}
|
|
|
|
*lt = LIST_NEXT(*lt, l_sibling);
|
|
|
|
}
|
|
|
|
|
2020-01-03 03:37:29 +03:00
|
|
|
while (*lt != NULL && (!lwp_alive(*lt) ||
|
|
|
|
((*lt)->l_flag & LW_SYSTEM) != 0))
|
2019-12-24 17:50:59 +03:00
|
|
|
*lt = LIST_NEXT(*lt, l_sibling);
|
|
|
|
|
|
|
|
if (*lt == NULL) {
|
|
|
|
memset(&pls, 0, sizeof(pls));
|
|
|
|
mutex_exit(t->p_lock);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
lwp_addref(*lt);
|
|
|
|
mutex_exit(t->p_lock);
|
|
|
|
|
|
|
|
pls.pl_lwpid = (*lt)->l_lid;
|
|
|
|
} else {
|
|
|
|
if ((error = ptrace_update_lwp(t, lt, pls.pl_lwpid)) != 0)
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
l = *lt;
|
|
|
|
|
|
|
|
ptrace_read_lwpstatus(l, &pls);
|
|
|
|
|
|
|
|
out:
|
|
|
|
DPRINTF(("%s: lwp=%d sigpend=%02x%02x%02x%02x sigmask=%02x%02x%02x%02x "
|
|
|
|
"name='%s' private=%p\n", __func__, pls.pl_lwpid,
|
|
|
|
pls.pl_sigpend.__bits[0], pls.pl_sigpend.__bits[1],
|
|
|
|
pls.pl_sigpend.__bits[2], pls.pl_sigpend.__bits[3],
|
|
|
|
pls.pl_sigmask.__bits[0], pls.pl_sigmask.__bits[1],
|
|
|
|
pls.pl_sigmask.__bits[2], pls.pl_sigmask.__bits[3],
|
|
|
|
pls.pl_name, pls.pl_private));
|
|
|
|
|
|
|
|
return ptm->ptm_copyout_lwpstatus(&pls, addr, data);
|
|
|
|
}
|
|
|
|
|
2017-12-17 07:35:21 +03:00
|
|
|
static int
|
|
|
|
ptrace_startstop(struct proc *t, struct lwp **lt, int rq, void *addr,
|
|
|
|
size_t data)
|
|
|
|
{
|
|
|
|
int error;
|
|
|
|
|
|
|
|
if ((error = ptrace_update_lwp(t, lt, data)) != 0)
|
|
|
|
return error;
|
|
|
|
|
2018-06-23 06:32:48 +03:00
|
|
|
DPRINTF(("%s: lwp=%d request=%d\n", __func__, (*lt)->l_lid, rq));
|
2017-12-17 07:35:21 +03:00
|
|
|
lwp_lock(*lt);
|
|
|
|
if (rq == PT_SUSPEND)
|
2019-10-04 01:48:44 +03:00
|
|
|
(*lt)->l_flag |= LW_DBGSUSPEND;
|
|
|
|
else {
|
|
|
|
(*lt)->l_flag &= ~LW_DBGSUSPEND;
|
|
|
|
if ((*lt)->l_flag != LSSUSPENDED)
|
|
|
|
(*lt)->l_stat = LSSTOP;
|
|
|
|
}
|
2017-12-17 07:35:21 +03:00
|
|
|
lwp_unlock(*lt);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-12-17 18:43:27 +03:00
|
|
|
#ifdef PT_REGISTERS
|
2017-12-17 07:35:21 +03:00
|
|
|
static int
|
|
|
|
ptrace_uio_dir(int req)
|
|
|
|
{
|
|
|
|
switch (req) {
|
2017-12-17 18:43:27 +03:00
|
|
|
case_PT_GETREGS
|
|
|
|
case_PT_GETFPREGS
|
|
|
|
case_PT_GETDBREGS
|
2017-12-17 07:35:21 +03:00
|
|
|
return UIO_READ;
|
2017-12-17 18:43:27 +03:00
|
|
|
case_PT_SETREGS
|
|
|
|
case_PT_SETFPREGS
|
|
|
|
case_PT_SETDBREGS
|
2017-12-17 07:35:21 +03:00
|
|
|
return UIO_WRITE;
|
|
|
|
default:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
ptrace_regs(struct lwp *l, struct lwp **lt, int rq, struct ptrace_methods *ptm,
|
|
|
|
void *addr, size_t data)
|
|
|
|
{
|
|
|
|
int error;
|
2019-11-22 08:01:44 +03:00
|
|
|
struct proc *p, *t;
|
2017-12-17 07:35:21 +03:00
|
|
|
struct vmspace *vm;
|
|
|
|
|
2019-11-22 08:01:44 +03:00
|
|
|
p = l->l_proc; /* tracer */
|
|
|
|
t = (*lt)->l_proc; /* traced */
|
|
|
|
|
2017-12-17 07:35:21 +03:00
|
|
|
if ((error = ptrace_update_lwp(t, lt, data)) != 0)
|
|
|
|
return error;
|
|
|
|
|
|
|
|
int dir = ptrace_uio_dir(rq);
|
|
|
|
size_t size;
|
|
|
|
int (*func)(struct lwp *, struct lwp *, struct uio *);
|
|
|
|
|
2018-06-23 06:32:48 +03:00
|
|
|
DPRINTF(("%s: lwp=%d request=%d\n", __func__, l->l_lid, rq));
|
|
|
|
|
2017-12-17 07:35:21 +03:00
|
|
|
switch (rq) {
|
|
|
|
#if defined(PT_SETREGS) || defined(PT_GETREGS)
|
2017-12-17 18:43:27 +03:00
|
|
|
case_PT_GETREGS
|
|
|
|
case_PT_SETREGS
|
2017-12-17 07:35:21 +03:00
|
|
|
if (!process_validregs(*lt))
|
|
|
|
return EINVAL;
|
2019-11-22 08:01:44 +03:00
|
|
|
size = PROC_REGSZ(p);
|
2017-12-17 07:35:21 +03:00
|
|
|
func = ptm->ptm_doregs;
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
#if defined(PT_SETFPREGS) || defined(PT_GETFPREGS)
|
2017-12-17 18:43:27 +03:00
|
|
|
case_PT_GETFPREGS
|
|
|
|
case_PT_SETFPREGS
|
2017-12-17 07:35:21 +03:00
|
|
|
if (!process_validfpregs(*lt))
|
|
|
|
return EINVAL;
|
2019-11-22 08:01:44 +03:00
|
|
|
size = PROC_FPREGSZ(p);
|
2017-12-17 07:35:21 +03:00
|
|
|
func = ptm->ptm_dofpregs;
|
2016-11-02 03:11:59 +03:00
|
|
|
break;
|
2017-12-17 07:35:21 +03:00
|
|
|
#endif
|
|
|
|
#if defined(PT_SETDBREGS) || defined(PT_GETDBREGS)
|
2017-12-17 18:43:27 +03:00
|
|
|
case_PT_GETDBREGS
|
|
|
|
case_PT_SETDBREGS
|
2017-12-17 07:35:21 +03:00
|
|
|
if (!process_validdbregs(*lt))
|
|
|
|
return EINVAL;
|
2019-11-22 08:01:44 +03:00
|
|
|
size = PROC_DBREGSZ(p);
|
2017-12-17 07:35:21 +03:00
|
|
|
func = ptm->ptm_dodbregs;
|
|
|
|
break;
|
|
|
|
#endif
|
2016-11-02 03:11:59 +03:00
|
|
|
default:
|
2017-12-17 07:35:21 +03:00
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
|
2018-01-08 09:10:30 +03:00
|
|
|
error = proc_vmspace_getref(l->l_proc, &vm);
|
2017-12-17 07:35:21 +03:00
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
|
|
|
|
struct uio uio;
|
|
|
|
struct iovec iov;
|
|
|
|
|
|
|
|
iov.iov_base = addr;
|
|
|
|
iov.iov_len = size;
|
|
|
|
uio.uio_iov = &iov;
|
|
|
|
uio.uio_iovcnt = 1;
|
|
|
|
uio.uio_offset = 0;
|
|
|
|
uio.uio_resid = iov.iov_len;
|
|
|
|
uio.uio_rw = dir;
|
|
|
|
uio.uio_vmspace = vm;
|
|
|
|
|
|
|
|
error = (*func)(l, *lt, &uio);
|
|
|
|
uvmspace_free(vm);
|
|
|
|
return error;
|
|
|
|
}
|
2017-12-17 18:43:27 +03:00
|
|
|
#endif
|
|
|
|
|
|
|
|
static int
|
2019-10-09 16:19:43 +03:00
|
|
|
ptrace_sendsig(struct lwp *l, int req, struct proc *t, struct lwp *lt, int signo, int resume_all)
|
2017-12-17 18:43:27 +03:00
|
|
|
{
|
|
|
|
ksiginfo_t ksi;
|
|
|
|
|
|
|
|
/* Finally, deliver the requested signal (or none). */
|
|
|
|
if (t->p_stat == SSTOP) {
|
|
|
|
/*
|
|
|
|
* Unstop the process. If it needs to take a
|
|
|
|
* signal, make all efforts to ensure that at
|
|
|
|
* an LWP runs to see it.
|
|
|
|
*/
|
|
|
|
t->p_xsig = signo;
|
2018-05-20 06:51:31 +03:00
|
|
|
|
|
|
|
/*
|
2018-05-20 07:00:35 +03:00
|
|
|
* signo > 0 check prevents a potential panic, as
|
2018-05-20 06:51:31 +03:00
|
|
|
* sigismember(&...,0) is invalid check and signo
|
|
|
|
* can be equal to 0 as a special case of no-signal.
|
|
|
|
*/
|
|
|
|
if (signo > 0 && sigismember(&stopsigmask, signo)) {
|
|
|
|
t->p_waited = 0;
|
|
|
|
child_psignal(t, 0);
|
|
|
|
} else if (resume_all)
|
2017-12-17 18:43:27 +03:00
|
|
|
proc_unstop(t);
|
|
|
|
else
|
|
|
|
lwp_unstop(lt);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-10-09 16:19:43 +03:00
|
|
|
KASSERT(req == PT_KILL || req == PT_STOP || req == PT_ATTACH);
|
|
|
|
|
|
|
|
KSI_INIT(&ksi);
|
|
|
|
ksi.ksi_signo = signo;
|
|
|
|
ksi.ksi_code = SI_USER;
|
|
|
|
ksi.ksi_pid = l->l_proc->p_pid;
|
|
|
|
ksi.ksi_uid = kauth_cred_geteuid(l->l_cred);
|
|
|
|
|
|
|
|
t->p_sigctx.ps_faked = false;
|
|
|
|
|
2018-06-23 06:32:48 +03:00
|
|
|
DPRINTF(("%s: pid=%d.%d signal=%d resume_all=%d\n", __func__, t->p_pid,
|
2019-10-09 16:19:43 +03:00
|
|
|
lt->l_lid, signo, resume_all));
|
2017-12-17 18:43:27 +03:00
|
|
|
|
2019-10-09 16:19:43 +03:00
|
|
|
return kpsignal2(t, &ksi);
|
2017-12-17 18:43:27 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
ptrace_dumpcore(struct lwp *lt, char *path, size_t len)
|
|
|
|
{
|
|
|
|
int error;
|
|
|
|
if (path != NULL) {
|
|
|
|
|
|
|
|
if (len >= MAXPATHLEN)
|
|
|
|
return EINVAL;
|
|
|
|
|
|
|
|
char *src = path;
|
|
|
|
path = kmem_alloc(len + 1, KM_SLEEP);
|
|
|
|
error = copyin(src, path, len);
|
|
|
|
if (error)
|
|
|
|
goto out;
|
|
|
|
path[len] = '\0';
|
|
|
|
}
|
2018-06-23 06:32:48 +03:00
|
|
|
DPRINTF(("%s: lwp=%d\n", __func__, lt->l_lid));
|
2019-11-13 18:48:36 +03:00
|
|
|
MODULE_HOOK_CALL(coredump_hook, (lt, path), 0, error);
|
2017-12-17 18:43:27 +03:00
|
|
|
out:
|
|
|
|
if (path)
|
|
|
|
kmem_free(path, len + 1);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
ptrace_doio(struct lwp *l, struct proc *t, struct lwp *lt,
|
2017-12-24 01:12:19 +03:00
|
|
|
struct ptrace_io_desc *piod, void *addr, bool sysspace)
|
2017-12-17 18:43:27 +03:00
|
|
|
{
|
|
|
|
struct uio uio;
|
|
|
|
struct iovec iov;
|
|
|
|
int error, tmp;
|
|
|
|
|
|
|
|
error = 0;
|
|
|
|
iov.iov_base = piod->piod_addr;
|
|
|
|
iov.iov_len = piod->piod_len;
|
|
|
|
uio.uio_iov = &iov;
|
|
|
|
uio.uio_iovcnt = 1;
|
|
|
|
uio.uio_offset = (off_t)(unsigned long)piod->piod_offs;
|
|
|
|
uio.uio_resid = piod->piod_len;
|
|
|
|
|
2018-06-23 06:32:48 +03:00
|
|
|
DPRINTF(("%s: lwp=%d request=%d\n", __func__, l->l_lid, piod->piod_op));
|
|
|
|
|
2017-12-17 18:43:27 +03:00
|
|
|
switch (piod->piod_op) {
|
|
|
|
case PIOD_READ_D:
|
|
|
|
case PIOD_READ_I:
|
|
|
|
uio.uio_rw = UIO_READ;
|
|
|
|
break;
|
|
|
|
case PIOD_WRITE_D:
|
|
|
|
case PIOD_WRITE_I:
|
|
|
|
/*
|
|
|
|
* Can't write to a RAS
|
|
|
|
*/
|
|
|
|
if (ras_lookup(t, addr) != (void *)-1) {
|
|
|
|
return EACCES;
|
|
|
|
}
|
|
|
|
uio.uio_rw = UIO_WRITE;
|
|
|
|
break;
|
|
|
|
case PIOD_READ_AUXV:
|
|
|
|
uio.uio_rw = UIO_READ;
|
|
|
|
tmp = t->p_execsw->es_arglen;
|
|
|
|
if (uio.uio_offset > tmp)
|
|
|
|
return EIO;
|
|
|
|
if (uio.uio_resid > tmp - uio.uio_offset)
|
|
|
|
uio.uio_resid = tmp - uio.uio_offset;
|
|
|
|
piod->piod_len = iov.iov_len = uio.uio_resid;
|
|
|
|
error = process_auxv_offset(t, &uio);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
error = EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
2017-12-24 01:12:19 +03:00
|
|
|
|
2017-12-17 18:43:27 +03:00
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
|
2017-12-24 01:12:19 +03:00
|
|
|
if (sysspace) {
|
|
|
|
uio.uio_vmspace = vmspace_kernel();
|
|
|
|
} else {
|
|
|
|
error = proc_vmspace_getref(l->l_proc, &uio.uio_vmspace);
|
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
}
|
2017-12-17 18:43:27 +03:00
|
|
|
|
|
|
|
error = process_domem(l, lt, &uio);
|
2017-12-24 01:12:19 +03:00
|
|
|
if (!sysspace)
|
|
|
|
uvmspace_free(uio.uio_vmspace);
|
|
|
|
if (error)
|
2017-12-17 18:43:27 +03:00
|
|
|
return error;
|
|
|
|
piod->piod_len -= uio.uio_resid;
|
|
|
|
return 0;
|
|
|
|
}
|
2017-12-17 07:35:21 +03:00
|
|
|
|
|
|
|
int
|
|
|
|
do_ptrace(struct ptrace_methods *ptm, struct lwp *l, int req, pid_t pid,
|
|
|
|
void *addr, int data, register_t *retval)
|
|
|
|
{
|
|
|
|
struct proc *p = l->l_proc;
|
|
|
|
struct lwp *lt = NULL;
|
|
|
|
struct lwp *lt2;
|
|
|
|
struct proc *t; /* target process */
|
|
|
|
struct ptrace_io_desc piod;
|
|
|
|
int error, write, tmp, pheld;
|
|
|
|
int signo = 0;
|
|
|
|
int resume_all;
|
2019-06-29 14:37:17 +03:00
|
|
|
bool locked;
|
2017-12-17 07:35:21 +03:00
|
|
|
error = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If attaching or detaching, we need to get a write hold on the
|
|
|
|
* proclist lock so that we can re-parent the target process.
|
|
|
|
*/
|
2020-05-24 02:42:41 +03:00
|
|
|
mutex_enter(&proc_lock);
|
2017-12-17 07:35:21 +03:00
|
|
|
|
|
|
|
t = ptrace_find(l, req, pid);
|
|
|
|
if (t == NULL) {
|
2020-05-24 02:42:41 +03:00
|
|
|
mutex_exit(&proc_lock);
|
2017-12-17 07:35:21 +03:00
|
|
|
return ESRCH;
|
|
|
|
}
|
|
|
|
|
|
|
|
pheld = 1;
|
2019-06-29 14:37:17 +03:00
|
|
|
if ((error = ptrace_allowed(l, req, t, p, &locked)) != 0)
|
2017-12-17 07:35:21 +03:00
|
|
|
goto out;
|
|
|
|
|
|
|
|
if ((error = kauth_authorize_process(l->l_cred,
|
|
|
|
KAUTH_PROCESS_PTRACE, t, KAUTH_ARG(req), NULL, NULL)) != 0)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
if ((lt = lwp_find_first(t)) == NULL) {
|
|
|
|
error = ESRCH;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Do single-step fixup if needed. */
|
|
|
|
FIX_SSTEP(t);
|
|
|
|
KASSERT(lt != NULL);
|
|
|
|
lwp_addref(lt);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Which locks do we need held? XXX Ugly.
|
|
|
|
*/
|
|
|
|
if ((pheld = ptrace_needs_hold(req)) == 0) {
|
2016-11-02 03:11:59 +03:00
|
|
|
mutex_exit(t->p_lock);
|
2020-05-24 02:42:41 +03:00
|
|
|
mutex_exit(&proc_lock);
|
2016-11-02 03:11:59 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Now do the operation. */
|
|
|
|
write = 0;
|
|
|
|
*retval = 0;
|
|
|
|
tmp = 0;
|
|
|
|
resume_all = 1;
|
|
|
|
|
|
|
|
switch (req) {
|
2017-12-17 18:43:27 +03:00
|
|
|
case PT_TRACE_ME:
|
2016-11-02 03:11:59 +03:00
|
|
|
/* Just set the trace flag. */
|
|
|
|
SET(t->p_slflag, PSL_TRACED);
|
|
|
|
t->p_opptr = t->p_pptr;
|
|
|
|
break;
|
|
|
|
|
2018-04-27 19:50:56 +03:00
|
|
|
/*
|
|
|
|
* The I and D separate address space has been inherited from PDP-11.
|
|
|
|
* The 16-bit UNIX started with a single address space per program,
|
|
|
|
* but was extended to two 16-bit (2 x 64kb) address spaces.
|
|
|
|
*
|
|
|
|
* We no longer maintain this feature in maintained architectures, but
|
2021-08-09 23:49:08 +03:00
|
|
|
* we keep the API for backward compatibility. Currently the I and D
|
2018-04-27 19:50:56 +03:00
|
|
|
* operations are exactly the same and not distinguished in debuggers.
|
|
|
|
*/
|
|
|
|
case PT_WRITE_I:
|
2017-12-17 18:43:27 +03:00
|
|
|
case PT_WRITE_D:
|
2016-11-02 03:11:59 +03:00
|
|
|
write = 1;
|
|
|
|
tmp = data;
|
|
|
|
/* FALLTHROUGH */
|
2018-04-27 19:50:56 +03:00
|
|
|
case PT_READ_I:
|
2017-12-17 18:43:27 +03:00
|
|
|
case PT_READ_D:
|
2017-12-24 01:12:19 +03:00
|
|
|
piod.piod_addr = &tmp;
|
|
|
|
piod.piod_len = sizeof(tmp);
|
|
|
|
piod.piod_offs = addr;
|
|
|
|
piod.piod_op = write ? PIOD_WRITE_D : PIOD_READ_D;
|
|
|
|
if ((error = ptrace_doio(l, t, lt, &piod, addr, true)) != 0)
|
|
|
|
break;
|
2019-05-25 06:20:43 +03:00
|
|
|
/*
|
|
|
|
* For legacy reasons we treat here two results as success:
|
|
|
|
* - incomplete transfer piod.piod_len < sizeof(tmp)
|
|
|
|
* - no transfer piod.piod_len == 0
|
|
|
|
*
|
|
|
|
* This means that there is no way to determine whether
|
|
|
|
* transfer operation was performed in PT_WRITE and PT_READ
|
|
|
|
* calls.
|
|
|
|
*/
|
2016-11-02 03:11:59 +03:00
|
|
|
if (!write)
|
|
|
|
*retval = tmp;
|
|
|
|
break;
|
|
|
|
|
2017-12-17 18:43:27 +03:00
|
|
|
case PT_IO:
|
2017-12-17 23:59:27 +03:00
|
|
|
if ((error = ptm->ptm_copyin_piod(&piod, addr, data)) != 0)
|
2016-11-02 03:11:59 +03:00
|
|
|
break;
|
2019-05-01 01:32:01 +03:00
|
|
|
if (piod.piod_len < 1) {
|
|
|
|
error = EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
2017-12-24 01:12:19 +03:00
|
|
|
if ((error = ptrace_doio(l, t, lt, &piod, addr, false)) != 0)
|
2016-11-02 03:11:59 +03:00
|
|
|
break;
|
2019-05-25 06:20:43 +03:00
|
|
|
/*
|
|
|
|
* For legacy reasons we treat here two results as success:
|
|
|
|
* - incomplete transfer piod.piod_len < sizeof(tmp)
|
|
|
|
* - no transfer piod.piod_len == 0
|
|
|
|
*/
|
2018-01-08 07:45:53 +03:00
|
|
|
error = ptm->ptm_copyout_piod(&piod, addr, data);
|
2016-11-02 03:11:59 +03:00
|
|
|
break;
|
|
|
|
|
2017-12-17 18:43:27 +03:00
|
|
|
case PT_DUMPCORE:
|
|
|
|
error = ptrace_dumpcore(lt, addr, data);
|
2016-11-02 03:11:59 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
#ifdef PT_STEP
|
2017-12-17 18:43:27 +03:00
|
|
|
case PT_STEP:
|
2016-11-02 03:11:59 +03:00
|
|
|
/*
|
|
|
|
* From the 4.4BSD PRM:
|
|
|
|
* "Execution continues as in request PT_CONTINUE; however
|
|
|
|
* as soon as possible after execution of at least one
|
|
|
|
* instruction, execution stops again. [ ... ]"
|
|
|
|
*/
|
|
|
|
#endif
|
2017-12-17 18:43:27 +03:00
|
|
|
case PT_CONTINUE:
|
|
|
|
case PT_SYSCALL:
|
|
|
|
case PT_DETACH:
|
2016-11-02 03:11:59 +03:00
|
|
|
if (req == PT_SYSCALL) {
|
|
|
|
if (!ISSET(t->p_slflag, PSL_SYSCALL)) {
|
|
|
|
SET(t->p_slflag, PSL_SYSCALL);
|
|
|
|
#ifdef __HAVE_SYSCALL_INTERN
|
|
|
|
(*t->p_emul->e_syscall_intern)(t);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (ISSET(t->p_slflag, PSL_SYSCALL)) {
|
|
|
|
CLR(t->p_slflag, PSL_SYSCALL);
|
|
|
|
#ifdef __HAVE_SYSCALL_INTERN
|
|
|
|
(*t->p_emul->e_syscall_intern)(t);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
t->p_trace_enabled = trace_is_enabled(t);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Pick up the LWPID, if supplied. There are two cases:
|
|
|
|
* data < 0 : step or continue single thread, lwp = -data
|
|
|
|
* data > 0 in PT_STEP : step this thread, continue others
|
|
|
|
* For operations other than PT_STEP, data > 0 means
|
|
|
|
* data is the signo to deliver to the process.
|
|
|
|
*/
|
|
|
|
tmp = data;
|
|
|
|
if (tmp >= 0) {
|
|
|
|
#ifdef PT_STEP
|
|
|
|
if (req == PT_STEP)
|
|
|
|
signo = 0;
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
signo = tmp;
|
|
|
|
tmp = 0; /* don't search for LWP */
|
|
|
|
}
|
2019-10-12 15:04:37 +03:00
|
|
|
} else if (tmp == INT_MIN) {
|
|
|
|
error = ESRCH;
|
|
|
|
break;
|
|
|
|
} else {
|
2016-11-02 03:11:59 +03:00
|
|
|
tmp = -tmp;
|
2019-10-12 15:04:37 +03:00
|
|
|
}
|
2016-11-02 03:11:59 +03:00
|
|
|
|
|
|
|
if (tmp > 0) {
|
|
|
|
if (req == PT_DETACH) {
|
|
|
|
error = EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
lwp_delref2 (lt);
|
|
|
|
lt = lwp_find(t, tmp);
|
|
|
|
if (lt == NULL) {
|
|
|
|
error = ESRCH;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
lwp_addref(lt);
|
|
|
|
resume_all = 0;
|
|
|
|
signo = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* From the 4.4BSD PRM:
|
|
|
|
* "The data argument is taken as a signal number and the
|
|
|
|
* child's execution continues at location addr as if it
|
|
|
|
* incurred that signal. Normally the signal number will
|
|
|
|
* be either 0 to indicate that the signal that caused the
|
|
|
|
* stop should be ignored, or that value fetched out of
|
|
|
|
* the process's image indicating which signal caused
|
|
|
|
* the stop. If addr is (int *)1 then execution continues
|
|
|
|
* from where it stopped."
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Check that the data is a valid signal number or zero. */
|
|
|
|
if (signo < 0 || signo >= NSIG) {
|
|
|
|
error = EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2017-02-23 02:43:43 +03:00
|
|
|
/* Prevent process deadlock */
|
|
|
|
if (resume_all) {
|
|
|
|
#ifdef PT_STEP
|
|
|
|
if (req == PT_STEP) {
|
2019-10-04 01:48:44 +03:00
|
|
|
if (lt->l_flag &
|
|
|
|
(LW_WSUSPEND | LW_DBGSUSPEND)) {
|
2017-02-23 02:43:43 +03:00
|
|
|
error = EDEADLK;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
error = EDEADLK;
|
|
|
|
LIST_FOREACH(lt2, &t->p_lwps, l_sibling) {
|
2019-10-04 01:48:44 +03:00
|
|
|
if ((lt2->l_flag &
|
|
|
|
(LW_WSUSPEND | LW_DBGSUSPEND)) == 0
|
|
|
|
) {
|
2017-02-23 02:43:43 +03:00
|
|
|
error = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (error != 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
2019-10-08 15:29:57 +03:00
|
|
|
if (lt->l_flag & (LW_WSUSPEND | LW_DBGSUSPEND)) {
|
2017-02-23 02:43:43 +03:00
|
|
|
error = EDEADLK;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-01 20:02:40 +03:00
|
|
|
/*
|
2019-10-08 00:32:51 +03:00
|
|
|
* Reject setting program counter to 0x0 if VA0 is disabled.
|
2019-05-01 20:02:40 +03:00
|
|
|
*
|
2019-05-25 06:20:43 +03:00
|
|
|
* Not all kernels implement this feature to set Program
|
|
|
|
* Counter in one go in PT_CONTINUE and similar operations.
|
|
|
|
* This causes portability issues as passing address 0x0
|
|
|
|
* on these kernels is no-operation, but can cause failure
|
|
|
|
* in most cases on NetBSD.
|
2019-05-01 20:02:40 +03:00
|
|
|
*/
|
2019-05-25 06:20:43 +03:00
|
|
|
if (user_va0_disable && addr == 0) {
|
2019-05-01 20:02:40 +03:00
|
|
|
error = EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2016-11-02 03:11:59 +03:00
|
|
|
/* If the address parameter is not (int *)1, set the pc. */
|
|
|
|
if ((int *)addr != (int *)1) {
|
|
|
|
error = process_set_pc(lt, addr);
|
|
|
|
if (error != 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
#ifdef PT_STEP
|
|
|
|
/*
|
|
|
|
* Arrange for a single-step, if that's requested and possible.
|
|
|
|
* More precisely, set the single step status as requested for
|
|
|
|
* the requested thread, and clear it for other threads.
|
|
|
|
*/
|
|
|
|
LIST_FOREACH(lt2, &t->p_lwps, l_sibling) {
|
2020-05-27 02:08:56 +03:00
|
|
|
error = process_sstep(lt2,
|
|
|
|
ISSET(lt2->l_pflag, LP_SINGLESTEP));
|
|
|
|
if (error)
|
|
|
|
break;
|
2016-11-02 03:11:59 +03:00
|
|
|
}
|
2020-05-27 02:08:56 +03:00
|
|
|
if (error)
|
|
|
|
break;
|
2017-04-08 03:25:49 +03:00
|
|
|
error = process_sstep(lt,
|
|
|
|
ISSET(lt->l_pflag, LP_SINGLESTEP) || req == PT_STEP);
|
2016-11-02 03:11:59 +03:00
|
|
|
if (error)
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
if (req == PT_DETACH) {
|
2020-05-08 13:35:51 +03:00
|
|
|
CLR(t->p_slflag,
|
|
|
|
PSL_TRACED|PSL_TRACEDCHILD|PSL_SYSCALL);
|
2016-11-02 03:11:59 +03:00
|
|
|
|
2020-05-14 16:32:15 +03:00
|
|
|
/* clear sigpass mask */
|
|
|
|
sigemptyset(&t->p_sigctx.ps_sigpass);
|
|
|
|
|
2016-11-02 03:11:59 +03:00
|
|
|
/* give process back to original parent or init */
|
|
|
|
if (t->p_opptr != t->p_pptr) {
|
|
|
|
struct proc *pp = t->p_opptr;
|
|
|
|
proc_reparent(t, pp ? pp : initproc);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* not being traced any more */
|
|
|
|
t->p_opptr = NULL;
|
2017-04-08 03:25:49 +03:00
|
|
|
|
|
|
|
/* clear single step */
|
|
|
|
LIST_FOREACH(lt2, &t->p_lwps, l_sibling) {
|
|
|
|
CLR(lt2->l_pflag, LP_SINGLESTEP);
|
|
|
|
}
|
|
|
|
CLR(lt->l_pflag, LP_SINGLESTEP);
|
2016-11-02 03:11:59 +03:00
|
|
|
}
|
|
|
|
sendsig:
|
2019-10-09 16:19:43 +03:00
|
|
|
error = ptrace_sendsig(l, req, t, lt, signo, resume_all);
|
2016-11-02 03:11:59 +03:00
|
|
|
break;
|
|
|
|
|
2017-12-17 18:43:27 +03:00
|
|
|
case PT_SYSCALLEMU:
|
2016-11-02 03:11:59 +03:00
|
|
|
if (!ISSET(t->p_slflag, PSL_SYSCALL) || t->p_stat != SSTOP) {
|
|
|
|
error = EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
SET(t->p_slflag, PSL_SYSCALLEMU);
|
|
|
|
break;
|
|
|
|
|
2017-04-08 03:25:49 +03:00
|
|
|
#ifdef PT_STEP
|
2017-12-17 18:43:27 +03:00
|
|
|
case PT_SETSTEP:
|
2017-04-08 03:25:49 +03:00
|
|
|
write = 1;
|
|
|
|
|
2019-02-03 06:19:25 +03:00
|
|
|
/* FALLTHROUGH */
|
2017-12-17 18:43:27 +03:00
|
|
|
case PT_CLEARSTEP:
|
2017-04-08 03:25:49 +03:00
|
|
|
/* write = 0 done above. */
|
2017-12-17 07:35:21 +03:00
|
|
|
if ((error = ptrace_update_lwp(t, <, data)) != 0)
|
|
|
|
break;
|
2017-04-08 03:25:49 +03:00
|
|
|
|
2017-12-17 07:35:21 +03:00
|
|
|
if (write)
|
2017-04-08 03:25:49 +03:00
|
|
|
SET(lt->l_pflag, LP_SINGLESTEP);
|
|
|
|
else
|
|
|
|
CLR(lt->l_pflag, LP_SINGLESTEP);
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
|
2017-12-17 18:43:27 +03:00
|
|
|
case PT_KILL:
|
2016-11-02 03:11:59 +03:00
|
|
|
/* just send the process a KILL signal. */
|
|
|
|
signo = SIGKILL;
|
|
|
|
goto sendsig; /* in PT_CONTINUE, above. */
|
|
|
|
|
2019-10-09 16:19:43 +03:00
|
|
|
case PT_STOP:
|
|
|
|
/* just send the process a STOP signal. */
|
|
|
|
signo = SIGSTOP;
|
|
|
|
goto sendsig; /* in PT_CONTINUE, above. */
|
|
|
|
|
2017-12-17 18:43:27 +03:00
|
|
|
case PT_ATTACH:
|
2016-11-02 03:11:59 +03:00
|
|
|
/*
|
|
|
|
* Go ahead and set the trace flag.
|
|
|
|
* Save the old parent (it's reset in
|
|
|
|
* _DETACH, and also in kern_exit.c:wait4()
|
|
|
|
* Reparent the process so that the tracing
|
|
|
|
* proc gets to see all the action.
|
|
|
|
* Stop the target.
|
|
|
|
*/
|
2016-11-04 21:14:04 +03:00
|
|
|
proc_changeparent(t, p);
|
2016-11-12 23:03:17 +03:00
|
|
|
signo = SIGSTOP;
|
2016-11-02 03:11:59 +03:00
|
|
|
goto sendsig;
|
|
|
|
|
2017-12-17 18:43:27 +03:00
|
|
|
case PT_GET_EVENT_MASK:
|
2017-12-17 07:35:21 +03:00
|
|
|
error = ptrace_get_event_mask(t, addr, data);
|
2016-11-02 03:11:59 +03:00
|
|
|
break;
|
|
|
|
|
2017-12-17 18:43:27 +03:00
|
|
|
case PT_SET_EVENT_MASK:
|
2017-12-17 07:35:21 +03:00
|
|
|
error = ptrace_set_event_mask(t, addr, data);
|
2016-11-02 03:11:59 +03:00
|
|
|
break;
|
|
|
|
|
2017-12-17 18:43:27 +03:00
|
|
|
case PT_GET_PROCESS_STATE:
|
2017-12-17 07:35:21 +03:00
|
|
|
error = ptrace_get_process_state(t, addr, data);
|
2016-11-02 03:11:59 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
case PT_LWPINFO:
|
2017-12-17 07:35:21 +03:00
|
|
|
error = ptrace_lwpinfo(t, <, addr, data);
|
2016-11-02 03:11:59 +03:00
|
|
|
break;
|
|
|
|
|
2017-12-17 18:43:27 +03:00
|
|
|
case PT_SET_SIGINFO:
|
2017-12-17 23:59:27 +03:00
|
|
|
error = ptrace_set_siginfo(t, <, ptm, addr, data);
|
2017-01-07 01:53:17 +03:00
|
|
|
break;
|
|
|
|
|
2017-12-17 18:43:27 +03:00
|
|
|
case PT_GET_SIGINFO:
|
2017-12-17 23:59:27 +03:00
|
|
|
error = ptrace_get_siginfo(t, ptm, addr, data);
|
2017-01-07 01:53:17 +03:00
|
|
|
break;
|
|
|
|
|
2017-12-17 18:43:27 +03:00
|
|
|
case PT_RESUME:
|
|
|
|
case PT_SUSPEND:
|
2017-12-17 07:35:21 +03:00
|
|
|
error = ptrace_startstop(t, <, req, addr, data);
|
2017-02-23 02:43:43 +03:00
|
|
|
break;
|
|
|
|
|
2019-12-24 17:50:59 +03:00
|
|
|
case PT_LWPSTATUS:
|
|
|
|
error = ptrace_lwpstatus(t, ptm, <, addr, data, false);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PT_LWPNEXT:
|
|
|
|
error = ptrace_lwpstatus(t, ptm, <, addr, data, true);
|
|
|
|
break;
|
|
|
|
|
2020-05-14 16:32:15 +03:00
|
|
|
case PT_SET_SIGPASS:
|
|
|
|
error = ptrace_set_sigpass(t, addr, data);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PT_GET_SIGPASS:
|
|
|
|
error = ptrace_get_sigpass(t, addr, data);
|
|
|
|
break;
|
|
|
|
|
2017-12-17 18:43:27 +03:00
|
|
|
#ifdef PT_REGISTERS
|
|
|
|
case_PT_SETREGS
|
|
|
|
case_PT_GETREGS
|
|
|
|
case_PT_SETFPREGS
|
|
|
|
case_PT_GETFPREGS
|
|
|
|
case_PT_SETDBREGS
|
|
|
|
case_PT_GETDBREGS
|
2017-12-17 07:35:21 +03:00
|
|
|
error = ptrace_regs(l, <, req, ptm, addr, data);
|
2016-12-15 15:04:17 +03:00
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
|
2016-11-02 03:11:59 +03:00
|
|
|
#ifdef __HAVE_PTRACE_MACHDEP
|
|
|
|
PTRACE_MACHDEP_REQUEST_CASES
|
2020-10-15 20:37:35 +03:00
|
|
|
error = ptrace_machdep_dorequest(l, <, req, addr, data);
|
2016-11-02 03:11:59 +03:00
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2017-12-17 07:35:21 +03:00
|
|
|
out:
|
2016-11-02 03:11:59 +03:00
|
|
|
if (pheld) {
|
|
|
|
mutex_exit(t->p_lock);
|
2020-05-24 02:42:41 +03:00
|
|
|
mutex_exit(&proc_lock);
|
2016-11-02 03:11:59 +03:00
|
|
|
}
|
|
|
|
if (lt != NULL)
|
|
|
|
lwp_delref(lt);
|
2019-06-29 14:37:17 +03:00
|
|
|
if (locked)
|
|
|
|
rw_exit(&t->p_reflock);
|
2016-11-02 03:11:59 +03:00
|
|
|
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
process_auxv_offset(struct proc *p, struct uio *uio)
|
|
|
|
{
|
|
|
|
struct ps_strings pss;
|
|
|
|
int error;
|
|
|
|
off_t off = (off_t)p->p_psstrp;
|
|
|
|
|
|
|
|
if ((error = copyin_psstrings(p, &pss)) != 0)
|
|
|
|
return error;
|
|
|
|
|
|
|
|
if (pss.ps_envstr == NULL)
|
|
|
|
return EIO;
|
|
|
|
|
2019-11-17 05:22:14 +03:00
|
|
|
#ifdef COMPAT_NETBSD32
|
|
|
|
if (p->p_flag & PK_32)
|
|
|
|
uio->uio_offset += (off_t)((vaddr_t)pss.ps_envstr +
|
|
|
|
sizeof(uint32_t) * (pss.ps_nenvstr + 1));
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
uio->uio_offset += (off_t)(vaddr_t)(pss.ps_envstr +
|
|
|
|
pss.ps_nenvstr + 1);
|
|
|
|
|
2016-11-02 03:11:59 +03:00
|
|
|
#ifdef __MACHINE_STACK_GROWS_UP
|
|
|
|
if (uio->uio_offset < off)
|
|
|
|
return EIO;
|
|
|
|
#else
|
|
|
|
if (uio->uio_offset > off)
|
|
|
|
return EIO;
|
|
|
|
if ((uio->uio_offset + uio->uio_resid) > off)
|
|
|
|
uio->uio_resid = off - uio->uio_offset;
|
|
|
|
#endif
|
|
|
|
return 0;
|
|
|
|
}
|
2020-10-19 17:52:19 +03:00
|
|
|
|
|
|
|
MODULE(MODULE_CLASS_EXEC, ptrace_common, NULL);
|
|
|
|
|
2020-10-25 18:55:36 +03:00
|
|
|
static int
|
|
|
|
ptrace_common_init(void)
|
|
|
|
{
|
|
|
|
|
2020-11-04 22:27:41 +03:00
|
|
|
#if 0
|
|
|
|
mutex_init(&ptrace_mtx, MUTEX_DEFAULT, IPL_NONE);
|
|
|
|
cv_init(&ptrace_cv, "ptracecb");
|
|
|
|
ptrace_cbref = 0;
|
|
|
|
#endif
|
|
|
|
ptrace_listener = kauth_listen_scope(KAUTH_SCOPE_PROCESS,
|
|
|
|
ptrace_listener_cb, NULL);
|
2020-10-25 18:55:36 +03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
ptrace_common_fini(void)
|
|
|
|
{
|
|
|
|
|
2020-11-04 22:27:41 +03:00
|
|
|
kauth_unlisten_scope(ptrace_listener);
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
/* Make sure no-one is executing our kauth listener */
|
|
|
|
|
|
|
|
mutex_enter(&ptrace_mtx);
|
|
|
|
while (ptrace_cbref != 0)
|
|
|
|
cv_wait(&ptrace_cv, &ptrace_mtx);
|
|
|
|
mutex_exit(&ptrace_mtx);
|
|
|
|
mutex_destroy(&ptrace_mtx);
|
|
|
|
cv_destroy(&ptrace_cv);
|
|
|
|
#endif
|
|
|
|
|
2020-10-25 18:55:36 +03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-10-19 17:52:19 +03:00
|
|
|
static int
|
|
|
|
ptrace_common_modcmd(modcmd_t cmd, void *arg)
|
|
|
|
{
|
|
|
|
int error;
|
|
|
|
|
|
|
|
switch (cmd) {
|
|
|
|
case MODULE_CMD_INIT:
|
2020-10-25 18:55:36 +03:00
|
|
|
error = ptrace_common_init();
|
2020-10-19 17:52:19 +03:00
|
|
|
break;
|
|
|
|
case MODULE_CMD_FINI:
|
2020-10-25 18:55:36 +03:00
|
|
|
error = ptrace_common_fini();
|
2020-10-19 17:52:19 +03:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ptrace_hooks();
|
|
|
|
error = ENOTTY;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return error;
|
|
|
|
}
|