1 /* $NetBSD: netbsd32_ptrace.c,v 1.11 2026/09/02 16:39:41 riastradh Exp $ */ 2 3 /* 4 * Copyright (c) 2016 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Nick Hudson 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 __KERNEL_RCSID(0, "$NetBSD: netbsd32_ptrace.c,v 1.11 2026/09/02 16:39:41 riastradh Exp $"); 34 35 #if defined(_KERNEL_OPT) 36 #include "opt_ptrace.h" 37 #include "opt_compat_netbsd.h" 38 #endif 39 40 #include <sys/param.h> 41 #include <sys/module.h> 42 #include <sys/ptrace.h> 43 #include <sys/syscallvar.h> 44 45 #include <compat/netbsd32/netbsd32.h> 46 #include <compat/netbsd32/netbsd32_syscall.h> 47 #include <compat/netbsd32/netbsd32_syscallargs.h> 48 #include <compat/netbsd32/netbsd32_conv.h> 49 50 #ifndef PTRACE_TRANSLATE_REQUEST32 51 #define PTRACE_TRANSLATE_REQUEST32(x) x 52 #endif 53 54 static void 55 netbsd32_lwpstatus_to_lwpstatus32(struct netbsd32_ptrace_lwpstatus *pls32, 56 const struct ptrace_lwpstatus *pls) 57 { 58 memset(pls32, 0, sizeof(*pls32)); 59 pls32->pl_lwpid = pls->pl_lwpid; 60 pls32->pl_sigpend = pls->pl_sigpend; 61 pls32->pl_sigmask = pls->pl_sigmask; 62 memcpy(&pls32->pl_name, &pls->pl_name, PL_LNAMELEN); 63 NETBSD32PTR32(pls32->pl_private, pls->pl_private); 64 } 65 66 void 67 netbsd32_read_lwpstatus(struct lwp *l, struct netbsd32_ptrace_lwpstatus *pls32) 68 { 69 struct ptrace_lwpstatus pls; 70 71 process_read_lwpstatus(l, &pls); 72 73 netbsd32_lwpstatus_to_lwpstatus32(pls32, &pls); 74 } 75 76 /* 77 * PTRACE methods 78 */ 79 80 static int 81 netbsd32_copyin_piod(struct ptrace_io_desc *piod, const void *addr, size_t len) 82 { 83 struct netbsd32_ptrace_io_desc piod32; 84 85 if (len != 0 && sizeof(piod32) != len) 86 return EINVAL; 87 88 int error = copyin(addr, &piod32, sizeof(piod32)); 89 if (error) 90 return error; 91 piod->piod_op = piod32.piod_op; 92 piod->piod_offs = NETBSD32PTR64(piod32.piod_offs); 93 piod->piod_addr = NETBSD32PTR64(piod32.piod_addr); 94 piod->piod_len = (size_t)piod32.piod_len; 95 96 return 0; 97 } 98 99 static int 100 netbsd32_copyout_piod(const struct ptrace_io_desc *piod, void *addr, size_t len) 101 { 102 struct netbsd32_ptrace_io_desc piod32; 103 104 if (len != 0 && sizeof(piod32) != len) 105 return EINVAL; 106 107 memset(&piod32, 0, sizeof(piod32)); 108 piod32.piod_op = piod->piod_op; 109 NETBSD32PTR32(piod32.piod_offs, piod->piod_offs); 110 NETBSD32PTR32(piod32.piod_addr, piod->piod_addr); 111 piod32.piod_len = (netbsd32_size_t)piod->piod_len; 112 return copyout(&piod32, addr, sizeof(piod32)); 113 } 114 115 static int 116 netbsd32_copyin_siginfo(struct ptrace_siginfo *psi, const void *addr, size_t len) 117 { 118 struct netbsd32_ptrace_siginfo psi32; 119 120 if (sizeof(psi32) != len) 121 return EINVAL; 122 123 int error = copyin(addr, &psi32, sizeof(psi32)); 124 if (error) 125 return error; 126 psi->psi_lwpid = psi32.psi_lwpid; 127 netbsd32_si32_to_si(&psi->psi_siginfo, &psi32.psi_siginfo); 128 return 0; 129 } 130 131 static int 132 netbsd32_copyout_siginfo(const struct ptrace_siginfo *psi, void *addr, size_t len) 133 { 134 struct netbsd32_ptrace_siginfo psi32; 135 136 if (sizeof(psi32) != len) 137 return EINVAL; 138 139 memset(&psi32, 0, sizeof(psi32)); 140 psi32.psi_lwpid = psi->psi_lwpid; 141 netbsd32_si_to_si32(&psi32.psi_siginfo, &psi->psi_siginfo); 142 return copyout(&psi32, addr, sizeof(psi32)); 143 } 144 145 static int 146 netbsd32_copyout_lwpstatus(const struct ptrace_lwpstatus *pls, void *addr, size_t len) 147 { 148 struct netbsd32_ptrace_lwpstatus pls32; 149 150 if (len > sizeof(pls32)) 151 return EINVAL; 152 153 netbsd32_lwpstatus_to_lwpstatus32(&pls32, pls); 154 155 return copyout(&pls32, addr, MIN(len, sizeof(pls32))); 156 } 157 158 /* 159 * Sync with proc_regio in sys_process_lwpstatus.c. 160 */ 161 static int 162 netbsd32_proc_regio(struct lwp *l, struct uio *uio, void *buf, size_t buflen, 163 ptrace_regrfunc_t r, ptrace_regwfunc_t w) 164 { 165 int error; 166 char *kv; 167 size_t kl; 168 size_t ks = buflen; 169 170 if (uio->uio_offset < 0 || uio->uio_offset > (off_t)ks) 171 return EINVAL; 172 173 kv = (char *)buf + uio->uio_offset; 174 kl = ks - uio->uio_offset; 175 176 if (kl > uio->uio_resid) 177 kl = uio->uio_resid; 178 179 /* 180 * Read all the registers first, even if the caller is writing 181 * to them, because the caller might be writing to only a part 182 * of them. But if the process is not stopped, refuse. Must 183 * hold the lwp lock to verify l->l_stat remains LSSTOP as we 184 * read. 185 */ 186 lwp_lock(l); 187 if (l->l_stat != LSSTOP) 188 error = EBUSY; 189 else 190 error = (*r)(l, buf, &ks); 191 lwp_unlock(l); 192 if (error) 193 goto out; 194 195 /* 196 * Copy (part of) the register content to or from the user's 197 * I/O space. 198 */ 199 error = uiomove(kv, kl, uio); 200 if (error) 201 goto out; 202 203 /* 204 * If we're writing registers (or part of the registers), write 205 * them back. Again, if the process is not stopped, refuse. 206 * Must hold the lwp lock to verify l->l_stat remains LSSTOP as 207 * we write. 208 * 209 * Note that there is a potential race condition or ABA problem 210 * here: in the time between the lwp_unlock above and the 211 * lwp_lock below, l->l_stat could transition from LSSTOP to 212 * something else back to LSSTOP again, and the register 213 * content could be scrambled. We should really fail with 214 * EBUSY if that happens at all, but there's no easy way to 215 * detect that case, and we certainly can't hold the lwp locked 216 * across uiomove(9) which might wait indefinitely for swap 217 * I/O. 218 */ 219 if (uio->uio_rw == UIO_WRITE) { 220 lwp_lock(l); 221 if (l->l_stat != LSSTOP) 222 error = EBUSY; 223 else 224 error = (*w)(l, buf, ks); 225 lwp_unlock(l); 226 if (error) 227 goto out; 228 } 229 230 out: uio->uio_offset = 0; 231 return error; 232 } 233 234 #if defined(PT_GETREGS) || defined(PT_SETREGS) 235 static int 236 process_read_regs32_wrapper(struct lwp *l, void *buf, size_t *sizep) 237 { 238 process_reg32 *reg32 = buf; 239 240 KASSERT(*sizep == sizeof(*reg32)); 241 return process_read_regs32(l, reg32); 242 } 243 244 static int 245 process_write_regs32_wrapper(struct lwp *l, void *buf, size_t size) 246 { 247 process_reg32 *reg32 = buf; 248 249 KASSERT(size == sizeof(*reg32)); 250 return process_write_regs32(l, reg32); 251 } 252 #endif /* defined(PT_GETREGS) || defined(PT_SETREGS) */ 253 254 static int 255 netbsd32_doregs(struct lwp *curl /*tracer*/, 256 struct lwp *l /*traced*/, 257 struct uio *uio) 258 { 259 #if defined(PT_GETREGS) || defined(PT_SETREGS) 260 process_reg32 r32; 261 262 return netbsd32_proc_regio(l, uio, &r32, sizeof(r32), 263 process_read_regs32_wrapper, 264 process_write_regs32_wrapper); 265 #else 266 return EINVAL; 267 #endif 268 } 269 270 #if defined(PT_GETFPREGS) || defined(PT_SETFPREGS) 271 static int 272 process_read_fpregs32_wrapper(struct lwp *l, void *buf, size_t *sizep) 273 { 274 process_fpreg32 *fpreg32 = buf; 275 276 KASSERT(*sizep == sizeof(*fpreg32)); 277 return process_read_fpregs32(l, fpreg32, sizep); 278 } 279 280 static int 281 process_write_fpregs32_wrapper(struct lwp *l, void *buf, size_t size) 282 { 283 process_fpreg32 *fpreg32 = buf; 284 285 KASSERT(size == sizeof(*fpreg32)); 286 return process_write_fpregs32(l, fpreg32, size); 287 } 288 #endif /* defined(PT_GETFPREGS) || defined(PT_SETFPREGS) */ 289 290 static int 291 netbsd32_dofpregs(struct lwp *curl /*tracer*/, 292 struct lwp *l /*traced*/, 293 struct uio *uio) 294 { 295 #if defined(PT_GETFPREGS) || defined(PT_SETFPREGS) 296 process_fpreg32 r32; 297 298 return netbsd32_proc_regio(l, uio, &r32, sizeof(r32), 299 process_read_fpregs32_wrapper, 300 process_write_fpregs32_wrapper); 301 #else 302 return EINVAL; 303 #endif 304 } 305 306 #if defined(PT_GETDBREGS) || defined(PT_SETDBREGS) 307 static int 308 process_read_dbregs32_wrapper(struct lwp *l, void *buf, size_t *sizep) 309 { 310 process_dbreg32 *dbreg32 = buf; 311 312 KASSERT(*sizep == sizeof(*dbreg32)); 313 return process_read_dbregs32(l, dbreg32, sizep); 314 } 315 316 static int 317 process_write_dbregs32_wrapper(struct lwp *l, void *buf, size_t size) 318 { 319 process_dbreg32 *dbreg32 = buf; 320 321 KASSERT(size == sizeof(*dbreg32)); 322 return process_write_dbregs32(l, dbreg32, size); 323 } 324 #endif /* defined(PT_GETDBREGS) || defined(PT_SETDBREGS) */ 325 326 static int 327 netbsd32_dodbregs(struct lwp *curl /*tracer*/, 328 struct lwp *l /*traced*/, 329 struct uio *uio) 330 { 331 #if defined(PT_GETDBREGS) || defined(PT_SETDBREGS) 332 process_dbreg32 r32; 333 334 KASSERT(rw_lock_held(&l->l_proc->p_reflock)); 335 process_alloc_dbregs(l); 336 337 return netbsd32_proc_regio(l, uio, &r32, sizeof(r32), 338 process_read_dbregs32_wrapper, 339 process_write_dbregs32_wrapper); 340 #else 341 return EINVAL; 342 #endif 343 } 344 345 static struct ptrace_methods netbsd32_ptm = { 346 .ptm_copyin_piod = netbsd32_copyin_piod, 347 .ptm_copyout_piod = netbsd32_copyout_piod, 348 .ptm_copyin_siginfo = netbsd32_copyin_siginfo, 349 .ptm_copyout_siginfo = netbsd32_copyout_siginfo, 350 .ptm_copyout_lwpstatus = netbsd32_copyout_lwpstatus, 351 .ptm_doregs = netbsd32_doregs, 352 .ptm_dofpregs = netbsd32_dofpregs, 353 .ptm_dodbregs = netbsd32_dodbregs 354 }; 355 356 357 int 358 netbsd32_ptrace(struct lwp *l, const struct netbsd32_ptrace_args *uap, 359 register_t *retval) 360 { 361 int req; 362 363 /* { 364 syscallarg(int) req; 365 syscallarg(pid_t) pid; 366 syscallarg(netbsd32_voidp *) addr; 367 syscallarg(int) data; 368 } */ 369 370 req = PTRACE_TRANSLATE_REQUEST32(SCARG(uap, req)); 371 if (req == -1) 372 return EOPNOTSUPP; 373 374 return do_ptrace(&netbsd32_ptm, l, req, SCARG(uap, pid), 375 SCARG_P32(uap, addr), SCARG(uap, data), retval); 376 } 377 378 static const struct syscall_package compat_ptrace_syscalls[] = { 379 { NETBSD32_SYS_netbsd32_ptrace, 0, (sy_call_t *)netbsd32_ptrace }, 380 { 0, 0, NULL }, 381 }; 382 383 #define DEPS "compat_netbsd32,ptrace_common" 384 385 MODULE(MODULE_CLASS_EXEC, compat_netbsd32_ptrace, DEPS); 386 387 static int 388 compat_netbsd32_ptrace_modcmd(modcmd_t cmd, void *arg) 389 { 390 int error; 391 392 switch (cmd) { 393 case MODULE_CMD_INIT: 394 error = syscall_establish(&emul_netbsd32, 395 compat_ptrace_syscalls); 396 break; 397 case MODULE_CMD_FINI: 398 error = syscall_disestablish(&emul_netbsd32, 399 compat_ptrace_syscalls); 400 break; 401 default: 402 error = ENOTTY; 403 break; 404 } 405 return error; 406 } 407