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

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