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