Home | History | Annotate | Line # | Download | only in kern
      1 /*	$NetBSD: sys_process_lwpstatus.c,v 1.5 2025/01/11 19:42:04 christos Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2019 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     17  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     18  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     26  * POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 
     29 #include <sys/cdefs.h>
     30 __KERNEL_RCSID(0, "$NetBSD: sys_process_lwpstatus.c,v 1.5 2025/01/11 19:42:04 christos Exp $");
     31 
     32 #ifdef _KERNEL_OPT
     33 #include "opt_ptrace.h"
     34 #include "opt_ktrace.h"
     35 #include "opt_pax.h"
     36 #include "opt_compat_netbsd32.h"
     37 #endif
     38 
     39 #if defined(__HAVE_COMPAT_NETBSD32) && !defined(COMPAT_NETBSD32) \
     40     && !defined(_RUMPKERNEL)
     41 #define COMPAT_NETBSD32
     42 #endif
     43 
     44 #include <sys/param.h>
     45 #include <sys/systm.h>
     46 #include <sys/errno.h>
     47 #include <sys/lwp.h>
     48 #include <sys/proc.h>
     49 #include <sys/ptrace.h>
     50 
     51 #ifndef PTRACE_REGS_ALIGN
     52 #define PTRACE_REGS_ALIGN /* nothing */
     53 #endif
     54 
     55 void
     56 ptrace_read_lwpstatus(struct lwp *l, struct ptrace_lwpstatus *pls)
     57 {
     58 
     59 	pls->pl_lwpid = l->l_lid;
     60 	memcpy(&pls->pl_sigmask, &l->l_sigmask, sizeof(pls->pl_sigmask));
     61 	memcpy(&pls->pl_sigpend, &l->l_sigpend.sp_set, sizeof(pls->pl_sigpend));
     62 
     63 	if (l->l_name == NULL)
     64 		memset(&pls->pl_name, 0, PL_LNAMELEN);
     65 	else {
     66 		KASSERT(strlen(l->l_name) < PL_LNAMELEN);
     67 		strncpy(pls->pl_name, l->l_name, PL_LNAMELEN);
     68 	}
     69 
     70 #ifdef PTRACE_LWP_GETPRIVATE
     71 	pls->pl_private = (void *)(intptr_t)PTRACE_LWP_GETPRIVATE(l);
     72 #else
     73 	pls->pl_private = l->l_private;
     74 #endif
     75 }
     76 
     77 void
     78 process_read_lwpstatus(struct lwp *l, struct ptrace_lwpstatus *pls)
     79 {
     80 
     81 	ptrace_read_lwpstatus(l, pls);
     82 }
     83 
     84 int
     85 ptrace_update_lwp(struct proc *t, struct lwp **lt, lwpid_t lid)
     86 {
     87 	if (lid == 0 || lid == (*lt)->l_lid || t->p_nlwps == 1)
     88 		return 0;
     89 
     90 	mutex_enter(t->p_lock);
     91 	lwp_delref2(*lt);
     92 
     93 	*lt = lwp_find(t, lid);
     94 	if (*lt == NULL) {
     95 		mutex_exit(t->p_lock);
     96 		return ESRCH;
     97 	}
     98 
     99 	if ((*lt)->l_flag & LW_SYSTEM) {
    100 		mutex_exit(t->p_lock);
    101 		*lt = NULL;
    102 		return EINVAL;
    103 	}
    104 
    105 	lwp_addref(*lt);
    106 	mutex_exit(t->p_lock);
    107 
    108 	return 0;
    109 }
    110 
    111 int
    112 process_validfpregs(struct lwp *l)
    113 {
    114 
    115 #if defined(PT_FPREGS)
    116 	return (l->l_flag & LW_SYSTEM) == 0;
    117 #else
    118 	return 0;
    119 #endif
    120 }
    121 
    122 int
    123 process_validregs(struct lwp *l)
    124 {
    125 
    126 #if defined(PT_REGS)
    127 	return (l->l_flag & LW_SYSTEM) == 0;
    128 #else
    129 	return 0;
    130 #endif
    131 }
    132 
    133 int
    134 process_validdbregs(struct lwp *l)
    135 {
    136 
    137 #if defined(PT_DBREGS)
    138 	return (l->l_flag & LW_SYSTEM) == 0;
    139 #else
    140 	return 0;
    141 #endif
    142 }
    143 
    144 #ifdef PT_REGISTERS
    145 static int
    146 proc_regio(struct lwp *l, struct uio *uio, size_t ks, ptrace_regrfunc_t r,
    147     ptrace_regwfunc_t w)
    148 {
    149 	char buf[1024] PTRACE_REGS_ALIGN;
    150 	int error;
    151 	char *kv;
    152 	size_t kl;
    153 
    154 	if (ks > sizeof(buf))
    155 		return E2BIG;
    156 
    157 	if (uio->uio_offset < 0 || uio->uio_offset > (off_t)ks)
    158 		return EINVAL;
    159 
    160 	kv = buf + uio->uio_offset;
    161 	kl = ks - uio->uio_offset;
    162 
    163 	if (kl > uio->uio_resid)
    164 		kl = uio->uio_resid;
    165 
    166 	error = (*r)(l, buf, &ks);
    167 	if (error == 0)
    168 		error = uiomove(kv, kl, uio);
    169 	if (error == 0 && uio->uio_rw == UIO_WRITE) {
    170 		if (l->l_stat != LSSTOP)
    171 			error = EBUSY;
    172 		else
    173 			error = (*w)(l, buf, ks);
    174 	}
    175 
    176 	uio->uio_offset = 0;
    177 	return error;
    178 }
    179 #endif
    180 
    181 int
    182 process_doregs(struct lwp *curl /*tracer*/,
    183     struct lwp *l /*traced*/,
    184     struct uio *uio)
    185 {
    186 #if defined(PT_REGS)
    187 	size_t s;
    188 	ptrace_regrfunc_t r;
    189 	ptrace_regwfunc_t w;
    190 
    191 #ifdef COMPAT_NETBSD32
    192 	const bool pk32 = (curl->l_proc->p_flag & PK_32) != 0;
    193 
    194 	if (__predict_false(pk32)) {
    195 		if ((l->l_proc->p_flag & PK_32) == 0) {
    196 			// 32 bit tracer can't trace 64 bit process
    197 			return EINVAL;
    198 		}
    199 		s = sizeof(process_reg32);
    200 		r = __FPTRCAST(ptrace_regrfunc_t, process_read_regs32);
    201 		w = __FPTRCAST(ptrace_regwfunc_t, process_write_regs32);
    202 	} else
    203 #endif
    204 	{
    205 		s = sizeof(struct reg);
    206 		r = __FPTRCAST(ptrace_regrfunc_t, process_read_regs);
    207 		w = __FPTRCAST(ptrace_regwfunc_t, process_write_regs);
    208 	}
    209 	return proc_regio(l, uio, s, r, w);
    210 #else
    211 	return EINVAL;
    212 #endif
    213 }
    214 
    215 int
    216 process_dofpregs(struct lwp *curl /*tracer*/,
    217     struct lwp *l /*traced*/,
    218     struct uio *uio)
    219 {
    220 #if defined(PT_FPREGS)
    221 	size_t s;
    222 	ptrace_regrfunc_t r;
    223 	ptrace_regwfunc_t w;
    224 
    225 #ifdef COMPAT_NETBSD32
    226 	const bool pk32 = (curl->l_proc->p_flag & PK_32) != 0;
    227 
    228 	if (__predict_false(pk32)) {
    229 		if ((l->l_proc->p_flag & PK_32) == 0) {
    230 			// 32 bit tracer can't trace 64 bit process
    231 			return EINVAL;
    232 		}
    233 		s = sizeof(process_fpreg32);
    234 		r = (ptrace_regrfunc_t)process_read_fpregs32;
    235 		w = (ptrace_regwfunc_t)process_write_fpregs32;
    236 	} else
    237 #endif
    238 	{
    239 		s = sizeof(struct fpreg);
    240 		r = (ptrace_regrfunc_t)process_read_fpregs;
    241 		w = (ptrace_regwfunc_t)process_write_fpregs;
    242 	}
    243 	return proc_regio(l, uio, s, r, w);
    244 #else
    245 	return EINVAL;
    246 #endif
    247 }
    248 
    249 
    250 int
    251 process_dodbregs(struct lwp *curl /*tracer*/,
    252     struct lwp *l /*traced*/,
    253     struct uio *uio)
    254 {
    255 #if defined(PT_DBREGS)
    256 	size_t s;
    257 	ptrace_regrfunc_t r;
    258 	ptrace_regwfunc_t w;
    259 
    260 #ifdef COMPAT_NETBSD32
    261 	const bool pk32 = (curl->l_proc->p_flag & PK_32) != 0;
    262 
    263 	if (__predict_false(pk32)) {
    264 		if ((l->l_proc->p_flag & PK_32) == 0) {
    265 			// 32 bit tracer can't trace 64 bit process
    266 			return EINVAL;
    267 		}
    268 		s = sizeof(process_dbreg32);
    269 		r = (ptrace_regrfunc_t)process_read_dbregs32;
    270 		w = (ptrace_regwfunc_t)process_write_dbregs32;
    271 	} else
    272 #endif
    273 	{
    274 		s = sizeof(struct dbreg);
    275 		r = (ptrace_regrfunc_t)process_read_dbregs;
    276 		w = (ptrace_regwfunc_t)process_write_dbregs;
    277 	}
    278 	return proc_regio(l, uio, s, r, w);
    279 #else
    280 	return EINVAL;
    281 #endif
    282 }
    283