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