2019-10-04 01:48:44 +03:00
|
|
|
/* $NetBSD: sys_ptrace_common.c,v 1.62 2019/10/03 22:48:44 kamil 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
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* References:
|
|
|
|
* (1) Bach's "The Design of the UNIX Operating System",
|
|
|
|
* (2) sys/miscfs/procfs from UCB's 4.4BSD-Lite distribution,
|
|
|
|
* (3) the "4.4BSD Programmer's Reference Manual" published
|
|
|
|
* by USENIX and O'Reilly & Associates.
|
|
|
|
* The 4.4BSD PRM does a reasonably good job of documenting what the various
|
|
|
|
* ptrace() requests should actually do, and its text is quoted several times
|
|
|
|
* in this file.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <sys/cdefs.h>
|
2019-10-04 01:48:44 +03:00
|
|
|
__KERNEL_RCSID(0, "$NetBSD: sys_ptrace_common.c,v 1.62 2019/10/03 22:48:44 kamil 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>
|
|
|
|
#include <sys/module.h>
|
|
|
|
#include <sys/condvar.h>
|
|
|
|
#include <sys/mutex.h>
|
|
|
|
|
|
|
|
#include <uvm/uvm_extern.h>
|
|
|
|
|
|
|
|
#include <machine/reg.h>
|
|
|
|
|
|
|
|
#ifdef PTRACE
|
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
|
|
|
|
|
|
|
|
#if defined(PT_SETREGS) || defined(PT_GETREGS) || \
|
2019-05-11 00:08:26 +03:00
|
|
|
defined(PT_SETFPREGS) || defined(PT_GETFPREGS) || \
|
2017-12-17 18:43:27 +03:00
|
|
|
defined(PT_SETDBREGS) || defined(PT_GETDBREGS)
|
|
|
|
# define PT_REGISTERS
|
|
|
|
#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:
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
ptrace_init(void)
|
|
|
|
{
|
|
|
|
|
|
|
|
#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);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
ptrace_fini(void)
|
|
|
|
{
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
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:
|
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.
|
2016-11-02 03:11:59 +03:00
|
|
|
*/
|
|
|
|
if (t->p_stat != SSTOP || !t->p_waited /* XXXSMP */) {
|
|
|
|
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:
|
|
|
|
return 1;
|
|
|
|
default:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
ptrace_update_lwp(struct proc *t, struct lwp **lt, lwpid_t lid)
|
|
|
|
{
|
|
|
|
if (lid == 0 || lid == (*lt)->l_lid || t->p_nlwps == 1)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
mutex_enter(t->p_lock);
|
2019-07-18 23:10:46 +03:00
|
|
|
lwp_delref2(*lt);
|
|
|
|
|
2017-12-17 07:35:21 +03:00
|
|
|
*lt = lwp_find(t, lid);
|
|
|
|
if (*lt == NULL) {
|
|
|
|
mutex_exit(t->p_lock);
|
|
|
|
return ESRCH;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((*lt)->l_flag & LW_SYSTEM) {
|
2019-07-18 23:10:46 +03:00
|
|
|
mutex_exit(t->p_lock);
|
2017-12-17 07:35:21 +03:00
|
|
|
*lt = NULL;
|
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
lwp_addref(*lt);
|
|
|
|
mutex_exit(t->p_lock);
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
while (*lt != NULL && !lwp_alive(*lt))
|
|
|
|
*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));
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2018-01-08 09:10:30 +03:00
|
|
|
struct proc *t = (*lt)->l_proc;
|
2017-12-17 07:35:21 +03:00
|
|
|
struct vmspace *vm;
|
|
|
|
|
|
|
|
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;
|
|
|
|
size = PROC_REGSZ(t);
|
|
|
|
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;
|
|
|
|
size = PROC_FPREGSZ(t);
|
|
|
|
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;
|
|
|
|
size = PROC_DBREGSZ(t);
|
|
|
|
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
|
|
|
|
ptrace_sendsig(struct proc *t, struct lwp *lt, int signo, int resume_all)
|
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
KSI_INIT_EMPTY(&ksi);
|
|
|
|
if (t->p_sigctx.ps_faked) {
|
|
|
|
if (signo != t->p_sigctx.ps_info._signo)
|
|
|
|
return EINVAL;
|
|
|
|
t->p_sigctx.ps_faked = false;
|
|
|
|
ksi.ksi_info = t->p_sigctx.ps_info;
|
|
|
|
ksi.ksi_lid = t->p_sigctx.ps_lwp;
|
|
|
|
} else if (signo == 0) {
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
ksi.ksi_signo = signo;
|
|
|
|
}
|
2018-06-23 06:32:48 +03:00
|
|
|
DPRINTF(("%s: pid=%d.%d signal=%d resume_all=%d\n", __func__, t->p_pid,
|
|
|
|
t->p_sigctx.ps_lwp, signo, resume_all));
|
2017-12-17 18:43:27 +03:00
|
|
|
|
|
|
|
kpsignal2(t, &ksi);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
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));
|
2017-12-17 18:43:27 +03:00
|
|
|
error = (*coredump_vec)(lt, path);
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
mutex_enter(proc_lock);
|
|
|
|
|
|
|
|
t = ptrace_find(l, req, pid);
|
|
|
|
if (t == NULL) {
|
2016-11-02 03:11:59 +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);
|
2017-12-17 07:35:21 +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
|
|
|
|
* we keep the API for backward compatiblity. Currently the I and D
|
|
|
|
* 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 */
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
tmp = -tmp;
|
|
|
|
|
|
|
|
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-04 01:48:44 +03:00
|
|
|
if (lt->l_flag & (LW_WSUSPEND | LW_WSUSPEND)) {
|
2017-02-23 02:43:43 +03:00
|
|
|
error = EDEADLK;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-01 20:02:40 +03:00
|
|
|
/*
|
2019-05-25 06:20:43 +03:00
|
|
|
* Reject setting program cunter 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) {
|
2017-04-08 03:25:49 +03:00
|
|
|
if (ISSET(lt2->l_pflag, LP_SINGLESTEP)) {
|
|
|
|
lwp_lock(lt2);
|
|
|
|
process_sstep(lt2, 1);
|
|
|
|
lwp_unlock(lt2);
|
|
|
|
} else if (lt != lt2) {
|
2016-11-02 03:11:59 +03:00
|
|
|
lwp_lock(lt2);
|
|
|
|
process_sstep(lt2, 0);
|
|
|
|
lwp_unlock(lt2);
|
|
|
|
}
|
|
|
|
}
|
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) {
|
2017-08-28 03:46:06 +03:00
|
|
|
CLR(t->p_slflag, PSL_TRACED|PSL_SYSCALL);
|
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:
|
2017-12-17 18:43:27 +03:00
|
|
|
error = ptrace_sendsig(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. */
|
|
|
|
|
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;
|
|
|
|
|
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
|
|
|
|
error = ptrace_machdep_dorequest(l, lt, req, addr, data);
|
|
|
|
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);
|
|
|
|
mutex_exit(proc_lock);
|
|
|
|
}
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2017-12-28 21:29:45 +03:00
|
|
|
typedef int (*regrfunc_t)(struct lwp *, void *, size_t *);
|
|
|
|
typedef int (*regwfunc_t)(struct lwp *, void *, size_t);
|
2017-12-07 18:21:34 +03:00
|
|
|
|
2017-12-17 18:43:27 +03:00
|
|
|
#ifdef PT_REGISTERS
|
2017-12-07 18:21:34 +03:00
|
|
|
static int
|
2017-12-28 21:29:45 +03:00
|
|
|
proc_regio(struct lwp *l, struct uio *uio, size_t ks, regrfunc_t r,
|
|
|
|
regwfunc_t w)
|
2016-11-02 03:11:59 +03:00
|
|
|
{
|
2017-12-07 18:21:34 +03:00
|
|
|
char buf[1024];
|
2016-11-02 03:11:59 +03:00
|
|
|
int error;
|
|
|
|
char *kv;
|
2017-12-28 21:29:45 +03:00
|
|
|
size_t kl;
|
2016-11-02 03:11:59 +03:00
|
|
|
|
2017-12-28 21:29:45 +03:00
|
|
|
if (ks > sizeof(buf))
|
2017-12-07 18:21:34 +03:00
|
|
|
return E2BIG;
|
2016-11-02 03:11:59 +03:00
|
|
|
|
2017-12-28 21:29:45 +03:00
|
|
|
if (uio->uio_offset < 0 || uio->uio_offset > (off_t)ks)
|
2017-12-07 18:21:34 +03:00
|
|
|
return EINVAL;
|
2016-11-02 03:11:59 +03:00
|
|
|
|
2017-12-07 18:21:34 +03:00
|
|
|
kv = buf + uio->uio_offset;
|
2017-12-28 21:29:45 +03:00
|
|
|
kl = ks - uio->uio_offset;
|
2017-12-07 18:21:34 +03:00
|
|
|
|
|
|
|
if (kl > uio->uio_resid)
|
2016-11-02 03:11:59 +03:00
|
|
|
kl = uio->uio_resid;
|
|
|
|
|
2017-12-28 21:29:45 +03:00
|
|
|
error = (*r)(l, buf, &ks);
|
2016-11-02 03:11:59 +03:00
|
|
|
if (error == 0)
|
|
|
|
error = uiomove(kv, kl, uio);
|
|
|
|
if (error == 0 && uio->uio_rw == UIO_WRITE) {
|
|
|
|
if (l->l_stat != LSSTOP)
|
|
|
|
error = EBUSY;
|
|
|
|
else
|
2017-12-28 21:29:45 +03:00
|
|
|
error = (*w)(l, buf, ks);
|
2016-11-02 03:11:59 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
uio->uio_offset = 0;
|
|
|
|
return error;
|
2017-12-07 18:21:34 +03:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
int
|
|
|
|
process_doregs(struct lwp *curl /*tracer*/,
|
|
|
|
struct lwp *l /*traced*/,
|
|
|
|
struct uio *uio)
|
|
|
|
{
|
|
|
|
#if defined(PT_GETREGS) || defined(PT_SETREGS)
|
|
|
|
size_t s;
|
2017-12-28 21:29:45 +03:00
|
|
|
regrfunc_t r;
|
|
|
|
regwfunc_t w;
|
2017-12-07 18:21:34 +03:00
|
|
|
|
|
|
|
#ifdef COMPAT_NETBSD32
|
2019-06-24 23:29:41 +03:00
|
|
|
const bool pk32 = (curl->l_proc->p_flag & PK_32) != 0;
|
2017-12-07 18:21:34 +03:00
|
|
|
|
|
|
|
if (__predict_false(pk32)) {
|
2019-06-24 23:29:41 +03:00
|
|
|
if ((l->l_proc->p_flag & PK_32) == 0) {
|
|
|
|
// 32 bit tracer can't trace 64 bit process
|
|
|
|
return EINVAL;
|
|
|
|
}
|
2017-12-09 08:18:45 +03:00
|
|
|
s = sizeof(process_reg32);
|
2017-12-28 21:29:45 +03:00
|
|
|
r = (regrfunc_t)process_read_regs32;
|
|
|
|
w = (regwfunc_t)process_write_regs32;
|
2017-12-07 18:21:34 +03:00
|
|
|
} else
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
s = sizeof(struct reg);
|
2017-12-28 21:29:45 +03:00
|
|
|
r = (regrfunc_t)process_read_regs;
|
|
|
|
w = (regwfunc_t)process_write_regs;
|
2017-12-07 18:21:34 +03:00
|
|
|
}
|
|
|
|
return proc_regio(l, uio, s, r, w);
|
2016-11-02 03:11:59 +03:00
|
|
|
#else
|
|
|
|
return EINVAL;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
process_validregs(struct lwp *l)
|
|
|
|
{
|
|
|
|
|
|
|
|
#if defined(PT_SETREGS) || defined(PT_GETREGS)
|
|
|
|
return (l->l_flag & LW_SYSTEM) == 0;
|
|
|
|
#else
|
|
|
|
return 0;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
process_dofpregs(struct lwp *curl /*tracer*/,
|
|
|
|
struct lwp *l /*traced*/,
|
|
|
|
struct uio *uio)
|
|
|
|
{
|
|
|
|
#if defined(PT_GETFPREGS) || defined(PT_SETFPREGS)
|
2017-12-07 18:21:34 +03:00
|
|
|
size_t s;
|
2017-12-28 21:29:45 +03:00
|
|
|
regrfunc_t r;
|
|
|
|
regwfunc_t w;
|
2016-11-02 03:11:59 +03:00
|
|
|
|
2017-12-07 18:21:34 +03:00
|
|
|
#ifdef COMPAT_NETBSD32
|
2019-06-24 23:29:41 +03:00
|
|
|
const bool pk32 = (curl->l_proc->p_flag & PK_32) != 0;
|
2016-11-02 03:11:59 +03:00
|
|
|
|
2017-12-07 18:21:34 +03:00
|
|
|
if (__predict_false(pk32)) {
|
2019-06-24 23:29:41 +03:00
|
|
|
if ((l->l_proc->p_flag & PK_32) == 0) {
|
|
|
|
// 32 bit tracer can't trace 64 bit process
|
|
|
|
return EINVAL;
|
|
|
|
}
|
2017-12-09 08:18:45 +03:00
|
|
|
s = sizeof(process_fpreg32);
|
2017-12-28 21:29:45 +03:00
|
|
|
r = (regrfunc_t)process_read_fpregs32;
|
|
|
|
w = (regwfunc_t)process_write_fpregs32;
|
2017-12-07 18:21:34 +03:00
|
|
|
} else
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
s = sizeof(struct fpreg);
|
2017-12-28 21:29:45 +03:00
|
|
|
r = (regrfunc_t)process_read_fpregs;
|
|
|
|
w = (regwfunc_t)process_write_fpregs;
|
2016-11-02 03:11:59 +03:00
|
|
|
}
|
2017-12-07 18:21:34 +03:00
|
|
|
return proc_regio(l, uio, s, r, w);
|
2016-11-02 03:11:59 +03:00
|
|
|
#else
|
|
|
|
return EINVAL;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
process_validfpregs(struct lwp *l)
|
|
|
|
{
|
|
|
|
|
|
|
|
#if defined(PT_SETFPREGS) || defined(PT_GETFPREGS)
|
|
|
|
return (l->l_flag & LW_SYSTEM) == 0;
|
|
|
|
#else
|
|
|
|
return 0;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
Introduce PT_GETDBREGS and PT_SETDBREGS in ptrace(2) on i386 and amd64
This interface is modeled after FreeBSD API with the usage.
This replaced previous watchpoint API. The previous one was introduced
recently in NetBSD-current and remove its spurs without any
backward-compatibility.
Design choices for Debug Register accessors:
- exec() (TRAP_EXEC event) must remove debug registers from LWP
- debug registers are only per-LWP, not per-process globally
- debug registers must not be inherited after (v)forking a process
- debug registers must not be inherited after forking a thread
- a debugger is responsible to set global watchpoints/breakpoints with the
debug registers, to achieve this PTRACE_LWP_CREATE/PTRACE_LWP_EXIT event
monitoring function is designed to be used
- debug register traps must generate SIGTRAP with si_code TRAP_DBREG
- debugger is responsible to retrieve debug register state to distinguish
the exact debug register trap (DR6 is Status Register on x86)
- kernel must not remove debug register traps after triggering a trap event
a debugger is responsible to detach this trap with appropriate PT_SETDBREGS
call (DR7 is Control Register on x86)
- debug registers must not be exposed in mcontext
- userland must not be allowed to set a trap on the kernel
Implementation notes on i386 and amd64:
- the initial state of debug register is retrieved on boot and this value is
stored in a local copy (initdbregs), this value is used to initialize dbreg
context after PT_GETDBREGS
- struct dbregs is stored in pcb as a pointer and by default not initialized
- reserved registers (DR4-DR5, DR9-DR15) are ignored
Further ideas:
- restrict this interface with securelevel
Tested on real hardware i386 (Intel Pentium IV) and amd64 (Intel i7).
This commit enables 390 debug register ATF tests in kernel/arch/x86.
All tests are passing.
This commit does not cover netbsd32 compat code. Currently other interface
PT_GET_SIGINFO/PT_SET_SIGINFO is required in netbsd32 compat code in order to
validate reliably PT_GETDBREGS/PT_SETDBREGS.
This implementation does not cover FreeBSD specific defines in their
<x86/reg.h>: DBREG_DR7_LOCAL_ENABLE, DBREG_DR7_GLOBAL_ENABLE, DBREG_DR7_LEN_1
etc. These values tend to be reinvented by each tracer on its own. GNU
Debugger (GDB) works with NetBSD debug registers after adding this patch:
--- gdb/amd64bsd-nat.c.orig 2016-02-10 03:19:39.000000000 +0000
+++ gdb/amd64bsd-nat.c
@@ -167,6 +167,10 @@ amd64bsd_target (void)
#ifdef HAVE_PT_GETDBREGS
+#ifndef DBREG_DRX
+#define DBREG_DRX(d,x) ((d)->dr[(x)])
+#endif
+
static unsigned long
amd64bsd_dr_get (ptid_t ptid, int regnum)
{
Another reason to stop introducing unpopular defines covering machine
specific register macros is that these value varies across generations of
the same CPU family.
GDB demo:
(gdb) c
Continuing.
Watchpoint 2: traceme
Old value = 0
New value = 16
main (argc=1, argv=0x7f7fff79fe30) at test.c:8
8 printf("traceme=%d\n", traceme);
(Currently the GDB interface is not reliable due to NetBSD support bugs)
Sponsored by <The NetBSD Foundation>
2017-02-23 06:34:22 +03:00
|
|
|
int
|
|
|
|
process_dodbregs(struct lwp *curl /*tracer*/,
|
|
|
|
struct lwp *l /*traced*/,
|
|
|
|
struct uio *uio)
|
|
|
|
{
|
|
|
|
#if defined(PT_GETDBREGS) || defined(PT_SETDBREGS)
|
2017-12-07 18:21:34 +03:00
|
|
|
size_t s;
|
2017-12-28 21:29:45 +03:00
|
|
|
regrfunc_t r;
|
|
|
|
regwfunc_t w;
|
Introduce PT_GETDBREGS and PT_SETDBREGS in ptrace(2) on i386 and amd64
This interface is modeled after FreeBSD API with the usage.
This replaced previous watchpoint API. The previous one was introduced
recently in NetBSD-current and remove its spurs without any
backward-compatibility.
Design choices for Debug Register accessors:
- exec() (TRAP_EXEC event) must remove debug registers from LWP
- debug registers are only per-LWP, not per-process globally
- debug registers must not be inherited after (v)forking a process
- debug registers must not be inherited after forking a thread
- a debugger is responsible to set global watchpoints/breakpoints with the
debug registers, to achieve this PTRACE_LWP_CREATE/PTRACE_LWP_EXIT event
monitoring function is designed to be used
- debug register traps must generate SIGTRAP with si_code TRAP_DBREG
- debugger is responsible to retrieve debug register state to distinguish
the exact debug register trap (DR6 is Status Register on x86)
- kernel must not remove debug register traps after triggering a trap event
a debugger is responsible to detach this trap with appropriate PT_SETDBREGS
call (DR7 is Control Register on x86)
- debug registers must not be exposed in mcontext
- userland must not be allowed to set a trap on the kernel
Implementation notes on i386 and amd64:
- the initial state of debug register is retrieved on boot and this value is
stored in a local copy (initdbregs), this value is used to initialize dbreg
context after PT_GETDBREGS
- struct dbregs is stored in pcb as a pointer and by default not initialized
- reserved registers (DR4-DR5, DR9-DR15) are ignored
Further ideas:
- restrict this interface with securelevel
Tested on real hardware i386 (Intel Pentium IV) and amd64 (Intel i7).
This commit enables 390 debug register ATF tests in kernel/arch/x86.
All tests are passing.
This commit does not cover netbsd32 compat code. Currently other interface
PT_GET_SIGINFO/PT_SET_SIGINFO is required in netbsd32 compat code in order to
validate reliably PT_GETDBREGS/PT_SETDBREGS.
This implementation does not cover FreeBSD specific defines in their
<x86/reg.h>: DBREG_DR7_LOCAL_ENABLE, DBREG_DR7_GLOBAL_ENABLE, DBREG_DR7_LEN_1
etc. These values tend to be reinvented by each tracer on its own. GNU
Debugger (GDB) works with NetBSD debug registers after adding this patch:
--- gdb/amd64bsd-nat.c.orig 2016-02-10 03:19:39.000000000 +0000
+++ gdb/amd64bsd-nat.c
@@ -167,6 +167,10 @@ amd64bsd_target (void)
#ifdef HAVE_PT_GETDBREGS
+#ifndef DBREG_DRX
+#define DBREG_DRX(d,x) ((d)->dr[(x)])
+#endif
+
static unsigned long
amd64bsd_dr_get (ptid_t ptid, int regnum)
{
Another reason to stop introducing unpopular defines covering machine
specific register macros is that these value varies across generations of
the same CPU family.
GDB demo:
(gdb) c
Continuing.
Watchpoint 2: traceme
Old value = 0
New value = 16
main (argc=1, argv=0x7f7fff79fe30) at test.c:8
8 printf("traceme=%d\n", traceme);
(Currently the GDB interface is not reliable due to NetBSD support bugs)
Sponsored by <The NetBSD Foundation>
2017-02-23 06:34:22 +03:00
|
|
|
|
2017-12-07 18:21:34 +03:00
|
|
|
#ifdef COMPAT_NETBSD32
|
2019-06-24 23:29:41 +03:00
|
|
|
const bool pk32 = (curl->l_proc->p_flag & PK_32) != 0;
|
Introduce PT_GETDBREGS and PT_SETDBREGS in ptrace(2) on i386 and amd64
This interface is modeled after FreeBSD API with the usage.
This replaced previous watchpoint API. The previous one was introduced
recently in NetBSD-current and remove its spurs without any
backward-compatibility.
Design choices for Debug Register accessors:
- exec() (TRAP_EXEC event) must remove debug registers from LWP
- debug registers are only per-LWP, not per-process globally
- debug registers must not be inherited after (v)forking a process
- debug registers must not be inherited after forking a thread
- a debugger is responsible to set global watchpoints/breakpoints with the
debug registers, to achieve this PTRACE_LWP_CREATE/PTRACE_LWP_EXIT event
monitoring function is designed to be used
- debug register traps must generate SIGTRAP with si_code TRAP_DBREG
- debugger is responsible to retrieve debug register state to distinguish
the exact debug register trap (DR6 is Status Register on x86)
- kernel must not remove debug register traps after triggering a trap event
a debugger is responsible to detach this trap with appropriate PT_SETDBREGS
call (DR7 is Control Register on x86)
- debug registers must not be exposed in mcontext
- userland must not be allowed to set a trap on the kernel
Implementation notes on i386 and amd64:
- the initial state of debug register is retrieved on boot and this value is
stored in a local copy (initdbregs), this value is used to initialize dbreg
context after PT_GETDBREGS
- struct dbregs is stored in pcb as a pointer and by default not initialized
- reserved registers (DR4-DR5, DR9-DR15) are ignored
Further ideas:
- restrict this interface with securelevel
Tested on real hardware i386 (Intel Pentium IV) and amd64 (Intel i7).
This commit enables 390 debug register ATF tests in kernel/arch/x86.
All tests are passing.
This commit does not cover netbsd32 compat code. Currently other interface
PT_GET_SIGINFO/PT_SET_SIGINFO is required in netbsd32 compat code in order to
validate reliably PT_GETDBREGS/PT_SETDBREGS.
This implementation does not cover FreeBSD specific defines in their
<x86/reg.h>: DBREG_DR7_LOCAL_ENABLE, DBREG_DR7_GLOBAL_ENABLE, DBREG_DR7_LEN_1
etc. These values tend to be reinvented by each tracer on its own. GNU
Debugger (GDB) works with NetBSD debug registers after adding this patch:
--- gdb/amd64bsd-nat.c.orig 2016-02-10 03:19:39.000000000 +0000
+++ gdb/amd64bsd-nat.c
@@ -167,6 +167,10 @@ amd64bsd_target (void)
#ifdef HAVE_PT_GETDBREGS
+#ifndef DBREG_DRX
+#define DBREG_DRX(d,x) ((d)->dr[(x)])
+#endif
+
static unsigned long
amd64bsd_dr_get (ptid_t ptid, int regnum)
{
Another reason to stop introducing unpopular defines covering machine
specific register macros is that these value varies across generations of
the same CPU family.
GDB demo:
(gdb) c
Continuing.
Watchpoint 2: traceme
Old value = 0
New value = 16
main (argc=1, argv=0x7f7fff79fe30) at test.c:8
8 printf("traceme=%d\n", traceme);
(Currently the GDB interface is not reliable due to NetBSD support bugs)
Sponsored by <The NetBSD Foundation>
2017-02-23 06:34:22 +03:00
|
|
|
|
2017-12-07 18:21:34 +03:00
|
|
|
if (__predict_false(pk32)) {
|
2019-06-24 23:29:41 +03:00
|
|
|
if ((l->l_proc->p_flag & PK_32) == 0) {
|
|
|
|
// 32 bit tracer can't trace 64 bit process
|
|
|
|
return EINVAL;
|
|
|
|
}
|
2017-12-09 08:18:45 +03:00
|
|
|
s = sizeof(process_dbreg32);
|
2017-12-28 21:29:45 +03:00
|
|
|
r = (regrfunc_t)process_read_dbregs32;
|
|
|
|
w = (regwfunc_t)process_write_dbregs32;
|
2017-12-07 18:21:34 +03:00
|
|
|
} else
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
s = sizeof(struct dbreg);
|
2017-12-28 21:29:45 +03:00
|
|
|
r = (regrfunc_t)process_read_dbregs;
|
|
|
|
w = (regwfunc_t)process_write_dbregs;
|
Introduce PT_GETDBREGS and PT_SETDBREGS in ptrace(2) on i386 and amd64
This interface is modeled after FreeBSD API with the usage.
This replaced previous watchpoint API. The previous one was introduced
recently in NetBSD-current and remove its spurs without any
backward-compatibility.
Design choices for Debug Register accessors:
- exec() (TRAP_EXEC event) must remove debug registers from LWP
- debug registers are only per-LWP, not per-process globally
- debug registers must not be inherited after (v)forking a process
- debug registers must not be inherited after forking a thread
- a debugger is responsible to set global watchpoints/breakpoints with the
debug registers, to achieve this PTRACE_LWP_CREATE/PTRACE_LWP_EXIT event
monitoring function is designed to be used
- debug register traps must generate SIGTRAP with si_code TRAP_DBREG
- debugger is responsible to retrieve debug register state to distinguish
the exact debug register trap (DR6 is Status Register on x86)
- kernel must not remove debug register traps after triggering a trap event
a debugger is responsible to detach this trap with appropriate PT_SETDBREGS
call (DR7 is Control Register on x86)
- debug registers must not be exposed in mcontext
- userland must not be allowed to set a trap on the kernel
Implementation notes on i386 and amd64:
- the initial state of debug register is retrieved on boot and this value is
stored in a local copy (initdbregs), this value is used to initialize dbreg
context after PT_GETDBREGS
- struct dbregs is stored in pcb as a pointer and by default not initialized
- reserved registers (DR4-DR5, DR9-DR15) are ignored
Further ideas:
- restrict this interface with securelevel
Tested on real hardware i386 (Intel Pentium IV) and amd64 (Intel i7).
This commit enables 390 debug register ATF tests in kernel/arch/x86.
All tests are passing.
This commit does not cover netbsd32 compat code. Currently other interface
PT_GET_SIGINFO/PT_SET_SIGINFO is required in netbsd32 compat code in order to
validate reliably PT_GETDBREGS/PT_SETDBREGS.
This implementation does not cover FreeBSD specific defines in their
<x86/reg.h>: DBREG_DR7_LOCAL_ENABLE, DBREG_DR7_GLOBAL_ENABLE, DBREG_DR7_LEN_1
etc. These values tend to be reinvented by each tracer on its own. GNU
Debugger (GDB) works with NetBSD debug registers after adding this patch:
--- gdb/amd64bsd-nat.c.orig 2016-02-10 03:19:39.000000000 +0000
+++ gdb/amd64bsd-nat.c
@@ -167,6 +167,10 @@ amd64bsd_target (void)
#ifdef HAVE_PT_GETDBREGS
+#ifndef DBREG_DRX
+#define DBREG_DRX(d,x) ((d)->dr[(x)])
+#endif
+
static unsigned long
amd64bsd_dr_get (ptid_t ptid, int regnum)
{
Another reason to stop introducing unpopular defines covering machine
specific register macros is that these value varies across generations of
the same CPU family.
GDB demo:
(gdb) c
Continuing.
Watchpoint 2: traceme
Old value = 0
New value = 16
main (argc=1, argv=0x7f7fff79fe30) at test.c:8
8 printf("traceme=%d\n", traceme);
(Currently the GDB interface is not reliable due to NetBSD support bugs)
Sponsored by <The NetBSD Foundation>
2017-02-23 06:34:22 +03:00
|
|
|
}
|
2017-12-07 18:21:34 +03:00
|
|
|
return proc_regio(l, uio, s, r, w);
|
Introduce PT_GETDBREGS and PT_SETDBREGS in ptrace(2) on i386 and amd64
This interface is modeled after FreeBSD API with the usage.
This replaced previous watchpoint API. The previous one was introduced
recently in NetBSD-current and remove its spurs without any
backward-compatibility.
Design choices for Debug Register accessors:
- exec() (TRAP_EXEC event) must remove debug registers from LWP
- debug registers are only per-LWP, not per-process globally
- debug registers must not be inherited after (v)forking a process
- debug registers must not be inherited after forking a thread
- a debugger is responsible to set global watchpoints/breakpoints with the
debug registers, to achieve this PTRACE_LWP_CREATE/PTRACE_LWP_EXIT event
monitoring function is designed to be used
- debug register traps must generate SIGTRAP with si_code TRAP_DBREG
- debugger is responsible to retrieve debug register state to distinguish
the exact debug register trap (DR6 is Status Register on x86)
- kernel must not remove debug register traps after triggering a trap event
a debugger is responsible to detach this trap with appropriate PT_SETDBREGS
call (DR7 is Control Register on x86)
- debug registers must not be exposed in mcontext
- userland must not be allowed to set a trap on the kernel
Implementation notes on i386 and amd64:
- the initial state of debug register is retrieved on boot and this value is
stored in a local copy (initdbregs), this value is used to initialize dbreg
context after PT_GETDBREGS
- struct dbregs is stored in pcb as a pointer and by default not initialized
- reserved registers (DR4-DR5, DR9-DR15) are ignored
Further ideas:
- restrict this interface with securelevel
Tested on real hardware i386 (Intel Pentium IV) and amd64 (Intel i7).
This commit enables 390 debug register ATF tests in kernel/arch/x86.
All tests are passing.
This commit does not cover netbsd32 compat code. Currently other interface
PT_GET_SIGINFO/PT_SET_SIGINFO is required in netbsd32 compat code in order to
validate reliably PT_GETDBREGS/PT_SETDBREGS.
This implementation does not cover FreeBSD specific defines in their
<x86/reg.h>: DBREG_DR7_LOCAL_ENABLE, DBREG_DR7_GLOBAL_ENABLE, DBREG_DR7_LEN_1
etc. These values tend to be reinvented by each tracer on its own. GNU
Debugger (GDB) works with NetBSD debug registers after adding this patch:
--- gdb/amd64bsd-nat.c.orig 2016-02-10 03:19:39.000000000 +0000
+++ gdb/amd64bsd-nat.c
@@ -167,6 +167,10 @@ amd64bsd_target (void)
#ifdef HAVE_PT_GETDBREGS
+#ifndef DBREG_DRX
+#define DBREG_DRX(d,x) ((d)->dr[(x)])
+#endif
+
static unsigned long
amd64bsd_dr_get (ptid_t ptid, int regnum)
{
Another reason to stop introducing unpopular defines covering machine
specific register macros is that these value varies across generations of
the same CPU family.
GDB demo:
(gdb) c
Continuing.
Watchpoint 2: traceme
Old value = 0
New value = 16
main (argc=1, argv=0x7f7fff79fe30) at test.c:8
8 printf("traceme=%d\n", traceme);
(Currently the GDB interface is not reliable due to NetBSD support bugs)
Sponsored by <The NetBSD Foundation>
2017-02-23 06:34:22 +03:00
|
|
|
#else
|
|
|
|
return EINVAL;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
process_validdbregs(struct lwp *l)
|
|
|
|
{
|
|
|
|
|
|
|
|
#if defined(PT_SETDBREGS) || defined(PT_GETDBREGS)
|
|
|
|
return (l->l_flag & LW_SYSTEM) == 0;
|
|
|
|
#else
|
|
|
|
return 0;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2016-11-02 03:11:59 +03:00
|
|
|
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;
|
|
|
|
|
|
|
|
uio->uio_offset += (off_t)(vaddr_t)(pss.ps_envstr + pss.ps_nenvstr + 1);
|
|
|
|
#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;
|
|
|
|
}
|
|
|
|
#endif /* PTRACE */
|
|
|
|
|
2019-04-26 11:38:25 +03:00
|
|
|
MODULE(MODULE_CLASS_EXEC, ptrace_common, NULL);
|
2016-11-02 03:11:59 +03:00
|
|
|
|
|
|
|
static int
|
|
|
|
ptrace_common_modcmd(modcmd_t cmd, void *arg)
|
|
|
|
{
|
|
|
|
int error;
|
|
|
|
|
|
|
|
switch (cmd) {
|
|
|
|
case MODULE_CMD_INIT:
|
|
|
|
error = ptrace_init();
|
|
|
|
break;
|
|
|
|
case MODULE_CMD_FINI:
|
|
|
|
error = ptrace_fini();
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ptrace_hooks();
|
|
|
|
error = ENOTTY;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return error;
|
|
|
|
}
|