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

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