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

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