Home | History | Annotate | Line # | Download | only in kern
      1 /*	$NetBSD: sys_process_lwpstatus.c,v 1.10 2026/08/29 12:46:56 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.10 2026/08/29 12:46:56 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 /*
    146  * Sync with proc_regio in netbsd32_ptrace.c.
    147  */
    148 static int
    149 proc_regio(struct lwp *l, struct uio *uio, void *buf, size_t buflen,
    150     ptrace_regrfunc_t r, ptrace_regwfunc_t w)
    151 {
    152 	int error;
    153 	char *kv;
    154 	size_t kl;
    155 	size_t ks = buflen;
    156 
    157 	if (uio->uio_offset < 0 || uio->uio_offset > (off_t)ks)
    158 		return EINVAL;
    159 
    160 	kv = (char *)buf + uio->uio_offset;
    161 	kl = ks - uio->uio_offset;
    162 
    163 	if (kl > uio->uio_resid)
    164 		kl = uio->uio_resid;
    165 
    166 	/*
    167 	 * Read all the registers first, even if the caller is writing
    168 	 * to them, because the caller might be writing to only a part
    169 	 * of them.  But if the process is not stopped, refuse.  Must
    170 	 * hold the lwp lock to verify l->l_stat remains LSSTOP as we
    171 	 * read.
    172 	 */
    173 	lwp_lock(l);
    174 	if (l->l_stat != LSSTOP)
    175 		error = EBUSY;
    176 	else
    177 		error = (*r)(l, buf, &ks);
    178 	lwp_unlock(l);
    179 	if (error)
    180 		goto out;
    181 
    182 	/*
    183 	 * Copy (part of) the register content to or from the user's
    184 	 * I/O space.
    185 	 */
    186 	error = uiomove(kv, kl, uio);
    187 	if (error)
    188 		goto out;
    189 
    190 	/*
    191 	 * If we're writing registers (or part of the registers), write
    192 	 * them back.  Again, if the process is not stopped, refuse.
    193 	 * Must hold the lwp lock to verify l->l_stat remains LSSTOP as
    194 	 * we write.
    195 	 *
    196 	 * Note that there is a potential race condition or ABA problem
    197 	 * here: in the time between the lwp_unlock above and the
    198 	 * lwp_lock below, l->l_stat could transition from LSSTOP to
    199 	 * something else back to LSSTOP again, and the register
    200 	 * content could be scrambled.  We should really fail with
    201 	 * EBUSY if that happens at all, but there's no easy way to
    202 	 * detect that case, and we certainly can't hold the lwp locked
    203 	 * across uiomove(9) which might wait indefinitely for swap
    204 	 * I/O.
    205 	 */
    206 	if (uio->uio_rw == UIO_WRITE) {
    207 		lwp_lock(l);
    208 		if (l->l_stat != LSSTOP)
    209 			error = EBUSY;
    210 		else
    211 			error = (*w)(l, buf, ks);
    212 		lwp_unlock(l);
    213 		if (error)
    214 			goto out;
    215 	}
    216 
    217 out:	uio->uio_offset = 0;
    218 	return error;
    219 }
    220 #endif
    221 
    222 #if defined(PT_REGS)
    223 #ifdef COMPAT_NETBSD32
    224 static int
    225 process_read_regs32_wrapper(struct lwp *l, void *buf, size_t *sizep)
    226 {
    227 	process_reg32 *r32 = buf;
    228 
    229 	KASSERT(*sizep == sizeof(*r32));
    230 	return process_read_regs32(l, buf);
    231 }
    232 
    233 static int
    234 process_write_regs32_wrapper(struct lwp *l, void *buf, size_t size)
    235 {
    236 	process_reg32 *r32 = buf;
    237 
    238 	KASSERT(size == sizeof(*r32));
    239 	return process_write_regs32(l, buf);
    240 }
    241 #endif	/* COMPAT_NETBSD32 */
    242 
    243 static int
    244 process_read_regs_wrapper(struct lwp *l, void *buf, size_t *sizep)
    245 {
    246 	struct reg *r = buf;
    247 
    248 	KASSERT(*sizep == sizeof(*r));
    249 	return process_read_regs(l, buf);
    250 }
    251 
    252 static int
    253 process_write_regs_wrapper(struct lwp *l, void *buf, size_t size)
    254 {
    255 	struct reg *r = buf;
    256 
    257 	KASSERT(size == sizeof(*r));
    258 	return process_write_regs(l, buf);
    259 }
    260 #endif	/* PT_REGS */
    261 
    262 int
    263 process_doregs(struct lwp *curl /*tracer*/,
    264     struct lwp *l /*traced*/,
    265     struct uio *uio)
    266 {
    267 #if defined(PT_REGS)
    268 #ifdef COMPAT_NETBSD32
    269 	const bool pk32 = (curl->l_proc->p_flag & PK_32) != 0;
    270 
    271 	if (__predict_false(pk32)) {
    272 		process_reg32 reg32 PTRACE_REGS_ALIGN;
    273 
    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 		return proc_regio(l, uio, &reg32, sizeof(reg32),
    279 		    process_read_regs32_wrapper,
    280 		    process_write_regs32_wrapper);
    281 	} else
    282 #endif
    283 	{
    284 		struct reg reg PTRACE_REGS_ALIGN;
    285 
    286 		return proc_regio(l, uio, &reg, sizeof(reg),
    287 		    process_read_regs_wrapper,
    288 		    process_write_regs_wrapper);
    289 	}
    290 #else
    291 	return EINVAL;
    292 #endif
    293 }
    294 
    295 #if defined(PT_FPREGS)
    296 #ifdef COMPAT_NETBSD32
    297 static int
    298 process_read_fpregs32_wrapper(struct lwp *l, void *buf, size_t *sizep)
    299 {
    300 	process_fpreg32 *r32 = buf;
    301 
    302 	KASSERT(*sizep == sizeof(*r32));
    303 	return process_read_fpregs32(l, buf, sizep);
    304 }
    305 
    306 static int
    307 process_write_fpregs32_wrapper(struct lwp *l, void *buf, size_t size)
    308 {
    309 	process_fpreg32 *r32 = buf;
    310 
    311 	KASSERT(size == sizeof(*r32));
    312 	return process_write_fpregs32(l, buf, size);
    313 }
    314 #endif	/* COMPAT_NETBSD32 */
    315 
    316 static int
    317 process_read_fpregs_wrapper(struct lwp *l, void *buf, size_t *sizep)
    318 {
    319 	struct fpreg *r = buf;
    320 
    321 	KASSERT(*sizep == sizeof(*r));
    322 	return process_read_fpregs(l, buf, sizep);
    323 }
    324 
    325 static int
    326 process_write_fpregs_wrapper(struct lwp *l, void *buf, size_t size)
    327 {
    328 	struct fpreg *r = buf;
    329 
    330 	KASSERT(size == sizeof(*r));
    331 	return process_write_fpregs(l, buf, size);
    332 }
    333 #endif	/* PT_FPREGS */
    334 
    335 int
    336 process_dofpregs(struct lwp *curl /*tracer*/,
    337     struct lwp *l /*traced*/,
    338     struct uio *uio)
    339 {
    340 #if defined(PT_FPREGS)
    341 #ifdef COMPAT_NETBSD32
    342 	const bool pk32 = (curl->l_proc->p_flag & PK_32) != 0;
    343 
    344 	if (__predict_false(pk32)) {
    345 		process_fpreg32 fpreg32 PTRACE_REGS_ALIGN;
    346 
    347 		if ((l->l_proc->p_flag & PK_32) == 0) {
    348 			// 32 bit tracer can't trace 64 bit process
    349 			return EINVAL;
    350 		}
    351 		return proc_regio(l, uio, &fpreg32, sizeof(fpreg32),
    352 		    process_read_fpregs32_wrapper,
    353 		    process_write_fpregs32_wrapper);
    354 	} else
    355 #endif
    356 	{
    357 		struct fpreg fpreg PTRACE_REGS_ALIGN;
    358 
    359 		return proc_regio(l, uio, &fpreg, sizeof(fpreg),
    360 		    process_read_fpregs_wrapper,
    361 		    process_write_fpregs_wrapper);
    362 	}
    363 #else
    364 	return EINVAL;
    365 #endif
    366 }
    367 
    368 #if defined(PT_DBREGS)
    369 #ifdef COMPAT_NETBSD32
    370 static int
    371 process_read_dbregs32_wrapper(struct lwp *l, void *buf, size_t *sizep)
    372 {
    373 	process_dbreg32 *r32 = buf;
    374 
    375 	KASSERT(*sizep == sizeof(*r32));
    376 	return process_read_dbregs32(l, buf, sizep);
    377 }
    378 
    379 static int
    380 process_write_dbregs32_wrapper(struct lwp *l, void *buf, size_t size)
    381 {
    382 	process_dbreg32 *r32 = buf;
    383 
    384 	KASSERT(size == sizeof(*r32));
    385 	return process_write_dbregs32(l, buf, size);
    386 }
    387 #endif	/* COMPAT_NETBSD32 */
    388 
    389 static int
    390 process_read_dbregs_wrapper(struct lwp *l, void *buf, size_t *sizep)
    391 {
    392 	struct dbreg *r = buf;
    393 
    394 	KASSERT(*sizep == sizeof(*r));
    395 	return process_read_dbregs(l, buf, sizep);
    396 }
    397 
    398 static int
    399 process_write_dbregs_wrapper(struct lwp *l, void *buf, size_t size)
    400 {
    401 	struct dbreg *r = buf;
    402 
    403 	KASSERT(size == sizeof(*r));
    404 	return process_write_dbregs(l, buf, size);
    405 }
    406 #endif	/* PT_DBREGS */
    407 
    408 int
    409 process_dodbregs(struct lwp *curl /*tracer*/,
    410     struct lwp *l /*traced*/,
    411     struct uio *uio)
    412 {
    413 #if defined(PT_DBREGS)
    414 	KASSERT(rw_lock_held(&l->l_proc->p_reflock));
    415 	process_alloc_dbregs(l);
    416 
    417 #ifdef COMPAT_NETBSD32
    418 	const bool pk32 = (curl->l_proc->p_flag & PK_32) != 0;
    419 
    420 	if (__predict_false(pk32)) {
    421 		process_dbreg32 dbreg32 PTRACE_REGS_ALIGN;
    422 
    423 		if ((l->l_proc->p_flag & PK_32) == 0) {
    424 			// 32 bit tracer can't trace 64 bit process
    425 			return EINVAL;
    426 		}
    427 		return proc_regio(l, uio, &dbreg32, sizeof(dbreg32),
    428 		    process_read_dbregs32_wrapper,
    429 		    process_write_dbregs32_wrapper);
    430 	} else
    431 #endif
    432 	{
    433 		struct dbreg dbreg PTRACE_REGS_ALIGN;
    434 
    435 		return proc_regio(l, uio, &dbreg, sizeof(dbreg),
    436 		    process_read_dbregs_wrapper,
    437 		    process_write_dbregs_wrapper);
    438 	}
    439 #else
    440 	return EINVAL;
    441 #endif
    442 }
    443