1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2017 Edward Tomasz Napierala <trasz@FreeBSD.org> 5 * 6 * This software was developed by SRI International and the University of 7 * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237) 8 * ("CTSRD"), as part of the DARPA CRASH research programme. 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 AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/param.h> 33 #include <sys/lock.h> 34 #include <sys/proc.h> 35 #include <sys/ptrace.h> 36 #include <sys/sx.h> 37 #include <sys/syscallsubr.h> 38 39 #include <machine/../linux/linux.h> 40 #include <machine/../linux/linux_proto.h> 41 #include <compat/linux/linux_emul.h> 42 #include <compat/linux/linux_errno.h> 43 #include <compat/linux/linux_misc.h> 44 #include <compat/linux/linux_signal.h> 45 #include <compat/linux/linux_util.h> 46 47 #define LINUX_PTRACE_TRACEME 0 48 #define LINUX_PTRACE_PEEKTEXT 1 49 #define LINUX_PTRACE_PEEKDATA 2 50 #define LINUX_PTRACE_PEEKUSER 3 51 #define LINUX_PTRACE_POKETEXT 4 52 #define LINUX_PTRACE_POKEDATA 5 53 #define LINUX_PTRACE_POKEUSER 6 54 #define LINUX_PTRACE_CONT 7 55 #define LINUX_PTRACE_KILL 8 56 #define LINUX_PTRACE_SINGLESTEP 9 57 #define LINUX_PTRACE_GETREGS 12 58 #define LINUX_PTRACE_SETREGS 13 59 #define LINUX_PTRACE_GETFPREGS 14 60 #define LINUX_PTRACE_SETFPREGS 15 61 #define LINUX_PTRACE_ATTACH 16 62 #define LINUX_PTRACE_DETACH 17 63 #define LINUX_PTRACE_SYSCALL 24 64 #define LINUX_PTRACE_SETOPTIONS 0x4200 65 #define LINUX_PTRACE_GETEVENTMSG 0x4201 66 #define LINUX_PTRACE_GETSIGINFO 0x4202 67 #define LINUX_PTRACE_GETREGSET 0x4204 68 #define LINUX_PTRACE_SEIZE 0x4206 69 #define LINUX_PTRACE_GET_SYSCALL_INFO 0x420e 70 71 #define LINUX_PTRACE_EVENT_EXEC 4 72 #define LINUX_PTRACE_EVENT_EXIT 6 73 74 #define LINUX_PTRACE_O_TRACESYSGOOD 1 75 #define LINUX_PTRACE_O_TRACEFORK 2 76 #define LINUX_PTRACE_O_TRACEVFORK 4 77 #define LINUX_PTRACE_O_TRACECLONE 8 78 #define LINUX_PTRACE_O_TRACEEXEC 16 79 #define LINUX_PTRACE_O_TRACEVFORKDONE 32 80 #define LINUX_PTRACE_O_TRACEEXIT 64 81 #define LINUX_PTRACE_O_TRACESECCOMP 128 82 #define LINUX_PTRACE_O_EXITKILL 1048576 83 #define LINUX_PTRACE_O_SUSPEND_SECCOMP 2097152 84 85 #define LINUX_NT_PRSTATUS 0x1 86 #define LINUX_NT_PRFPREG 0x2 87 #define LINUX_NT_X86_XSTATE 0x202 88 89 #define LINUX_PTRACE_O_MASK (LINUX_PTRACE_O_TRACESYSGOOD | \ 90 LINUX_PTRACE_O_TRACEFORK | LINUX_PTRACE_O_TRACEVFORK | \ 91 LINUX_PTRACE_O_TRACECLONE | LINUX_PTRACE_O_TRACEEXEC | \ 92 LINUX_PTRACE_O_TRACEVFORKDONE | LINUX_PTRACE_O_TRACEEXIT | \ 93 LINUX_PTRACE_O_TRACESECCOMP | LINUX_PTRACE_O_EXITKILL | \ 94 LINUX_PTRACE_O_SUSPEND_SECCOMP) 95 96 #define LINUX_PTRACE_SYSCALL_INFO_NONE 0 97 #define LINUX_PTRACE_SYSCALL_INFO_ENTRY 1 98 #define LINUX_PTRACE_SYSCALL_INFO_EXIT 2 99 100 static int 101 map_signum(int lsig, int *bsigp) 102 { 103 int bsig; 104 105 if (lsig == 0) { 106 *bsigp = 0; 107 return (0); 108 } 109 110 if (lsig < 0 || lsig > LINUX_SIGRTMAX) 111 return (EINVAL); 112 113 bsig = linux_to_bsd_signal(lsig); 114 if (bsig == SIGSTOP) 115 bsig = 0; 116 117 *bsigp = bsig; 118 return (0); 119 } 120 121 int 122 linux_ptrace_status(struct thread *td, pid_t pid, int status) 123 { 124 struct ptrace_lwpinfo lwpinfo; 125 struct linux_pemuldata *pem; 126 register_t saved_retval; 127 int error; 128 129 saved_retval = td->td_retval[0]; 130 error = kern_ptrace(td, PT_LWPINFO, pid, &lwpinfo, sizeof(lwpinfo)); 131 td->td_retval[0] = saved_retval; 132 if (error != 0) { 133 linux_msg(td, "PT_LWPINFO failed with error %d", error); 134 return (status); 135 } 136 137 pem = pem_find(td->td_proc); 138 KASSERT(pem != NULL, ("%s: proc emuldata not found.\n", __func__)); 139 140 LINUX_PEM_SLOCK(pem); 141 if ((pem->ptrace_flags & LINUX_PTRACE_O_TRACESYSGOOD) && 142 lwpinfo.pl_flags & PL_FLAG_SCE) 143 status |= (LINUX_SIGTRAP | 0x80) << 8; 144 if ((pem->ptrace_flags & LINUX_PTRACE_O_TRACESYSGOOD) && 145 lwpinfo.pl_flags & PL_FLAG_SCX) { 146 if (lwpinfo.pl_flags & PL_FLAG_EXEC) 147 status |= (LINUX_SIGTRAP | LINUX_PTRACE_EVENT_EXEC << 8) << 8; 148 else 149 status |= (LINUX_SIGTRAP | 0x80) << 8; 150 } 151 if ((pem->ptrace_flags & LINUX_PTRACE_O_TRACEEXIT) && 152 lwpinfo.pl_flags & PL_FLAG_EXITED) 153 status |= (LINUX_SIGTRAP | LINUX_PTRACE_EVENT_EXIT << 8) << 8; 154 LINUX_PEM_SUNLOCK(pem); 155 156 return (status); 157 } 158 159 static int 160 linux_ptrace_peek(struct thread *td, pid_t pid, void *addr, void *data) 161 { 162 int error; 163 164 error = kern_ptrace(td, PT_READ_I, pid, addr, 0); 165 if (error == 0) 166 error = copyout(td->td_retval, data, sizeof(l_int)); 167 else if (error == ENOMEM) 168 error = EIO; 169 td->td_retval[0] = error; 170 171 return (error); 172 } 173 174 static int 175 linux_ptrace_setoptions(struct thread *td, pid_t pid, l_ulong data) 176 { 177 struct linux_pemuldata *pem; 178 int mask; 179 180 mask = 0; 181 182 if (data & ~LINUX_PTRACE_O_MASK) { 183 linux_msg(td, "unknown ptrace option %lx set; " 184 "returning EINVAL", 185 data & ~LINUX_PTRACE_O_MASK); 186 return (EINVAL); 187 } 188 189 pem = pem_find(td->td_proc); 190 KASSERT(pem != NULL, ("%s: proc emuldata not found.\n", __func__)); 191 192 /* 193 * PTRACE_O_EXITKILL is ignored, we do that by default. 194 */ 195 196 LINUX_PEM_XLOCK(pem); 197 if (data & LINUX_PTRACE_O_TRACESYSGOOD) { 198 pem->ptrace_flags |= LINUX_PTRACE_O_TRACESYSGOOD; 199 } else { 200 pem->ptrace_flags &= ~LINUX_PTRACE_O_TRACESYSGOOD; 201 } 202 LINUX_PEM_XUNLOCK(pem); 203 204 if (data & LINUX_PTRACE_O_TRACEFORK) 205 mask |= PTRACE_FORK; 206 207 if (data & LINUX_PTRACE_O_TRACEVFORK) 208 mask |= PTRACE_VFORK; 209 210 if (data & LINUX_PTRACE_O_TRACECLONE) 211 mask |= PTRACE_VFORK; 212 213 if (data & LINUX_PTRACE_O_TRACEEXEC) 214 mask |= PTRACE_EXEC; 215 216 if (data & LINUX_PTRACE_O_TRACEVFORKDONE) 217 mask |= PTRACE_VFORK; /* XXX: Close enough? */ 218 219 if (data & LINUX_PTRACE_O_TRACEEXIT) { 220 pem->ptrace_flags |= LINUX_PTRACE_O_TRACEEXIT; 221 } else { 222 pem->ptrace_flags &= ~LINUX_PTRACE_O_TRACEEXIT; 223 } 224 225 return (kern_ptrace(td, PT_SET_EVENT_MASK, pid, &mask, sizeof(mask))); 226 } 227 228 static int 229 linux_ptrace_geteventmsg(struct thread *td, pid_t pid, l_ulong data) 230 { 231 232 linux_msg(td, "PTRACE_GETEVENTMSG not implemented; returning EINVAL"); 233 return (EINVAL); 234 } 235 236 static int 237 linux_ptrace_getsiginfo(struct thread *td, pid_t pid, l_ulong data) 238 { 239 struct ptrace_lwpinfo lwpinfo; 240 l_siginfo_t l_siginfo; 241 int error, sig; 242 243 error = kern_ptrace(td, PT_LWPINFO, pid, &lwpinfo, sizeof(lwpinfo)); 244 if (error != 0) { 245 linux_msg(td, "PT_LWPINFO failed with error %d", error); 246 return (error); 247 } 248 249 if ((lwpinfo.pl_flags & PL_FLAG_SI) == 0) { 250 error = EINVAL; 251 linux_msg(td, "no PL_FLAG_SI, returning %d", error); 252 return (error); 253 } 254 255 sig = bsd_to_linux_signal(lwpinfo.pl_siginfo.si_signo); 256 memset(&l_siginfo, 0, sizeof(l_siginfo)); 257 siginfo_to_lsiginfo(&lwpinfo.pl_siginfo, &l_siginfo, sig); 258 error = copyout(&l_siginfo, (void *)data, sizeof(l_siginfo)); 259 return (error); 260 } 261 262 static int 263 linux_ptrace_getregs(struct thread *td, pid_t pid, void *data) 264 { 265 struct reg b_reg; 266 struct linux_pt_regset l_regset; 267 int error; 268 269 error = kern_ptrace(td, PT_GETREGS, pid, &b_reg, 0); 270 if (error != 0) 271 return (error); 272 273 bsd_to_linux_regset(&b_reg, &l_regset); 274 error = linux_ptrace_getregs_machdep(td, pid, &l_regset); 275 if (error != 0) 276 return (error); 277 278 error = copyout(&l_regset, (void *)data, sizeof(l_regset)); 279 return (error); 280 } 281 282 static int 283 linux_ptrace_setregs(struct thread *td, pid_t pid, void *data) 284 { 285 struct reg b_reg; 286 struct linux_pt_regset l_regset; 287 int error; 288 289 error = copyin(data, &l_regset, sizeof(l_regset)); 290 if (error != 0) 291 return (error); 292 linux_to_bsd_regset(&b_reg, &l_regset); 293 error = kern_ptrace(td, PT_SETREGS, pid, &b_reg, 0); 294 return (error); 295 } 296 297 static int 298 linux_ptrace_getregset_prstatus(struct thread *td, pid_t pid, l_ulong data) 299 { 300 struct reg b_reg; 301 struct linux_pt_regset l_regset; 302 struct iovec iov; 303 size_t len; 304 int error; 305 306 error = copyin((const void *)data, &iov, sizeof(iov)); 307 if (error != 0) { 308 linux_msg(td, "copyin error %d", error); 309 return (error); 310 } 311 312 error = kern_ptrace(td, PT_GETREGS, pid, &b_reg, 0); 313 if (error != 0) 314 return (error); 315 316 bsd_to_linux_regset(&b_reg, &l_regset); 317 error = linux_ptrace_getregs_machdep(td, pid, &l_regset); 318 if (error != 0) 319 return (error); 320 321 len = MIN(iov.iov_len, sizeof(l_regset)); 322 error = copyout(&l_regset, (void *)iov.iov_base, len); 323 if (error != 0) { 324 linux_msg(td, "copyout error %d", error); 325 return (error); 326 } 327 328 iov.iov_len = len; 329 error = copyout(&iov, (void *)data, sizeof(iov)); 330 if (error != 0) { 331 linux_msg(td, "iov copyout error %d", error); 332 return (error); 333 } 334 335 return (error); 336 } 337 338 static int 339 linux_ptrace_getregset(struct thread *td, pid_t pid, l_ulong addr, l_ulong data) 340 { 341 342 switch (addr) { 343 case LINUX_NT_PRSTATUS: 344 return (linux_ptrace_getregset_prstatus(td, pid, data)); 345 case LINUX_NT_PRFPREG: 346 linux_msg(td, "PTRAGE_GETREGSET NT_PRFPREG not implemented; " 347 "returning EINVAL"); 348 return (EINVAL); 349 case LINUX_NT_X86_XSTATE: 350 linux_msg(td, "PTRAGE_GETREGSET NT_X86_XSTATE not implemented; " 351 "returning EINVAL"); 352 return (EINVAL); 353 default: 354 linux_msg(td, "PTRACE_GETREGSET request %#lx not implemented; " 355 "returning EINVAL", addr); 356 return (EINVAL); 357 } 358 } 359 360 static int 361 linux_ptrace_seize(struct thread *td, pid_t pid, l_ulong addr, l_ulong data) 362 { 363 364 linux_msg(td, "PTRACE_SEIZE not implemented; returning EINVAL"); 365 return (EINVAL); 366 } 367 368 static int 369 linux_ptrace_get_syscall_info(struct thread *td, pid_t pid, 370 l_ulong len, l_ulong data) 371 { 372 struct ptrace_lwpinfo lwpinfo; 373 struct ptrace_sc_ret sr; 374 struct reg b_reg; 375 struct syscall_info si; 376 int error; 377 378 error = kern_ptrace(td, PT_LWPINFO, pid, &lwpinfo, sizeof(lwpinfo)); 379 if (error != 0) { 380 linux_msg(td, "PT_LWPINFO failed with error %d", error); 381 return (error); 382 } 383 384 memset(&si, 0, sizeof(si)); 385 386 if (lwpinfo.pl_flags & PL_FLAG_SCE) { 387 si.op = LINUX_PTRACE_SYSCALL_INFO_ENTRY; 388 si.entry.nr = lwpinfo.pl_syscall_code; 389 error = kern_ptrace(td, PTLINUX_GET_SC_ARGS, pid, 390 si.entry.args, sizeof(si.entry.args)); 391 if (error != 0) { 392 linux_msg(td, 393 "PT_LINUX_GET_SC_ARGS failed with error %d", error); 394 return (error); 395 } 396 } else if (lwpinfo.pl_flags & PL_FLAG_SCX) { 397 si.op = LINUX_PTRACE_SYSCALL_INFO_EXIT; 398 error = kern_ptrace(td, PT_GET_SC_RET, pid, &sr, sizeof(sr)); 399 400 if (error != 0) { 401 linux_msg(td, "PT_GET_SC_RET failed with error %d", 402 error); 403 return (error); 404 } 405 406 if (sr.sr_error == 0) { 407 si.exit.rval = sr.sr_retval[0]; 408 si.exit.is_error = 0; 409 } else if (sr.sr_error == EJUSTRETURN) { 410 /* 411 * EJUSTRETURN means the actual value to return 412 * has already been put into td_frame; instead 413 * of extracting it and trying to determine whether 414 * it's an error or not just bail out and let 415 * the ptracing process fall back to another method. 416 */ 417 si.op = LINUX_PTRACE_SYSCALL_INFO_NONE; 418 } else if (sr.sr_error == ERESTART) { 419 si.exit.rval = -LINUX_ERESTARTSYS; 420 si.exit.is_error = 1; 421 } else { 422 si.exit.rval = bsd_to_linux_errno(sr.sr_error); 423 si.exit.is_error = 1; 424 } 425 } else { 426 si.op = LINUX_PTRACE_SYSCALL_INFO_NONE; 427 } 428 429 error = kern_ptrace(td, PT_GETREGS, pid, &b_reg, 0); 430 if (error != 0) 431 return (error); 432 433 linux_ptrace_get_syscall_info_machdep(&b_reg, &si); 434 435 len = MIN(len, sizeof(si)); 436 error = copyout(&si, (void *)data, len); 437 if (error == 0) 438 td->td_retval[0] = sizeof(si); 439 440 return (error); 441 } 442 443 int 444 linux_ptrace(struct thread *td, struct linux_ptrace_args *uap) 445 { 446 void *addr; 447 pid_t pid; 448 int error, sig; 449 450 if (!allow_ptrace) 451 return (ENOSYS); 452 453 pid = (pid_t)uap->pid; 454 addr = (void *)uap->addr; 455 456 switch (uap->req) { 457 case LINUX_PTRACE_TRACEME: 458 error = kern_ptrace(td, PT_TRACE_ME, 0, 0, 0); 459 break; 460 case LINUX_PTRACE_PEEKTEXT: 461 case LINUX_PTRACE_PEEKDATA: 462 error = linux_ptrace_peek(td, pid, addr, (void *)uap->data); 463 if (error != 0) 464 goto out; 465 /* 466 * Linux expects this syscall to read 64 bits, not 32. 467 */ 468 error = linux_ptrace_peek(td, pid, 469 (void *)(uap->addr + 4), (void *)(uap->data + 4)); 470 break; 471 case LINUX_PTRACE_PEEKUSER: 472 error = linux_ptrace_peekuser(td, pid, addr, (void *)uap->data); 473 break; 474 case LINUX_PTRACE_POKETEXT: 475 case LINUX_PTRACE_POKEDATA: 476 error = kern_ptrace(td, PT_WRITE_D, pid, addr, uap->data); 477 if (error != 0) 478 goto out; 479 /* 480 * Linux expects this syscall to write 64 bits, not 32. 481 */ 482 error = kern_ptrace(td, PT_WRITE_D, pid, 483 (void *)(uap->addr + 4), uap->data >> 32); 484 break; 485 case LINUX_PTRACE_POKEUSER: 486 error = linux_ptrace_pokeuser(td, pid, addr, (void *)uap->data); 487 break; 488 case LINUX_PTRACE_CONT: 489 error = map_signum(uap->data, &sig); 490 if (error != 0) 491 break; 492 error = kern_ptrace(td, PT_CONTINUE, pid, (void *)1, sig); 493 break; 494 case LINUX_PTRACE_KILL: 495 error = kern_ptrace(td, PT_KILL, pid, addr, uap->data); 496 break; 497 case LINUX_PTRACE_SINGLESTEP: 498 error = map_signum(uap->data, &sig); 499 if (error != 0) 500 break; 501 error = kern_ptrace(td, PT_STEP, pid, (void *)1, sig); 502 break; 503 case LINUX_PTRACE_GETREGS: 504 error = linux_ptrace_getregs(td, pid, (void *)uap->data); 505 break; 506 case LINUX_PTRACE_SETREGS: 507 error = linux_ptrace_setregs(td, pid, (void *)uap->data); 508 break; 509 case LINUX_PTRACE_ATTACH: 510 error = kern_ptrace(td, PT_ATTACH, pid, addr, uap->data); 511 break; 512 case LINUX_PTRACE_DETACH: 513 error = map_signum(uap->data, &sig); 514 if (error != 0) 515 break; 516 error = kern_ptrace(td, PT_DETACH, pid, (void *)1, sig); 517 break; 518 case LINUX_PTRACE_SYSCALL: 519 error = map_signum(uap->data, &sig); 520 if (error != 0) 521 break; 522 error = kern_ptrace(td, PT_SYSCALL, pid, (void *)1, sig); 523 break; 524 case LINUX_PTRACE_SETOPTIONS: 525 error = linux_ptrace_setoptions(td, pid, uap->data); 526 break; 527 case LINUX_PTRACE_GETEVENTMSG: 528 error = linux_ptrace_geteventmsg(td, pid, uap->data); 529 break; 530 case LINUX_PTRACE_GETSIGINFO: 531 error = linux_ptrace_getsiginfo(td, pid, uap->data); 532 break; 533 case LINUX_PTRACE_GETREGSET: 534 error = linux_ptrace_getregset(td, pid, uap->addr, uap->data); 535 break; 536 case LINUX_PTRACE_SEIZE: 537 error = linux_ptrace_seize(td, pid, uap->addr, uap->data); 538 break; 539 case LINUX_PTRACE_GET_SYSCALL_INFO: 540 error = linux_ptrace_get_syscall_info(td, pid, uap->addr, uap->data); 541 break; 542 default: 543 linux_msg(td, "ptrace(%ld, ...) not implemented; " 544 "returning EINVAL", uap->req); 545 error = EINVAL; 546 break; 547 } 548 549 out: 550 if (error == EBUSY) 551 error = ESRCH; 552 553 return (error); 554 } 555