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, ®32, 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, ®, 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