root/kernel/ptrace.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. get_task
  2. get_stack_long
  3. put_stack_long
  4. get_long
  5. put_long
  6. read_long
  7. write_long
  8. sys_ptrace
  9. syscall_trace

   1 /* ptrace.c */
   2 /* By Ross Biro 1/23/92 */
   3 /* edited by Linus Torvalds */
   4 
   5 #include <linux/head.h>
   6 #include <linux/kernel.h>
   7 #include <linux/sched.h>
   8 #include <linux/mm.h>
   9 #include <linux/errno.h>
  10 #include <linux/ptrace.h>
  11 #include <linux/user.h>
  12 
  13 #include <asm/segment.h>
  14 #include <asm/system.h>
  15 #include <linux/debugreg.h>
  16 
  17 /*
  18  * does not yet catch signals sent when the child dies.
  19  * in exit.c or in signal.c.
  20  */
  21 
  22 /* determines which flags the user has access to. */
  23 /* 1 = access 0 = no access */
  24 #define FLAG_MASK 0x00044dd5
  25 
  26 /* set's the trap flag. */
  27 #define TRAP_FLAG 0x100
  28 
  29 /*
  30  * this is the number to subtract from the top of the stack. To find
  31  * the local frame.
  32  */
  33 #define MAGICNUMBER 68
  34 
  35 /* change a pid into a task struct. */
  36 static inline struct task_struct * get_task(int pid)
     /* [previous][next][first][last][top][bottom][index][help] */
  37 {
  38         int i;
  39 
  40         for (i = 1; i < NR_TASKS; i++) {
  41                 if (task[i] != NULL && (task[i]->pid == pid))
  42                         return task[i];
  43         }
  44         return NULL;
  45 }
  46 
  47 /*
  48  * this routine will get a word off of the processes priviledged stack. 
  49  * the offset is how far from the base addr as stored in the TSS.  
  50  * this routine assumes that all the priviledged stacks are in our
  51  * data space.
  52  */   
  53 static inline int get_stack_long(struct task_struct *task, int offset)
     /* [previous][next][first][last][top][bottom][index][help] */
  54 {
  55         unsigned char *stack;
  56 
  57         stack = (unsigned char *)task->tss.esp0;
  58         stack += offset;
  59         return (*((int *)stack));
  60 }
  61 
  62 /*
  63  * this routine will put a word on the processes priviledged stack. 
  64  * the offset is how far from the base addr as stored in the TSS.  
  65  * this routine assumes that all the priviledged stacks are in our
  66  * data space.
  67  */
  68 static inline int put_stack_long(struct task_struct *task, int offset,
     /* [previous][next][first][last][top][bottom][index][help] */
  69         unsigned long data)
  70 {
  71         unsigned char * stack;
  72 
  73         stack = (unsigned char *) task->tss.esp0;
  74         stack += offset;
  75         *(unsigned long *) stack = data;
  76         return 0;
  77 }
  78 
  79 /*
  80  * This routine gets a long from any process space by following the page
  81  * tables. NOTE! You should check that the long isn't on a page boundary,
  82  * and that it is in the task area before calling this: this routine does
  83  * no checking.
  84  *
  85  * NOTE2! This uses "tsk->tss.cr3" even though we know it's currently always
  86  * zero. This routine shouldn't have to change when we make a better mm.
  87  */
  88 static unsigned long get_long(struct task_struct * tsk,
     /* [previous][next][first][last][top][bottom][index][help] */
  89         unsigned long addr)
  90 {
  91         unsigned long page;
  92 
  93 repeat:
  94         page = *PAGE_DIR_OFFSET(tsk->tss.cr3,addr);
  95         if (page & PAGE_PRESENT) {
  96                 page &= PAGE_MASK;
  97                 page += PAGE_PTR(addr);
  98                 page = *((unsigned long *) page);
  99         }
 100         if (!(page & PAGE_PRESENT)) {
 101                 do_no_page(0,addr,tsk,0);
 102                 goto repeat;
 103         }
 104 /* this is a hack for non-kernel-mapped video buffers and similar */
 105         if (page >= high_memory)
 106                 return 0;
 107         page &= PAGE_MASK;
 108         page += addr & ~PAGE_MASK;
 109         return *(unsigned long *) page;
 110 }
 111 
 112 /*
 113  * This routine puts a long into any process space by following the page
 114  * tables. NOTE! You should check that the long isn't on a page boundary,
 115  * and that it is in the task area before calling this: this routine does
 116  * no checking.
 117  *
 118  * Now keeps R/W state of page so that a text page stays readonly
 119  * even if a debugger scribbles breakpoints into it.  -M.U-
 120  */
 121 static void put_long(struct task_struct * tsk, unsigned long addr,
     /* [previous][next][first][last][top][bottom][index][help] */
 122         unsigned long data)
 123 {
 124         unsigned long page, pte = 0;
 125         int readonly = 0;
 126 
 127 repeat:
 128         page = *PAGE_DIR_OFFSET(tsk->tss.cr3,addr);
 129         if (page & PAGE_PRESENT) {
 130                 page &= PAGE_MASK;
 131                 page += PAGE_PTR(addr);
 132                 pte = page;
 133                 page = *((unsigned long *) page);
 134         }
 135         if (!(page & PAGE_PRESENT)) {
 136                 do_no_page(0 /* PAGE_RW */ ,addr,tsk,0);
 137                 goto repeat;
 138         }
 139         if (!(page & PAGE_RW)) {
 140                 if(!(page & PAGE_COW))
 141                         readonly = 1;
 142                 do_wp_page(PAGE_RW | PAGE_PRESENT,addr,tsk,0);
 143                 goto repeat;
 144         }
 145 /* this is a hack for non-kernel-mapped video buffers and similar */
 146         if (page >= high_memory)
 147                 return;
 148 /* we're bypassing pagetables, so we have to set the dirty bit ourselves */
 149         *(unsigned long *) pte |= (PAGE_DIRTY|PAGE_COW);
 150         page &= PAGE_MASK;
 151         page += addr & ~PAGE_MASK;
 152         *(unsigned long *) page = data;
 153         if(readonly) {
 154                 *(unsigned long *) pte &=~ (PAGE_RW|PAGE_COW);
 155                 invalidate();
 156         } 
 157 }
 158 
 159 /*
 160  * This routine checks the page boundaries, and that the offset is
 161  * within the task area. It then calls get_long() to read a long.
 162  */
 163 static int read_long(struct task_struct * tsk, unsigned long addr,
     /* [previous][next][first][last][top][bottom][index][help] */
 164         unsigned long * result)
 165 {
 166         unsigned long low,high;
 167 
 168         if (addr > TASK_SIZE-sizeof(long))
 169                 return -EIO;
 170         if ((addr & ~PAGE_MASK) > PAGE_SIZE-sizeof(long)) {
 171                 low = get_long(tsk,addr & ~(sizeof(long)-1));
 172                 high = get_long(tsk,(addr+sizeof(long)) & ~(sizeof(long)-1));
 173                 switch (addr & (sizeof(long)-1)) {
 174                         case 1:
 175                                 low >>= 8;
 176                                 low |= high << 24;
 177                                 break;
 178                         case 2:
 179                                 low >>= 16;
 180                                 low |= high << 16;
 181                                 break;
 182                         case 3:
 183                                 low >>= 24;
 184                                 low |= high << 8;
 185                                 break;
 186                 }
 187                 *result = low;
 188         } else
 189                 *result = get_long(tsk,addr);
 190         return 0;
 191 }
 192 
 193 /*
 194  * This routine checks the page boundaries, and that the offset is
 195  * within the task area. It then calls put_long() to write a long.
 196  */
 197 static int write_long(struct task_struct * tsk, unsigned long addr,
     /* [previous][next][first][last][top][bottom][index][help] */
 198         unsigned long data)
 199 {
 200         unsigned long low,high;
 201 
 202         if (addr > TASK_SIZE-sizeof(long))
 203                 return -EIO;
 204         if ((addr & ~PAGE_MASK) > PAGE_SIZE-sizeof(long)) {
 205                 low = get_long(tsk,addr & ~(sizeof(long)-1));
 206                 high = get_long(tsk,(addr+sizeof(long)) & ~(sizeof(long)-1));
 207                 switch (addr & (sizeof(long)-1)) {
 208                         case 0: /* shouldn't happen, but safety first */
 209                                 low = data;
 210                                 break;
 211                         case 1:
 212                                 low &= 0x000000ff;
 213                                 low |= data << 8;
 214                                 high &= ~0xff;
 215                                 high |= data >> 24;
 216                                 break;
 217                         case 2:
 218                                 low &= 0x0000ffff;
 219                                 low |= data << 16;
 220                                 high &= ~0xffff;
 221                                 high |= data >> 16;
 222                                 break;
 223                         case 3:
 224                                 low &= 0x00ffffff;
 225                                 low |= data << 24;
 226                                 high &= ~0xffffff;
 227                                 high |= data >> 8;
 228                                 break;
 229                 }
 230                 put_long(tsk,addr & ~(sizeof(long)-1),low);
 231                 put_long(tsk,(addr+sizeof(long)) & ~(sizeof(long)-1),high);
 232         } else
 233                 put_long(tsk,addr,data);
 234         return 0;
 235 }
 236 
 237 asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
     /* [previous][next][first][last][top][bottom][index][help] */
 238 {
 239         struct task_struct *child;
 240         struct user * dummy;
 241         int i;
 242 
 243         dummy = NULL;
 244 
 245         if (request == PTRACE_TRACEME) {
 246                 /* are we already being traced? */
 247                 if (current->flags & PF_PTRACED)
 248                         return -EPERM;
 249                 /* set the ptrace bit in the proccess flags. */
 250                 current->flags |= PF_PTRACED;
 251                 return 0;
 252         }
 253         if (pid == 1)           /* you may not mess with init */
 254                 return -EPERM;
 255         if (!(child = get_task(pid)))
 256                 return -ESRCH;
 257         if (request == PTRACE_ATTACH) {
 258                 if (child == current)
 259                         return -EPERM;
 260                 if ((!child->dumpable || (current->uid != child->euid) ||
 261                     (current->gid != child->egid)) && !suser())
 262                         return -EPERM;
 263                 /* the same process cannot be attached many times */
 264                 if (child->flags & PF_PTRACED)
 265                         return -EPERM;
 266                 child->flags |= PF_PTRACED;
 267                 if (child->p_pptr != current) {
 268                         REMOVE_LINKS(child);
 269                         child->p_pptr = current;
 270                         SET_LINKS(child);
 271                 }
 272                 send_sig(SIGSTOP, child, 1);
 273                 return 0;
 274         }
 275         if (!(child->flags & PF_PTRACED))
 276                 return -ESRCH;
 277         if (child->state != TASK_STOPPED) {
 278                 if (request != PTRACE_KILL)
 279                         return -ESRCH;
 280         }
 281         if (child->p_pptr != current)
 282                 return -ESRCH;
 283 
 284         switch (request) {
 285         /* when I and D space are seperate, these will need to be fixed. */
 286                 case PTRACE_PEEKTEXT: /* read word at location addr. */ 
 287                 case PTRACE_PEEKDATA: {
 288                         unsigned long tmp;
 289                         int res;
 290 
 291                         res = read_long(child, addr, &tmp);
 292                         if (res < 0)
 293                                 return res;
 294                         res = verify_area(VERIFY_WRITE, (void *) data, sizeof(long));
 295                         if (!res)
 296                                 put_fs_long(tmp,(unsigned long *) data);
 297                         return res;
 298                 }
 299 
 300         /* read the word at location addr in the USER area. */
 301                 case PTRACE_PEEKUSR: {
 302                         unsigned long tmp;
 303                         int res;
 304 
 305                         if ((addr & 3) || addr < 0 || 
 306                             addr > sizeof(struct user) - 3)
 307                                 return -EIO;
 308 
 309                         res = verify_area(VERIFY_WRITE, (void *) data, sizeof(long));
 310                         if (res)
 311                                 return res;
 312                         tmp = 0;  /* Default return condition */
 313                         if(addr < 17*sizeof(long)) {
 314                           addr = addr >> 2; /* temporary hack. */
 315 
 316                           tmp = get_stack_long(child, sizeof(long)*addr - MAGICNUMBER);
 317                           if (addr == DS || addr == ES ||
 318                               addr == FS || addr == GS ||
 319                               addr == CS || addr == SS)
 320                             tmp &= 0xffff;
 321                         };
 322                         if(addr >= (long) &dummy->u_debugreg[0] &&
 323                            addr <= (long) &dummy->u_debugreg[7]){
 324                                 addr -= (long) &dummy->u_debugreg[0];
 325                                 addr = addr >> 2;
 326                                 tmp = child->debugreg[addr];
 327                         };
 328                         put_fs_long(tmp,(unsigned long *) data);
 329                         return 0;
 330                 }
 331 
 332       /* when I and D space are seperate, this will have to be fixed. */
 333                 case PTRACE_POKETEXT: /* write the word at location addr. */
 334                 case PTRACE_POKEDATA:
 335                         return write_long(child,addr,data);
 336 
 337                 case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
 338                         if ((addr & 3) || addr < 0 || 
 339                             addr > sizeof(struct user) - 3)
 340                                 return -EIO;
 341 
 342                         addr = addr >> 2; /* temproary hack. */
 343 
 344                         if (addr == ORIG_EAX)
 345                                 return -EIO;
 346                         if (addr == DS || addr == ES ||
 347                             addr == FS || addr == GS ||
 348                             addr == CS || addr == SS) {
 349                                 data &= 0xffff;
 350                                 if (data && (data & 3) != 3)
 351                                         return -EIO;
 352                         }
 353                         if (addr == EFL) {   /* flags. */
 354                                 data &= FLAG_MASK;
 355                                 data |= get_stack_long(child, EFL*sizeof(long)-MAGICNUMBER)  & ~FLAG_MASK;
 356                         }
 357                   /* Do not allow the user to set the debug register for kernel
 358                      address space */
 359                   if(addr < 17){
 360                           if (put_stack_long(child, sizeof(long)*addr-MAGICNUMBER, data))
 361                                 return -EIO;
 362                         return 0;
 363                         };
 364 
 365                   /* We need to be very careful here.  We implicitly
 366                      want to modify a portion of the task_struct, and we
 367                      have to be selective about what portions we allow someone
 368                      to modify. */
 369 
 370                   addr = addr << 2;  /* Convert back again */
 371                   if(addr >= (long) &dummy->u_debugreg[0] &&
 372                      addr <= (long) &dummy->u_debugreg[7]){
 373 
 374                           if(addr == (long) &dummy->u_debugreg[4]) return -EIO;
 375                           if(addr == (long) &dummy->u_debugreg[5]) return -EIO;
 376                           if(addr < (long) &dummy->u_debugreg[4] &&
 377                              ((unsigned long) data) >= 0xbffffffd) return -EIO;
 378                           
 379                           if(addr == (long) &dummy->u_debugreg[7]) {
 380                                   data &= ~DR_CONTROL_RESERVED;
 381                                   for(i=0; i<4; i++)
 382                                           if ((0x5f54 >> ((data >> (16 + 4*i)) & 0xf)) & 1)
 383                                                   return -EIO;
 384                           };
 385 
 386                           addr -= (long) &dummy->u_debugreg;
 387                           addr = addr >> 2;
 388                           child->debugreg[addr] = data;
 389                           return 0;
 390                   };
 391                   return -EIO;
 392 
 393                 case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
 394                 case PTRACE_CONT: { /* restart after signal. */
 395                         long tmp;
 396 
 397                         if ((unsigned long) data > NSIG)
 398                                 return -EIO;
 399                         if (request == PTRACE_SYSCALL)
 400                                 child->flags |= PF_TRACESYS;
 401                         else
 402                                 child->flags &= ~PF_TRACESYS;
 403                         child->exit_code = data;
 404                         child->state = TASK_RUNNING;
 405         /* make sure the single step bit is not set. */
 406                         tmp = get_stack_long(child, sizeof(long)*EFL-MAGICNUMBER) & ~TRAP_FLAG;
 407                         put_stack_long(child, sizeof(long)*EFL-MAGICNUMBER,tmp);
 408                         return 0;
 409                 }
 410 
 411 /*
 412  * make the child exit.  Best I can do is send it a sigkill. 
 413  * perhaps it should be put in the status that it want's to 
 414  * exit.
 415  */
 416                 case PTRACE_KILL: {
 417                         long tmp;
 418 
 419                         child->state = TASK_RUNNING;
 420                         child->exit_code = SIGKILL;
 421         /* make sure the single step bit is not set. */
 422                         tmp = get_stack_long(child, sizeof(long)*EFL-MAGICNUMBER) & ~TRAP_FLAG;
 423                         put_stack_long(child, sizeof(long)*EFL-MAGICNUMBER,tmp);
 424                         return 0;
 425                 }
 426 
 427                 case PTRACE_SINGLESTEP: {  /* set the trap flag. */
 428                         long tmp;
 429 
 430                         if ((unsigned long) data > NSIG)
 431                                 return -EIO;
 432                         child->flags &= ~PF_TRACESYS;
 433                         tmp = get_stack_long(child, sizeof(long)*EFL-MAGICNUMBER) | TRAP_FLAG;
 434                         put_stack_long(child, sizeof(long)*EFL-MAGICNUMBER,tmp);
 435                         child->state = TASK_RUNNING;
 436                         child->exit_code = data;
 437         /* give it a chance to run. */
 438                         return 0;
 439                 }
 440 
 441                 case PTRACE_DETACH: { /* detach a process that was attached. */
 442                         long tmp;
 443 
 444                         if ((unsigned long) data > NSIG)
 445                                 return -EIO;
 446                         child->flags &= ~(PF_PTRACED|PF_TRACESYS);
 447                         child->state = TASK_RUNNING;
 448                         child->exit_code = data;
 449                         REMOVE_LINKS(child);
 450                         child->p_pptr = child->p_opptr;
 451                         SET_LINKS(child);
 452                         /* make sure the single step bit is not set. */
 453                         tmp = get_stack_long(child, sizeof(long)*EFL-MAGICNUMBER) & ~TRAP_FLAG;
 454                         put_stack_long(child, sizeof(long)*EFL-MAGICNUMBER,tmp);
 455                         return 0;
 456                 }
 457 
 458                 default:
 459                         return -EIO;
 460         }
 461 }
 462 
 463 asmlinkage void syscall_trace(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 464 {
 465         if ((current->flags & (PF_PTRACED|PF_TRACESYS))
 466                         != (PF_PTRACED|PF_TRACESYS))
 467                 return;
 468         current->exit_code = SIGTRAP;
 469         current->state = TASK_STOPPED;
 470         notify_parent(current);
 471         schedule();
 472         /*
 473          * this isn't the same as continuing with a signal, but it will do
 474          * for normal use.  strace only continues with a signal if the
 475          * stopping signal is not SIGTRAP.  -brl
 476          */
 477         if (current->exit_code)
 478                 current->signal |= (1 << (current->exit_code - 1));
 479         current->exit_code = 0;
 480 }

/* [previous][next][first][last][top][bottom][index][help] */