Home | History | Annotate | Line # | Download | only in kern
      1 /*	$NetBSD: sys_process_lwpstatus.c,v 1.8 2026/08/20 16:20:54 riastradh 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.8 2026/08/20 16:20:54 riastradh 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 	/*
    155 	 * Sanity-check the transfer and find the window inside the
    156 	 * register content that the user is doing I/O on.
    157 	 */
    158 	if (ks > sizeof(buf))
    159 		return E2BIG;
    160 
    161 	if (uio->uio_offset < 0 || uio->uio_offset > (off_t)ks)
    162 		return EINVAL;
    163 
    164 	kv = buf + uio->uio_offset;
    165 	kl = ks - uio->uio_offset;
    166 
    167 	if (kl > uio->uio_resid)
    168 		kl = uio->uio_resid;
    169 
    170 	/*
    171 	 * Read all the registers first, even if the caller is writing
    172 	 * to them, because the caller might be writing to only a part
    173 	 * of them.  But if the process is not stopped, refuse.  Must
    174 	 * hold the lwp lock to verify l->l_stat remains LSSTOP as we
    175 	 * read.
    176 	 */
    177 	lwp_lock(l);
    178 	if (l->l_stat != LSSTOP)
    179 		error = EBUSY;
    180 	else
    181 		error = (*r)(l, buf, &ks);
    182 	lwp_unlock(l);
    183 	if (error)
    184 		goto out;
    185 
    186 	/*
    187 	 * Copy (part of) the register content to or from the user's
    188 	 * I/O space.
    189 	 */
    190 	error = uiomove(kv, kl, uio);
    191 	if (error)
    192 		goto out;
    193 
    194 	/*
    195 	 * If we're writing registers (or part of the registers), write
    196 	 * them back.  Again, if the process is not stopped, refuse.
    197 	 * Must hold the lwp lock to verify l->l_stat remains LSSTOP as
    198 	 * we write.
    199 	 *
    200 	 * Note that there is a potential race condition or ABA problem
    201 	 * here: in the time between the lwp_unlock above and the
    202 	 * lwp_lock below, l->l_stat could transition from LSSTOP to
    203 	 * something else back to LSSTOP again, and the register
    204 	 * content could be scrambled.  We should really fail with
    205 	 * EBUSY if that happens at all, but there's no easy way to
    206 	 * detect that case, and we certainly can't hold the lwp locked
    207 	 * across uiomove(9) which might wait indefinitely for swap
    208 	 * I/O.
    209 	 */
    210 	if (uio->uio_rw == UIO_WRITE) {
    211 		lwp_lock(l);
    212 		if (l->l_stat != LSSTOP)
    213 			error = EBUSY;
    214 		else
    215 			error = (*w)(l, buf, ks);
    216 		lwp_unlock(l);
    217 		if (error)
    218 			goto out;
    219 	}
    220 
    221 out:	uio->uio_offset = 0;
    222 	return error;
    223 }
    224 #endif
    225 
    226 int
    227 process_doregs(struct lwp *curl /*tracer*/,
    228     struct lwp *l /*traced*/,
    229     struct uio *uio)
    230 {
    231 #if defined(PT_REGS)
    232 	size_t s;
    233 	ptrace_regrfunc_t r;
    234 	ptrace_regwfunc_t w;
    235 
    236 #ifdef COMPAT_NETBSD32
    237 	const bool pk32 = (curl->l_proc->p_flag & PK_32) != 0;
    238 
    239 	if (__predict_false(pk32)) {
    240 		if ((l->l_proc->p_flag & PK_32) == 0) {
    241 			// 32 bit tracer can't trace 64 bit process
    242 			return EINVAL;
    243 		}
    244 		s = sizeof(process_reg32);
    245 		r = __FPTRCAST(ptrace_regrfunc_t, process_read_regs32);
    246 		w = __FPTRCAST(ptrace_regwfunc_t, process_write_regs32);
    247 	} else
    248 #endif
    249 	{
    250 		s = sizeof(struct reg);
    251 		r = __FPTRCAST(ptrace_regrfunc_t, process_read_regs);
    252 		w = __FPTRCAST(ptrace_regwfunc_t, process_write_regs);
    253 	}
    254 	return proc_regio(l, uio, s, r, w);
    255 #else
    256 	return EINVAL;
    257 #endif
    258 }
    259 
    260 int
    261 process_dofpregs(struct lwp *curl /*tracer*/,
    262     struct lwp *l /*traced*/,
    263     struct uio *uio)
    264 {
    265 #if defined(PT_FPREGS)
    266 	size_t s;
    267 	ptrace_regrfunc_t r;
    268 	ptrace_regwfunc_t w;
    269 
    270 #ifdef COMPAT_NETBSD32
    271 	const bool pk32 = (curl->l_proc->p_flag & PK_32) != 0;
    272 
    273 	if (__predict_false(pk32)) {
    274 		if ((l->l_proc->p_flag & PK_32) == 0) {
    275 			// 32 bit tracer can't trace 64 bit process
    276 			return EINVAL;
    277 		}
    278 		s = sizeof(process_fpreg32);
    279 		r = (ptrace_regrfunc_t)process_read_fpregs32;
    280 		w = (ptrace_regwfunc_t)process_write_fpregs32;
    281 	} else
    282 #endif
    283 	{
    284 		s = sizeof(struct fpreg);
    285 		r = (ptrace_regrfunc_t)process_read_fpregs;
    286 		w = (ptrace_regwfunc_t)process_write_fpregs;
    287 	}
    288 	return proc_regio(l, uio, s, r, w);
    289 #else
    290 	return EINVAL;
    291 #endif
    292 }
    293 
    294 
    295 int
    296 process_dodbregs(struct lwp *curl /*tracer*/,
    297     struct lwp *l /*traced*/,
    298     struct uio *uio)
    299 {
    300 #if defined(PT_DBREGS)
    301 	size_t s;
    302 	ptrace_regrfunc_t r;
    303 	ptrace_regwfunc_t w;
    304 
    305 	KASSERT(rw_lock_held(&l->l_proc->p_reflock));
    306 	process_alloc_dbregs(l);
    307 
    308 #ifdef COMPAT_NETBSD32
    309 	const bool pk32 = (curl->l_proc->p_flag & PK_32) != 0;
    310 
    311 	if (__predict_false(pk32)) {
    312 		if ((l->l_proc->p_flag & PK_32) == 0) {
    313 			// 32 bit tracer can't trace 64 bit process
    314 			return EINVAL;
    315 		}
    316 		s = sizeof(process_dbreg32);
    317 		r = (ptrace_regrfunc_t)process_read_dbregs32;
    318 		w = (ptrace_regwfunc_t)process_write_dbregs32;
    319 	} else
    320 #endif
    321 	{
    322 		s = sizeof(struct dbreg);
    323 		r = (ptrace_regrfunc_t)process_read_dbregs;
    324 		w = (ptrace_regwfunc_t)process_write_dbregs;
    325 	}
    326 	return proc_regio(l, uio, s, r, w);
    327 #else
    328 	return EINVAL;
    329 #endif
    330 }
    331