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

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