1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2017 Edward Tomasz Napierala <[email protected]> 5 * All rights reserved. 6 * 7 * This software was developed by SRI International and the University of 8 * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237) 9 * ("CTSRD"), as part of the DARPA CRASH research programme. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 #include <sys/cdefs.h> 34 __FBSDID("$FreeBSD$"); 35 36 #include <sys/param.h> 37 #include <sys/proc.h> 38 #include <sys/ptrace.h> 39 #include <sys/syscallsubr.h> 40 41 #include <machine/pcb.h> 42 #include <machine/reg.h> 43 44 #include <amd64/linux/linux.h> 45 #include <amd64/linux/linux_proto.h> 46 #include <compat/linux/linux_signal.h> 47 48 #define LINUX_PTRACE_TRACEME 0 49 #define LINUX_PTRACE_PEEKTEXT 1 50 #define LINUX_PTRACE_PEEKDATA 2 51 #define LINUX_PTRACE_PEEKUSER 3 52 #define LINUX_PTRACE_POKETEXT 4 53 #define LINUX_PTRACE_POKEDATA 5 54 #define LINUX_PTRACE_POKEUSER 6 55 #define LINUX_PTRACE_CONT 7 56 #define LINUX_PTRACE_KILL 8 57 #define LINUX_PTRACE_SINGLESTEP 9 58 #define LINUX_PTRACE_GETREGS 12 59 #define LINUX_PTRACE_SETREGS 13 60 #define LINUX_PTRACE_GETFPREGS 14 61 #define LINUX_PTRACE_SETFPREGS 15 62 #define LINUX_PTRACE_ATTACH 16 63 #define LINUX_PTRACE_DETACH 17 64 #define LINUX_PTRACE_SYSCALL 24 65 #define LINUX_PTRACE_SETOPTIONS 0x4200 66 #define LINUX_PTRACE_GETREGSET 0x4204 67 #define LINUX_PTRACE_SEIZE 0x4206 68 69 #define LINUX_PTRACE_O_TRACESYSGOOD 1 70 #define LINUX_PTRACE_O_TRACEFORK 2 71 #define LINUX_PTRACE_O_TRACEVFORK 4 72 #define LINUX_PTRACE_O_TRACECLONE 8 73 #define LINUX_PTRACE_O_TRACEEXEC 16 74 #define LINUX_PTRACE_O_TRACEVFORKDONE 32 75 #define LINUX_PTRACE_O_TRACEEXIT 64 76 #define LINUX_PTRACE_O_TRACESECCOMP 128 77 #define LINUX_PTRACE_O_EXITKILL 1048576 78 #define LINUX_PTRACE_O_SUSPEND_SECCOMP 2097152 79 80 #define LINUX_NT_PRSTATUS 1 81 82 #define LINUX_PTRACE_O_MASK (LINUX_PTRACE_O_TRACESYSGOOD | \ 83 LINUX_PTRACE_O_TRACEFORK | LINUX_PTRACE_O_TRACEVFORK | \ 84 LINUX_PTRACE_O_TRACECLONE | LINUX_PTRACE_O_TRACEEXEC | \ 85 LINUX_PTRACE_O_TRACEVFORKDONE | LINUX_PTRACE_O_TRACEEXIT | \ 86 LINUX_PTRACE_O_TRACESECCOMP | LINUX_PTRACE_O_EXITKILL | \ 87 LINUX_PTRACE_O_SUSPEND_SECCOMP) 88 89 static int 90 map_signum(int lsig, int *bsigp) 91 { 92 int bsig; 93 94 if (lsig == 0) { 95 *bsigp = 0; 96 return (0); 97 } 98 99 if (lsig < 0 || lsig > LINUX_SIGRTMAX) 100 return (EINVAL); 101 102 bsig = linux_to_bsd_signal(lsig); 103 if (bsig == SIGSTOP) 104 bsig = 0; 105 106 *bsigp = bsig; 107 return (0); 108 } 109 110 struct linux_pt_reg { 111 l_ulong r15; 112 l_ulong r14; 113 l_ulong r13; 114 l_ulong r12; 115 l_ulong rbp; 116 l_ulong rbx; 117 l_ulong r11; 118 l_ulong r10; 119 l_ulong r9; 120 l_ulong r8; 121 l_ulong rax; 122 l_ulong rcx; 123 l_ulong rdx; 124 l_ulong rsi; 125 l_ulong rdi; 126 l_ulong orig_rax; 127 l_ulong rip; 128 l_ulong cs; 129 l_ulong eflags; 130 l_ulong rsp; 131 l_ulong ss; 132 }; 133 134 /* 135 * Translate amd64 ptrace registers between Linux and FreeBSD formats. 136 * The translation is pretty straighforward, for all registers but 137 * orig_rax on Linux side and r_trapno and r_err in FreeBSD. 138 */ 139 static void 140 map_regs_to_linux(struct reg *b_reg, struct linux_pt_reg *l_reg) 141 { 142 143 l_reg->r15 = b_reg->r_r15; 144 l_reg->r14 = b_reg->r_r14; 145 l_reg->r13 = b_reg->r_r13; 146 l_reg->r12 = b_reg->r_r12; 147 l_reg->rbp = b_reg->r_rbp; 148 l_reg->rbx = b_reg->r_rbx; 149 l_reg->r11 = b_reg->r_r11; 150 l_reg->r10 = b_reg->r_r10; 151 l_reg->r9 = b_reg->r_r9; 152 l_reg->r8 = b_reg->r_r8; 153 l_reg->rax = b_reg->r_rax; 154 l_reg->rcx = b_reg->r_rcx; 155 l_reg->rdx = b_reg->r_rdx; 156 l_reg->rsi = b_reg->r_rsi; 157 l_reg->rdi = b_reg->r_rdi; 158 l_reg->orig_rax = b_reg->r_rax; 159 l_reg->rip = b_reg->r_rip; 160 l_reg->cs = b_reg->r_cs; 161 l_reg->eflags = b_reg->r_rflags; 162 l_reg->rsp = b_reg->r_rsp; 163 l_reg->ss = b_reg->r_ss; 164 } 165 166 static void 167 map_regs_from_linux(struct reg *b_reg, struct linux_pt_reg *l_reg) 168 { 169 b_reg->r_r15 = l_reg->r15; 170 b_reg->r_r14 = l_reg->r14; 171 b_reg->r_r13 = l_reg->r13; 172 b_reg->r_r12 = l_reg->r12; 173 b_reg->r_r11 = l_reg->r11; 174 b_reg->r_r10 = l_reg->r10; 175 b_reg->r_r9 = l_reg->r9; 176 b_reg->r_r8 = l_reg->r8; 177 b_reg->r_rdi = l_reg->rdi; 178 b_reg->r_rsi = l_reg->rsi; 179 b_reg->r_rbp = l_reg->rbp; 180 b_reg->r_rbx = l_reg->rbx; 181 b_reg->r_rdx = l_reg->rdx; 182 b_reg->r_rcx = l_reg->rcx; 183 b_reg->r_rax = l_reg->rax; 184 185 /* 186 * XXX: Are zeroes the right thing to put here? 187 */ 188 b_reg->r_trapno = 0; 189 b_reg->r_fs = 0; 190 b_reg->r_gs = 0; 191 b_reg->r_err = 0; 192 b_reg->r_es = 0; 193 b_reg->r_ds = 0; 194 195 b_reg->r_rip = l_reg->rip; 196 b_reg->r_cs = l_reg->cs; 197 b_reg->r_rflags = l_reg->eflags; 198 b_reg->r_rsp = l_reg->rsp; 199 b_reg->r_ss = l_reg->ss; 200 } 201 202 static int 203 linux_ptrace_peek(struct thread *td, pid_t pid, void *addr, void *data) 204 { 205 int error; 206 207 error = kern_ptrace(td, PT_READ_I, pid, addr, 0); 208 if (error == 0) 209 error = copyout(td->td_retval, data, sizeof(l_int)); 210 td->td_retval[0] = error; 211 212 return (error); 213 } 214 215 static int 216 linux_ptrace_setoptions(struct thread *td, pid_t pid, l_ulong data) 217 { 218 int mask; 219 220 mask = 0; 221 222 if (data & ~LINUX_PTRACE_O_MASK) { 223 printf("%s: unknown ptrace option %lx set; " 224 "returning EINVAL\n", 225 __func__, data & ~LINUX_PTRACE_O_MASK); 226 return (EINVAL); 227 } 228 229 /* 230 * PTRACE_O_EXITKILL is ignored, we do that by default. 231 */ 232 233 if (data & LINUX_PTRACE_O_TRACESYSGOOD) { 234 printf("%s: PTRACE_O_TRACESYSGOOD not implemented; " 235 "returning EINVAL\n", __func__); 236 return (EINVAL); 237 } 238 239 if (data & LINUX_PTRACE_O_TRACEFORK) 240 mask |= PTRACE_FORK; 241 242 if (data & LINUX_PTRACE_O_TRACEVFORK) 243 mask |= PTRACE_VFORK; 244 245 if (data & LINUX_PTRACE_O_TRACECLONE) 246 mask |= PTRACE_VFORK; 247 248 if (data & LINUX_PTRACE_O_TRACEEXEC) 249 mask |= PTRACE_EXEC; 250 251 if (data & LINUX_PTRACE_O_TRACEVFORKDONE) 252 mask |= PTRACE_VFORK; /* XXX: Close enough? */ 253 254 if (data & LINUX_PTRACE_O_TRACEEXIT) { 255 printf("%s: PTRACE_O_TRACEEXIT not implemented; " 256 "returning EINVAL\n", __func__); 257 return (EINVAL); 258 } 259 260 return (kern_ptrace(td, PT_SET_EVENT_MASK, pid, &mask, sizeof(mask))); 261 } 262 263 static int 264 linux_ptrace_getregs(struct thread *td, pid_t pid, void *data) 265 { 266 struct ptrace_lwpinfo lwpinfo; 267 struct reg b_reg; 268 struct linux_pt_reg l_reg; 269 int error; 270 271 error = kern_ptrace(td, PT_GETREGS, pid, &b_reg, 0); 272 if (error != 0) 273 return (error); 274 275 map_regs_to_linux(&b_reg, &l_reg); 276 277 /* 278 * The strace(1) utility depends on RAX being set to -ENOSYS 279 * on syscall entry. 280 */ 281 error = kern_ptrace(td, PT_LWPINFO, pid, &lwpinfo, sizeof(lwpinfo)); 282 if (error != 0) { 283 printf("%s: PT_LWPINFO failed with error %d\n", __func__, error); 284 return (error); 285 } 286 if (lwpinfo.pl_flags & PL_FLAG_SCE) 287 l_reg.rax = -38; // XXX: Don't hardcode? 288 289 error = copyout(&l_reg, (void *)data, sizeof(l_reg)); 290 return (error); 291 } 292 293 static int 294 linux_ptrace_setregs(struct thread *td, pid_t pid, void *data) 295 { 296 struct reg b_reg; 297 struct linux_pt_reg l_reg; 298 int error; 299 300 error = copyin(data, &l_reg, sizeof(l_reg)); 301 if (error != 0) 302 return (error); 303 map_regs_from_linux(&b_reg, &l_reg); 304 error = kern_ptrace(td, PT_SETREGS, pid, &b_reg, 0); 305 return (error); 306 } 307 308 static int 309 linux_ptrace_getregset(struct thread *td, pid_t pid, l_ulong addr, l_ulong data) 310 { 311 312 switch (addr) { 313 case LINUX_NT_PRSTATUS: 314 printf("%s: NT_PRSTATUS not implemented; returning EINVAL\n", 315 __func__); 316 return (EINVAL); 317 default: 318 printf("%s: PTRACE_GETREGSET request %ld not implemented; " 319 "returning EINVAL\n", __func__, addr); 320 return (EINVAL); 321 } 322 } 323 324 static int 325 linux_ptrace_seize(struct thread *td, pid_t pid, l_ulong addr, l_ulong data) 326 { 327 328 printf("%s: PTRACE_SEIZE not implemented; returning EINVAL\n", __func__); 329 return (EINVAL); 330 } 331 332 int 333 linux_ptrace(struct thread *td, struct linux_ptrace_args *uap) 334 { 335 void *addr; 336 pid_t pid; 337 int error, sig; 338 339 pid = (pid_t)uap->pid; 340 addr = (void *)uap->addr; 341 342 switch (uap->req) { 343 case LINUX_PTRACE_TRACEME: 344 error = kern_ptrace(td, PT_TRACE_ME, 0, 0, 0); 345 break; 346 case LINUX_PTRACE_PEEKTEXT: 347 case LINUX_PTRACE_PEEKDATA: 348 error = linux_ptrace_peek(td, pid, addr, (void *)uap->data); 349 if (error != 0) 350 return (error); 351 /* 352 * Linux expects this syscall to read 64 bits, not 32. 353 */ 354 error = linux_ptrace_peek(td, pid, 355 (void *)(uap->addr + 4), (void *)(uap->data + 4)); 356 break; 357 case LINUX_PTRACE_POKETEXT: 358 error = kern_ptrace(td, PT_WRITE_I, pid, addr, uap->data); 359 break; 360 case LINUX_PTRACE_POKEDATA: 361 error = kern_ptrace(td, PT_WRITE_D, pid, addr, uap->data); 362 break; 363 case LINUX_PTRACE_CONT: 364 error = map_signum(uap->data, &sig); 365 if (error != 0) 366 break; 367 error = kern_ptrace(td, PT_CONTINUE, pid, (void *)1, sig); 368 break; 369 case LINUX_PTRACE_KILL: 370 error = kern_ptrace(td, PT_KILL, pid, addr, uap->data); 371 break; 372 case LINUX_PTRACE_SINGLESTEP: 373 error = map_signum(uap->data, &sig); 374 if (error != 0) 375 break; 376 error = kern_ptrace(td, PT_STEP, pid, (void *)1, sig); 377 break; 378 case LINUX_PTRACE_GETREGS: 379 error = linux_ptrace_getregs(td, pid, (void *)uap->data); 380 break; 381 case LINUX_PTRACE_SETREGS: 382 error = linux_ptrace_setregs(td, pid, (void *)uap->data); 383 break; 384 case LINUX_PTRACE_ATTACH: 385 error = kern_ptrace(td, PT_ATTACH, pid, addr, uap->data); 386 break; 387 case LINUX_PTRACE_DETACH: 388 error = map_signum(uap->data, &sig); 389 if (error != 0) 390 break; 391 error = kern_ptrace(td, PT_DETACH, pid, (void *)1, sig); 392 break; 393 case LINUX_PTRACE_SYSCALL: 394 error = map_signum(uap->data, &sig); 395 if (error != 0) 396 break; 397 error = kern_ptrace(td, PT_SYSCALL, pid, (void *)1, sig); 398 break; 399 case LINUX_PTRACE_SETOPTIONS: 400 error = linux_ptrace_setoptions(td, pid, uap->data); 401 break; 402 case LINUX_PTRACE_GETREGSET: 403 error = linux_ptrace_getregset(td, pid, uap->addr, uap->data); 404 break; 405 case LINUX_PTRACE_SEIZE: 406 error = linux_ptrace_seize(td, pid, uap->addr, uap->data); 407 break; 408 default: 409 printf("%s: ptrace(%ld, ...) not implemented; returning EINVAL\n", 410 __func__, uap->req); 411 error = EINVAL; 412 break; 413 } 414 415 return (error); 416 } 417