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

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