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

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