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

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