1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2001 Alexander Kabaev 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 AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 #include "opt_cpu.h" 31 32 #include <sys/param.h> 33 #include <sys/lock.h> 34 #include <sys/mutex.h> 35 #include <sys/proc.h> 36 #include <sys/ptrace.h> 37 #include <sys/syscallsubr.h> 38 #include <sys/systm.h> 39 40 #include <machine/md_var.h> 41 #include <machine/pcb.h> 42 43 #include <i386/linux/linux.h> 44 #include <i386/linux/linux_proto.h> 45 #include <compat/linux/linux_signal.h> 46 47 /* 48 * Linux ptrace requests numbers. Mostly identical to FreeBSD, 49 * except for MD ones and PT_ATTACH/PT_DETACH. 50 */ 51 #define PTRACE_TRACEME 0 52 #define PTRACE_PEEKTEXT 1 53 #define PTRACE_PEEKDATA 2 54 #define PTRACE_PEEKUSR 3 55 #define PTRACE_POKETEXT 4 56 #define PTRACE_POKEDATA 5 57 #define PTRACE_POKEUSR 6 58 #define PTRACE_CONT 7 59 #define PTRACE_KILL 8 60 #define PTRACE_SINGLESTEP 9 61 62 #define PTRACE_ATTACH 16 63 #define PTRACE_DETACH 17 64 65 #define LINUX_PTRACE_SYSCALL 24 66 67 #define PTRACE_GETREGS 12 68 #define PTRACE_SETREGS 13 69 #define PTRACE_GETFPREGS 14 70 #define PTRACE_SETFPREGS 15 71 #define PTRACE_GETFPXREGS 18 72 #define PTRACE_SETFPXREGS 19 73 74 #define PTRACE_SETOPTIONS 21 75 76 /* 77 * Linux keeps debug registers at the following 78 * offset in the user struct 79 */ 80 #define LINUX_DBREG_OFFSET 252 81 #define LINUX_DBREG_SIZE (8*sizeof(l_int)) 82 83 static __inline int 84 map_signum(int signum) 85 { 86 87 signum = linux_to_bsd_signal(signum); 88 return ((signum == SIGSTOP)? 0 : signum); 89 } 90 91 struct linux_pt_reg { 92 l_long ebx; 93 l_long ecx; 94 l_long edx; 95 l_long esi; 96 l_long edi; 97 l_long ebp; 98 l_long eax; 99 l_int xds; 100 l_int xes; 101 l_int xfs; 102 l_int xgs; 103 l_long orig_eax; 104 l_long eip; 105 l_int xcs; 106 l_long eflags; 107 l_long esp; 108 l_int xss; 109 }; 110 111 /* 112 * Translate i386 ptrace registers between Linux and FreeBSD formats. 113 * The translation is pretty straighforward, for all registers, but 114 * orig_eax on Linux side and r_trapno and r_err in FreeBSD 115 */ 116 static void 117 map_regs_to_linux(struct reg *bsd_r, struct linux_pt_reg *linux_r) 118 { 119 linux_r->ebx = bsd_r->r_ebx; 120 linux_r->ecx = bsd_r->r_ecx; 121 linux_r->edx = bsd_r->r_edx; 122 linux_r->esi = bsd_r->r_esi; 123 linux_r->edi = bsd_r->r_edi; 124 linux_r->ebp = bsd_r->r_ebp; 125 linux_r->eax = bsd_r->r_eax; 126 linux_r->xds = bsd_r->r_ds; 127 linux_r->xes = bsd_r->r_es; 128 linux_r->xfs = bsd_r->r_fs; 129 linux_r->xgs = bsd_r->r_gs; 130 linux_r->orig_eax = bsd_r->r_eax; 131 linux_r->eip = bsd_r->r_eip; 132 linux_r->xcs = bsd_r->r_cs; 133 linux_r->eflags = bsd_r->r_eflags; 134 linux_r->esp = bsd_r->r_esp; 135 linux_r->xss = bsd_r->r_ss; 136 } 137 138 static void 139 map_regs_from_linux(struct reg *bsd_r, struct linux_pt_reg *linux_r) 140 { 141 bsd_r->r_ebx = linux_r->ebx; 142 bsd_r->r_ecx = linux_r->ecx; 143 bsd_r->r_edx = linux_r->edx; 144 bsd_r->r_esi = linux_r->esi; 145 bsd_r->r_edi = linux_r->edi; 146 bsd_r->r_ebp = linux_r->ebp; 147 bsd_r->r_eax = linux_r->eax; 148 bsd_r->r_ds = linux_r->xds; 149 bsd_r->r_es = linux_r->xes; 150 bsd_r->r_fs = linux_r->xfs; 151 bsd_r->r_gs = linux_r->xgs; 152 bsd_r->r_eip = linux_r->eip; 153 bsd_r->r_cs = linux_r->xcs; 154 bsd_r->r_eflags = linux_r->eflags; 155 bsd_r->r_esp = linux_r->esp; 156 bsd_r->r_ss = linux_r->xss; 157 } 158 159 struct linux_pt_fpreg { 160 l_long cwd; 161 l_long swd; 162 l_long twd; 163 l_long fip; 164 l_long fcs; 165 l_long foo; 166 l_long fos; 167 l_long st_space[2*10]; 168 }; 169 170 static void 171 map_fpregs_to_linux(struct fpreg *bsd_r, struct linux_pt_fpreg *linux_r) 172 { 173 linux_r->cwd = bsd_r->fpr_env[0]; 174 linux_r->swd = bsd_r->fpr_env[1]; 175 linux_r->twd = bsd_r->fpr_env[2]; 176 linux_r->fip = bsd_r->fpr_env[3]; 177 linux_r->fcs = bsd_r->fpr_env[4]; 178 linux_r->foo = bsd_r->fpr_env[5]; 179 linux_r->fos = bsd_r->fpr_env[6]; 180 bcopy(bsd_r->fpr_acc, linux_r->st_space, sizeof(linux_r->st_space)); 181 } 182 183 static void 184 map_fpregs_from_linux(struct fpreg *bsd_r, struct linux_pt_fpreg *linux_r) 185 { 186 bsd_r->fpr_env[0] = linux_r->cwd; 187 bsd_r->fpr_env[1] = linux_r->swd; 188 bsd_r->fpr_env[2] = linux_r->twd; 189 bsd_r->fpr_env[3] = linux_r->fip; 190 bsd_r->fpr_env[4] = linux_r->fcs; 191 bsd_r->fpr_env[5] = linux_r->foo; 192 bsd_r->fpr_env[6] = linux_r->fos; 193 bcopy(bsd_r->fpr_acc, linux_r->st_space, sizeof(bsd_r->fpr_acc)); 194 } 195 196 struct linux_pt_fpxreg { 197 l_ushort cwd; 198 l_ushort swd; 199 l_ushort twd; 200 l_ushort fop; 201 l_long fip; 202 l_long fcs; 203 l_long foo; 204 l_long fos; 205 l_long mxcsr; 206 l_long reserved; 207 l_long st_space[32]; 208 l_long xmm_space[32]; 209 l_long padding[56]; 210 }; 211 212 static int 213 linux_proc_read_fpxregs(struct thread *td, struct linux_pt_fpxreg *fpxregs) 214 { 215 216 PROC_LOCK_ASSERT(td->td_proc, MA_OWNED); 217 if (cpu_fxsr == 0 || (td->td_proc->p_flag & P_INMEM) == 0) 218 return (EIO); 219 bcopy(&get_pcb_user_save_td(td)->sv_xmm, fpxregs, sizeof(*fpxregs)); 220 return (0); 221 } 222 223 static int 224 linux_proc_write_fpxregs(struct thread *td, struct linux_pt_fpxreg *fpxregs) 225 { 226 227 PROC_LOCK_ASSERT(td->td_proc, MA_OWNED); 228 if (cpu_fxsr == 0 || (td->td_proc->p_flag & P_INMEM) == 0) 229 return (EIO); 230 bcopy(fpxregs, &get_pcb_user_save_td(td)->sv_xmm, sizeof(*fpxregs)); 231 return (0); 232 } 233 234 int 235 linux_ptrace(struct thread *td, struct linux_ptrace_args *uap) 236 { 237 union { 238 struct linux_pt_reg reg; 239 struct linux_pt_fpreg fpreg; 240 struct linux_pt_fpxreg fpxreg; 241 } r; 242 union { 243 struct reg bsd_reg; 244 struct fpreg bsd_fpreg; 245 struct dbreg bsd_dbreg; 246 } u; 247 void *addr; 248 pid_t pid; 249 int error, req; 250 251 error = 0; 252 253 /* by default, just copy data intact */ 254 req = uap->req; 255 pid = (pid_t)uap->pid; 256 addr = (void *)uap->addr; 257 258 switch (req) { 259 case PTRACE_TRACEME: 260 case PTRACE_POKETEXT: 261 case PTRACE_POKEDATA: 262 case PTRACE_KILL: 263 error = kern_ptrace(td, req, pid, addr, uap->data); 264 break; 265 case PTRACE_PEEKTEXT: 266 case PTRACE_PEEKDATA: { 267 /* need to preserve return value */ 268 int rval = td->td_retval[0]; 269 error = kern_ptrace(td, req, pid, addr, 0); 270 if (error == 0) 271 error = copyout(td->td_retval, (void *)uap->data, 272 sizeof(l_int)); 273 td->td_retval[0] = rval; 274 break; 275 } 276 case PTRACE_DETACH: 277 error = kern_ptrace(td, PT_DETACH, pid, (void *)1, 278 map_signum(uap->data)); 279 break; 280 case PTRACE_SINGLESTEP: 281 case PTRACE_CONT: 282 error = kern_ptrace(td, req, pid, (void *)1, 283 map_signum(uap->data)); 284 break; 285 case PTRACE_ATTACH: 286 error = kern_ptrace(td, PT_ATTACH, pid, addr, uap->data); 287 break; 288 case PTRACE_GETREGS: 289 /* Linux is using data where FreeBSD is using addr */ 290 error = kern_ptrace(td, PT_GETREGS, pid, &u.bsd_reg, 0); 291 if (error == 0) { 292 map_regs_to_linux(&u.bsd_reg, &r.reg); 293 error = copyout(&r.reg, (void *)uap->data, 294 sizeof(r.reg)); 295 } 296 break; 297 case PTRACE_SETREGS: 298 /* Linux is using data where FreeBSD is using addr */ 299 error = copyin((void *)uap->data, &r.reg, sizeof(r.reg)); 300 if (error == 0) { 301 map_regs_from_linux(&u.bsd_reg, &r.reg); 302 error = kern_ptrace(td, PT_SETREGS, pid, &u.bsd_reg, 0); 303 } 304 break; 305 case PTRACE_GETFPREGS: 306 /* Linux is using data where FreeBSD is using addr */ 307 error = kern_ptrace(td, PT_GETFPREGS, pid, &u.bsd_fpreg, 0); 308 if (error == 0) { 309 map_fpregs_to_linux(&u.bsd_fpreg, &r.fpreg); 310 error = copyout(&r.fpreg, (void *)uap->data, 311 sizeof(r.fpreg)); 312 } 313 break; 314 case PTRACE_SETFPREGS: 315 /* Linux is using data where FreeBSD is using addr */ 316 error = copyin((void *)uap->data, &r.fpreg, sizeof(r.fpreg)); 317 if (error == 0) { 318 map_fpregs_from_linux(&u.bsd_fpreg, &r.fpreg); 319 error = kern_ptrace(td, PT_SETFPREGS, pid, 320 &u.bsd_fpreg, 0); 321 } 322 break; 323 case PTRACE_SETFPXREGS: 324 error = copyin((void *)uap->data, &r.fpxreg, sizeof(r.fpxreg)); 325 if (error) 326 break; 327 /* FALL THROUGH */ 328 case PTRACE_GETFPXREGS: { 329 struct proc *p; 330 struct thread *td2; 331 332 if (sizeof(struct linux_pt_fpxreg) != sizeof(struct savexmm)) { 333 static int once = 0; 334 if (!once) { 335 printf("linux: savexmm != linux_pt_fpxreg\n"); 336 once = 1; 337 } 338 error = EIO; 339 break; 340 } 341 342 if ((p = pfind(uap->pid)) == NULL) { 343 error = ESRCH; 344 break; 345 } 346 347 /* Exiting processes can't be debugged. */ 348 if ((p->p_flag & P_WEXIT) != 0) { 349 error = ESRCH; 350 goto fail; 351 } 352 353 if ((error = p_candebug(td, p)) != 0) 354 goto fail; 355 356 /* System processes can't be debugged. */ 357 if ((p->p_flag & P_SYSTEM) != 0) { 358 error = EINVAL; 359 goto fail; 360 } 361 362 /* not being traced... */ 363 if ((p->p_flag & P_TRACED) == 0) { 364 error = EPERM; 365 goto fail; 366 } 367 368 /* not being traced by YOU */ 369 if (p->p_pptr != td->td_proc) { 370 error = EBUSY; 371 goto fail; 372 } 373 374 /* not currently stopped */ 375 if (!P_SHOULDSTOP(p) || (p->p_flag & P_WAITED) == 0) { 376 error = EBUSY; 377 goto fail; 378 } 379 380 if (req == PTRACE_GETFPXREGS) { 381 _PHOLD(p); /* may block */ 382 td2 = FIRST_THREAD_IN_PROC(p); 383 error = linux_proc_read_fpxregs(td2, &r.fpxreg); 384 _PRELE(p); 385 PROC_UNLOCK(p); 386 if (error == 0) 387 error = copyout(&r.fpxreg, (void *)uap->data, 388 sizeof(r.fpxreg)); 389 } else { 390 /* clear dangerous bits exactly as Linux does*/ 391 r.fpxreg.mxcsr &= 0xffbf; 392 _PHOLD(p); /* may block */ 393 td2 = FIRST_THREAD_IN_PROC(p); 394 error = linux_proc_write_fpxregs(td2, &r.fpxreg); 395 _PRELE(p); 396 PROC_UNLOCK(p); 397 } 398 break; 399 400 fail: 401 PROC_UNLOCK(p); 402 break; 403 } 404 case PTRACE_PEEKUSR: 405 case PTRACE_POKEUSR: { 406 error = EIO; 407 408 /* check addr for alignment */ 409 if (uap->addr < 0 || uap->addr & (sizeof(l_int) - 1)) 410 break; 411 /* 412 * Allow Linux programs to access register values in 413 * user struct. We simulate this through PT_GET/SETREGS 414 * as necessary. 415 */ 416 if (uap->addr < sizeof(struct linux_pt_reg)) { 417 error = kern_ptrace(td, PT_GETREGS, pid, &u.bsd_reg, 0); 418 if (error != 0) 419 break; 420 421 map_regs_to_linux(&u.bsd_reg, &r.reg); 422 if (req == PTRACE_PEEKUSR) { 423 error = copyout((char *)&r.reg + uap->addr, 424 (void *)uap->data, sizeof(l_int)); 425 break; 426 } 427 428 *(l_int *)((char *)&r.reg + uap->addr) = 429 (l_int)uap->data; 430 431 map_regs_from_linux(&u.bsd_reg, &r.reg); 432 error = kern_ptrace(td, PT_SETREGS, pid, &u.bsd_reg, 0); 433 } 434 435 /* 436 * Simulate debug registers access 437 */ 438 if (uap->addr >= LINUX_DBREG_OFFSET && 439 uap->addr <= LINUX_DBREG_OFFSET + LINUX_DBREG_SIZE) { 440 error = kern_ptrace(td, PT_GETDBREGS, pid, &u.bsd_dbreg, 441 0); 442 if (error != 0) 443 break; 444 445 uap->addr -= LINUX_DBREG_OFFSET; 446 if (req == PTRACE_PEEKUSR) { 447 error = copyout((char *)&u.bsd_dbreg + 448 uap->addr, (void *)uap->data, 449 sizeof(l_int)); 450 break; 451 } 452 453 *(l_int *)((char *)&u.bsd_dbreg + uap->addr) = 454 uap->data; 455 error = kern_ptrace(td, PT_SETDBREGS, pid, 456 &u.bsd_dbreg, 0); 457 } 458 459 break; 460 } 461 case LINUX_PTRACE_SYSCALL: 462 /* fall through */ 463 default: 464 printf("linux: ptrace(%u, ...) not implemented\n", 465 (unsigned int)uap->req); 466 error = EINVAL; 467 break; 468 } 469 470 return (error); 471 } 472