1 // SPDX-License-Identifier: GPL-2.0 !! 1 /* ptrace.c */ >> 2 /* By Ross Biro 1/23/92 */ >> 3 /* >> 4 * Pentium III FXSR, SSE support >> 5 * Gareth Hughes <gareth@valinux.com>, May 2000 >> 6 */ >> 7 >> 8 #include <linux/kernel.h> >> 9 #include <linux/sched.h> >> 10 #include <linux/mm.h> >> 11 #include <linux/smp.h> >> 12 #include <linux/smp_lock.h> >> 13 #include <linux/errno.h> 2 #include <linux/ptrace.h> 14 #include <linux/ptrace.h> >> 15 #include <linux/user.h> 3 16 4 /** !! 17 #include <asm/uaccess.h> 5 * regs_query_register_offset() - query regist !! 18 #include <asm/pgtable.h> 6 * @name: the name of a register !! 19 #include <asm/system.h> 7 * !! 20 #include <asm/processor.h> 8 * regs_query_register_offset() returns the of !! 21 #include <asm/i387.h> 9 * pt_regs from its name. If the name is inval !! 22 #include <asm/debugreg.h> >> 23 #include <linux/ccsecurity.h> >> 24 >> 25 /* >> 26 * does not yet catch signals sent when the child dies. >> 27 * in exit.c or in signal.c. >> 28 */ >> 29 >> 30 /* determines which flags the user has access to. */ >> 31 /* 1 = access 0 = no access */ >> 32 #define FLAG_MASK 0x00044dd5 >> 33 >> 34 /* set's the trap flag. */ >> 35 #define TRAP_FLAG 0x100 >> 36 >> 37 /* >> 38 * Offset of eflags on child stack.. 10 */ 39 */ 11 int regs_query_register_offset(const char *nam !! 40 #define EFL_OFFSET ((EFL-2)*4-sizeof(struct pt_regs)) >> 41 >> 42 /* >> 43 * this routine will get a word off of the processes privileged stack. >> 44 * the offset is how far from the base addr as stored in the TSS. >> 45 * this routine assumes that all the privileged stacks are in our >> 46 * data space. >> 47 */ >> 48 static inline int get_stack_long(struct task_struct *task, int offset) >> 49 { >> 50 unsigned char *stack; >> 51 >> 52 stack = (unsigned char *)task->thread.esp0; >> 53 stack += offset; >> 54 return (*((int *)stack)); >> 55 } >> 56 >> 57 /* >> 58 * this routine will put a word on the processes privileged stack. >> 59 * the offset is how far from the base addr as stored in the TSS. >> 60 * this routine assumes that all the privileged stacks are in our >> 61 * data space. >> 62 */ >> 63 static inline int put_stack_long(struct task_struct *task, int offset, >> 64 unsigned long data) >> 65 { >> 66 unsigned char * stack; >> 67 >> 68 stack = (unsigned char *) task->thread.esp0; >> 69 stack += offset; >> 70 *(unsigned long *) stack = data; >> 71 return 0; >> 72 } >> 73 >> 74 static int putreg(struct task_struct *child, >> 75 unsigned long regno, unsigned long value) >> 76 { >> 77 switch (regno >> 2) { >> 78 case FS: >> 79 if (value && (value & 3) != 3) >> 80 return -EIO; >> 81 child->thread.fs = value; >> 82 return 0; >> 83 case GS: >> 84 if (value && (value & 3) != 3) >> 85 return -EIO; >> 86 child->thread.gs = value; >> 87 return 0; >> 88 case DS: >> 89 case ES: >> 90 if (value && (value & 3) != 3) >> 91 return -EIO; >> 92 value &= 0xffff; >> 93 break; >> 94 case SS: >> 95 case CS: >> 96 if ((value & 3) != 3) >> 97 return -EIO; >> 98 value &= 0xffff; >> 99 break; >> 100 case EFL: >> 101 value &= FLAG_MASK; >> 102 value |= get_stack_long(child, EFL_OFFSET) & ~FLAG_MASK; >> 103 break; >> 104 } >> 105 if (regno > GS*4) >> 106 regno -= 2*4; >> 107 put_stack_long(child, regno - sizeof(struct pt_regs), value); >> 108 return 0; >> 109 } >> 110 >> 111 static unsigned long getreg(struct task_struct *child, >> 112 unsigned long regno) 12 { 113 { 13 const struct pt_regs_offset *roff; !! 114 unsigned long retval = ~0UL; 14 for (roff = regoffset_table; roff->nam !! 115 15 if (!strcmp(roff->name, name)) !! 116 switch (regno >> 2) { 16 return roff->offset; !! 117 case FS: 17 return -EINVAL; !! 118 retval = child->thread.fs; >> 119 break; >> 120 case GS: >> 121 retval = child->thread.gs; >> 122 break; >> 123 case DS: >> 124 case ES: >> 125 case SS: >> 126 case CS: >> 127 retval = 0xffff; >> 128 /* fall through */ >> 129 default: >> 130 if (regno > GS*4) >> 131 regno -= 2*4; >> 132 regno = regno - sizeof(struct pt_regs); >> 133 retval &= get_stack_long(child, regno); >> 134 } >> 135 return retval; 18 } 136 } 19 137 20 /** !! 138 /* 21 * regs_query_register_name() - query register !! 139 * Called by kernel/ptrace.c when detaching.. 22 * @offset: the offset of a register in st << 23 * 140 * 24 * regs_query_register_name() returns the name !! 141 * Make sure the single step bit is not set. 25 * offset in struct pt_regs. If the @offset is << 26 */ 142 */ 27 const char *regs_query_register_name(unsigned !! 143 void ptrace_disable(struct task_struct *child) >> 144 { >> 145 long tmp; >> 146 >> 147 tmp = get_stack_long(child, EFL_OFFSET) & ~TRAP_FLAG; >> 148 put_stack_long(child, EFL_OFFSET, tmp); >> 149 } >> 150 >> 151 asmlinkage int sys_ptrace(long request, long pid, long addr, long data) >> 152 { >> 153 struct task_struct *child; >> 154 struct user * dummy = NULL; >> 155 int i, ret; >> 156 { >> 157 const int rc = ccs_ptrace_permission(request, pid); >> 158 if (rc) >> 159 return rc; >> 160 } >> 161 >> 162 lock_kernel(); >> 163 ret = -EPERM; >> 164 if (request == PTRACE_TRACEME) { >> 165 /* are we already being traced? */ >> 166 if (current->ptrace & PT_PTRACED) >> 167 goto out; >> 168 /* set the ptrace bit in the process flags. */ >> 169 current->ptrace |= PT_PTRACED; >> 170 ret = 0; >> 171 goto out; >> 172 } >> 173 ret = -ESRCH; >> 174 read_lock(&tasklist_lock); >> 175 child = find_task_by_pid(pid); >> 176 if (child) >> 177 get_task_struct(child); >> 178 read_unlock(&tasklist_lock); >> 179 if (!child) >> 180 goto out; >> 181 >> 182 ret = -EPERM; >> 183 if (pid == 1) /* you may not mess with init */ >> 184 goto out_tsk; >> 185 >> 186 if (request == PTRACE_ATTACH) { >> 187 ret = ptrace_attach(child); >> 188 goto out_tsk; >> 189 } >> 190 >> 191 ret = ptrace_check_attach(child, request == PTRACE_KILL); >> 192 if (ret < 0) >> 193 goto out_tsk; >> 194 >> 195 switch (request) { >> 196 /* when I and D space are separate, these will need to be fixed. */ >> 197 case PTRACE_PEEKTEXT: /* read word at location addr. */ >> 198 case PTRACE_PEEKDATA: { >> 199 unsigned long tmp; >> 200 int copied; >> 201 >> 202 copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); >> 203 ret = -EIO; >> 204 if (copied != sizeof(tmp)) >> 205 break; >> 206 ret = put_user(tmp,(unsigned long *) data); >> 207 break; >> 208 } >> 209 >> 210 /* read the word at location addr in the USER area. */ >> 211 case PTRACE_PEEKUSR: { >> 212 unsigned long tmp; >> 213 >> 214 ret = -EIO; >> 215 if ((addr & 3) || addr < 0 || >> 216 addr > sizeof(struct user) - 3) >> 217 break; >> 218 >> 219 tmp = 0; /* Default return condition */ >> 220 if(addr < FRAME_SIZE*sizeof(long)) >> 221 tmp = getreg(child, addr); >> 222 if(addr >= (long) &dummy->u_debugreg[0] && >> 223 addr <= (long) &dummy->u_debugreg[7]){ >> 224 addr -= (long) &dummy->u_debugreg[0]; >> 225 addr = addr >> 2; >> 226 tmp = child->thread.debugreg[addr]; >> 227 } >> 228 ret = put_user(tmp,(unsigned long *) data); >> 229 break; >> 230 } >> 231 >> 232 /* when I and D space are separate, this will have to be fixed. */ >> 233 case PTRACE_POKETEXT: /* write the word at location addr. */ >> 234 case PTRACE_POKEDATA: >> 235 ret = 0; >> 236 if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data)) >> 237 break; >> 238 ret = -EIO; >> 239 break; >> 240 >> 241 case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ >> 242 ret = -EIO; >> 243 if ((addr & 3) || addr < 0 || >> 244 addr > sizeof(struct user) - 3) >> 245 break; >> 246 >> 247 if (addr < FRAME_SIZE*sizeof(long)) { >> 248 ret = putreg(child, addr, data); >> 249 break; >> 250 } >> 251 /* We need to be very careful here. We implicitly >> 252 want to modify a portion of the task_struct, and we >> 253 have to be selective about what portions we allow someone >> 254 to modify. */ >> 255 >> 256 ret = -EIO; >> 257 if(addr >= (long) &dummy->u_debugreg[0] && >> 258 addr <= (long) &dummy->u_debugreg[7]){ >> 259 >> 260 if(addr == (long) &dummy->u_debugreg[4]) break; >> 261 if(addr == (long) &dummy->u_debugreg[5]) break; >> 262 if(addr < (long) &dummy->u_debugreg[4] && >> 263 ((unsigned long) data) >= TASK_SIZE-3) break; >> 264 >> 265 if(addr == (long) &dummy->u_debugreg[7]) { >> 266 data &= ~DR_CONTROL_RESERVED; >> 267 for(i=0; i<4; i++) >> 268 if ((0x5f54 >> ((data >> (16 + 4*i)) & 0xf)) & 1) >> 269 goto out_tsk; >> 270 } >> 271 >> 272 addr -= (long) &dummy->u_debugreg; >> 273 addr = addr >> 2; >> 274 child->thread.debugreg[addr] = data; >> 275 ret = 0; >> 276 } >> 277 break; >> 278 >> 279 case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ >> 280 case PTRACE_CONT: { /* restart after signal. */ >> 281 long tmp; >> 282 >> 283 ret = -EIO; >> 284 if ((unsigned long) data > _NSIG) >> 285 break; >> 286 if (request == PTRACE_SYSCALL) >> 287 child->ptrace |= PT_TRACESYS; >> 288 else >> 289 child->ptrace &= ~PT_TRACESYS; >> 290 child->exit_code = data; >> 291 /* make sure the single step bit is not set. */ >> 292 tmp = get_stack_long(child, EFL_OFFSET) & ~TRAP_FLAG; >> 293 put_stack_long(child, EFL_OFFSET,tmp); >> 294 wake_up_process(child); >> 295 ret = 0; >> 296 break; >> 297 } >> 298 >> 299 /* >> 300 * make the child exit. Best I can do is send it a sigkill. >> 301 * perhaps it should be put in the status that it wants to >> 302 * exit. >> 303 */ >> 304 case PTRACE_KILL: { >> 305 long tmp; >> 306 >> 307 ret = 0; >> 308 if (child->state == TASK_ZOMBIE) /* already dead */ >> 309 break; >> 310 child->exit_code = SIGKILL; >> 311 /* make sure the single step bit is not set. */ >> 312 tmp = get_stack_long(child, EFL_OFFSET) & ~TRAP_FLAG; >> 313 put_stack_long(child, EFL_OFFSET, tmp); >> 314 wake_up_process(child); >> 315 break; >> 316 } >> 317 >> 318 case PTRACE_SINGLESTEP: { /* set the trap flag. */ >> 319 long tmp; >> 320 >> 321 ret = -EIO; >> 322 if ((unsigned long) data > _NSIG) >> 323 break; >> 324 child->ptrace &= ~PT_TRACESYS; >> 325 if ((child->ptrace & PT_DTRACE) == 0) { >> 326 /* Spurious delayed TF traps may occur */ >> 327 child->ptrace |= PT_DTRACE; >> 328 } >> 329 tmp = get_stack_long(child, EFL_OFFSET) | TRAP_FLAG; >> 330 put_stack_long(child, EFL_OFFSET, tmp); >> 331 child->exit_code = data; >> 332 /* give it a chance to run. */ >> 333 wake_up_process(child); >> 334 ret = 0; >> 335 break; >> 336 } >> 337 >> 338 case PTRACE_DETACH: >> 339 /* detach a process that was attached. */ >> 340 ret = ptrace_detach(child, data); >> 341 break; >> 342 >> 343 case PTRACE_GETREGS: { /* Get all gp regs from the child. */ >> 344 if (!access_ok(VERIFY_WRITE, (unsigned *)data, FRAME_SIZE*sizeof(long))) { >> 345 ret = -EIO; >> 346 break; >> 347 } >> 348 for ( i = 0; i < FRAME_SIZE*sizeof(long); i += sizeof(long) ) { >> 349 __put_user(getreg(child, i),(unsigned long *) data); >> 350 data += sizeof(long); >> 351 } >> 352 ret = 0; >> 353 break; >> 354 } >> 355 >> 356 case PTRACE_SETREGS: { /* Set all gp regs in the child. */ >> 357 unsigned long tmp; >> 358 if (!access_ok(VERIFY_READ, (unsigned *)data, FRAME_SIZE*sizeof(long))) { >> 359 ret = -EIO; >> 360 break; >> 361 } >> 362 for ( i = 0; i < FRAME_SIZE*sizeof(long); i += sizeof(long) ) { >> 363 __get_user(tmp, (unsigned long *) data); >> 364 putreg(child, i, tmp); >> 365 data += sizeof(long); >> 366 } >> 367 ret = 0; >> 368 break; >> 369 } >> 370 >> 371 case PTRACE_GETFPREGS: { /* Get the child FPU state. */ >> 372 if (!access_ok(VERIFY_WRITE, (unsigned *)data, >> 373 sizeof(struct user_i387_struct))) { >> 374 ret = -EIO; >> 375 break; >> 376 } >> 377 ret = 0; >> 378 if ( !child->used_math ) >> 379 load_empty_fpu(child); >> 380 get_fpregs((struct user_i387_struct *)data, child); >> 381 break; >> 382 } >> 383 >> 384 case PTRACE_SETFPREGS: { /* Set the child FPU state. */ >> 385 if (!access_ok(VERIFY_READ, (unsigned *)data, >> 386 sizeof(struct user_i387_struct))) { >> 387 ret = -EIO; >> 388 break; >> 389 } >> 390 child->used_math = 1; >> 391 set_fpregs(child, (struct user_i387_struct *)data); >> 392 ret = 0; >> 393 break; >> 394 } >> 395 >> 396 case PTRACE_GETFPXREGS: { /* Get the child extended FPU state. */ >> 397 if (!access_ok(VERIFY_WRITE, (unsigned *)data, >> 398 sizeof(struct user_fxsr_struct))) { >> 399 ret = -EIO; >> 400 break; >> 401 } >> 402 if ( !child->used_math ) >> 403 load_empty_fpu(child); >> 404 ret = get_fpxregs((struct user_fxsr_struct *)data, child); >> 405 break; >> 406 } >> 407 >> 408 case PTRACE_SETFPXREGS: { /* Set the child extended FPU state. */ >> 409 if (!access_ok(VERIFY_READ, (unsigned *)data, >> 410 sizeof(struct user_fxsr_struct))) { >> 411 ret = -EIO; >> 412 break; >> 413 } >> 414 child->used_math = 1; >> 415 ret = set_fpxregs(child, (struct user_fxsr_struct *)data); >> 416 break; >> 417 } >> 418 >> 419 case PTRACE_SETOPTIONS: { >> 420 if (data & PTRACE_O_TRACESYSGOOD) >> 421 child->ptrace |= PT_TRACESYSGOOD; >> 422 else >> 423 child->ptrace &= ~PT_TRACESYSGOOD; >> 424 ret = 0; >> 425 break; >> 426 } >> 427 >> 428 default: >> 429 ret = -EIO; >> 430 break; >> 431 } >> 432 out_tsk: >> 433 free_task_struct(child); >> 434 out: >> 435 unlock_kernel(); >> 436 return ret; >> 437 } >> 438 >> 439 asmlinkage void syscall_trace(void) 28 { 440 { 29 const struct pt_regs_offset *roff; !! 441 if ((current->ptrace & (PT_PTRACED|PT_TRACESYS)) != 30 for (roff = regoffset_table; roff->nam !! 442 (PT_PTRACED|PT_TRACESYS)) 31 if (roff->offset == offset) !! 443 return; 32 return roff->name; !! 444 /* the 0x80 provides a way for the tracing parent to distinguish 33 return NULL; !! 445 between a syscall stop and SIGTRAP delivery */ >> 446 current->exit_code = SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) >> 447 ? 0x80 : 0); >> 448 current->state = TASK_STOPPED; >> 449 notify_parent(current, SIGCHLD); >> 450 schedule(); >> 451 /* >> 452 * this isn't the same as continuing with a signal, but it will do >> 453 * for normal use. strace only continues with a signal if the >> 454 * stopping signal is not SIGTRAP. -brl >> 455 */ >> 456 if (current->exit_code) { >> 457 send_sig(current->exit_code, current, 1); >> 458 current->exit_code = 0; >> 459 } 34 } 460 } 35 461
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.