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