root/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. read_long
  7. write_long
  8. sys_ptrace

   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 
  12 #include <asm/segment.h>
  13 #include <asm/system.h>
  14 
  15 /*
  16  * does not yet catch signals sent when the child dies.
  17  * in exit.c or in signal.c.
  18  */
  19 
  20 /* determines which flags the user has access to. */
  21 /* 1 = access 0 = no access */
  22 #define FLAG_MASK 0x00000dd9
  23 
  24 /* set's the trap flag. */
  25 #define TRAP_FLAG 0x100
  26 
  27 /*
  28  * this is the number to subtract from the top of the stack. To find
  29  * the local frame.
  30  */
  31 #define MAGICNUMBER 68
  32 
  33 /* change a pid into a task struct. */
  34 static inline struct task_struct * get_task(int pid)
     /* [previous][next][first][last][top][bottom][index][help] */
  35 {
  36         int i;
  37 
  38         for (i = 1; i < NR_TASKS; i++) {
  39                 if (task[i] != NULL && (task[i]->pid == pid))
  40                         return task[i];
  41         }
  42         return NULL;
  43 }
  44 
  45 /*
  46  * this routine will get a word off of the processes priviledged stack. 
  47  * the offset is how far from the base addr as stored in the TSS.  
  48  * this routine assumes that all the priviledged stacks are in our
  49  * data space.
  50  */   
  51 static inline int get_stack_long(struct task_struct *task, int offset)
     /* [previous][next][first][last][top][bottom][index][help] */
  52 {
  53         unsigned char *stack;
  54 
  55         stack = (unsigned char *)task->tss.esp0;
  56         stack += offset;
  57         return (*((int *)stack));
  58 }
  59 
  60 /*
  61  * this routine will put a word on the processes priviledged stack. 
  62  * the offset is how far from the base addr as stored in the TSS.  
  63  * this routine assumes that all the priviledged stacks are in our
  64  * data space.
  65  */
  66 static inline int put_stack_long(struct task_struct *task, int offset,
     /* [previous][next][first][last][top][bottom][index][help] */
  67         unsigned long data)
  68 {
  69         unsigned char * stack;
  70 
  71         stack = (unsigned char *) task->tss.esp0;
  72         stack += offset;
  73         *(unsigned long *) stack = data;
  74         return 0;
  75 }
  76 
  77 /*
  78  * This routine gets a long from any process space by following the page
  79  * tables. NOTE! You should check that the long isn't on a page boundary,
  80  * and that it is in the task area before calling this: this routine does
  81  * no checking.
  82  *
  83  * NOTE2! This uses "tsk->tss.cr3" even though we know it's currently always
  84  * zero. This routine shouldn't have to change when we make a better mm.
  85  */
  86 static unsigned long get_long(struct task_struct * tsk,
     /* [previous][next][first][last][top][bottom][index][help] */
  87         unsigned long addr)
  88 {
  89         unsigned long page;
  90 
  91 repeat:
  92         page = tsk->tss.cr3 + ((addr >> 20) & 0xffc);
  93         page = *(unsigned long *) page;
  94         if (page & PAGE_PRESENT) {
  95                 page &= 0xfffff000;
  96                 page += (addr >> 10) & 0xffc;
  97                 page = *((unsigned long *) page);
  98         }
  99         if (!(page & PAGE_PRESENT)) {
 100                 do_no_page(0,addr,tsk,0);
 101                 goto repeat;
 102         }
 103         page &= 0xfffff000;
 104         page += addr & 0xfff;
 105         return *(unsigned long *) page;
 106 }
 107 
 108 /*
 109  * This routine puts a long into any process space by following the page
 110  * tables. NOTE! You should check that the long isn't on a page boundary,
 111  * and that it is in the task area before calling this: this routine does
 112  * no checking.
 113  */
 114 static void put_long(struct task_struct * tsk, unsigned long addr,
     /* [previous][next][first][last][top][bottom][index][help] */
 115         unsigned long data)
 116 {
 117         unsigned long page;
 118 
 119 repeat:
 120         page = tsk->tss.cr3 + ((addr >> 20) & 0xffc);
 121         page = *(unsigned long *) page;
 122         if (page & PAGE_PRESENT) {
 123                 page &= 0xfffff000;
 124                 page += (addr >> 10) & 0xffc;
 125 /* we're bypassing pagetables, so we have to set the dirty bit ourselves */
 126                 *(unsigned long *) page |= PAGE_DIRTY;
 127                 page = *((unsigned long *) page);
 128         }
 129         if (!(page & PAGE_PRESENT)) {
 130                 do_no_page(0,addr,tsk,0);
 131                 goto repeat;
 132         }
 133         if (!(page & PAGE_RW)) {
 134                 do_wp_page(0,addr,tsk,0);
 135                 goto repeat;
 136         }
 137         page &= 0xfffff000;
 138         page += addr & 0xfff;
 139         *(unsigned long *) page = data;
 140 }
 141 
 142 /*
 143  * This routine checks the page boundaries, and that the offset is
 144  * within the task area. It then calls get_long() to read a long.
 145  */
 146 static int read_long(struct task_struct * tsk, unsigned long addr,
     /* [previous][next][first][last][top][bottom][index][help] */
 147         unsigned long * result)
 148 {
 149         unsigned long low,high;
 150 
 151         if (addr > TASK_SIZE-4)
 152                 return -EIO;
 153         if ((addr & 0xfff) > PAGE_SIZE-4) {
 154                 low = get_long(tsk,addr & 0xfffffffc);
 155                 high = get_long(tsk,(addr+4) & 0xfffffffc);
 156                 switch (addr & 3) {
 157                         case 1:
 158                                 low >>= 8;
 159                                 low |= high << 24;
 160                                 break;
 161                         case 2:
 162                                 low >>= 16;
 163                                 low |= high << 16;
 164                                 break;
 165                         case 3:
 166                                 low >>= 24;
 167                                 low |= high << 8;
 168                                 break;
 169                 }
 170                 *result = low;
 171         } else
 172                 *result = get_long(tsk,addr);
 173         return 0;
 174 }
 175 
 176 /*
 177  * This routine checks the page boundaries, and that the offset is
 178  * within the task area. It then calls put_long() to write a long.
 179  */
 180 static int write_long(struct task_struct * tsk, unsigned long addr,
     /* [previous][next][first][last][top][bottom][index][help] */
 181         unsigned long data)
 182 {
 183         unsigned long low,high;
 184 
 185         if (addr > TASK_SIZE-4)
 186                 return -EIO;
 187         if ((addr & 0xfff) > PAGE_SIZE-4) {
 188                 low = get_long(tsk,addr & 0xfffffffc);
 189                 high = get_long(tsk,(addr+4) & 0xfffffffc);
 190                 switch (addr & 3) {
 191                         case 0: /* shouldn't happen, but safety first */
 192                                 low = data;
 193                                 break;
 194                         case 1:
 195                                 low &= 0x000000ff;
 196                                 low |= data << 8;
 197                                 high &= 0xffffff00;
 198                                 high |= data >> 24;
 199                                 break;
 200                         case 2:
 201                                 low &= 0x0000ffff;
 202                                 low |= data << 16;
 203                                 high &= 0xffff0000;
 204                                 high |= data >> 16;
 205                                 break;
 206                         case 3:
 207                                 low &= 0x00ffffff;
 208                                 low |= data << 24;
 209                                 high &= 0xff000000;
 210                                 high |= data >> 8;
 211                                 break;
 212                 }
 213                 put_long(tsk,addr & 0xfffffffc,low);
 214                 put_long(tsk,(addr+4) & 0xfffffffc,high);
 215         } else
 216                 put_long(tsk,addr,data);
 217         return 0;
 218 }
 219 
 220 int sys_ptrace(long request, long pid, long addr, long data)
     /* [previous][next][first][last][top][bottom][index][help] */
 221 {
 222         struct task_struct *child;
 223 
 224         if (request == PTRACE_TRACEME) {
 225                 /* are we already being traced? */
 226                 if (current->flags & PF_PTRACED)
 227                         return -EPERM;
 228                 /* set the ptrace bit in the proccess flags. */
 229                 current->flags |= PF_PTRACED;
 230                 return 0;
 231         }
 232         if (pid == 1)           /* you may not mess with init */
 233                 return -EPERM;
 234         if (!(child = get_task(pid)))
 235                 return -ESRCH;
 236         if (request == PTRACE_ATTACH) {
 237                 long tmp;
 238 
 239                 if (child == current)
 240                         return -EPERM;
 241                 if ((!child->dumpable || (current->uid != child->euid) ||
 242                     (current->gid != child->egid)) && !suser())
 243                         return -EPERM;
 244                 /* the same process cannot be attached many times */
 245                 if (child->flags & PF_PTRACED)
 246                         return -EPERM;
 247                 child->flags |= PF_PTRACED;
 248                 if (child->p_pptr != current) {
 249                         REMOVE_LINKS(child);
 250                         child->p_pptr = current;
 251                         SET_LINKS(child);
 252                 }
 253                 tmp = get_stack_long(child, 4*EFL-MAGICNUMBER) | TRAP_FLAG;
 254                 put_stack_long(child, 4*EFL-MAGICNUMBER,tmp);
 255                 if (child->state == TASK_INTERRUPTIBLE ||
 256                     child->state == TASK_STOPPED)
 257                         child->state = TASK_RUNNING;
 258                 child->signal = 0;
 259                 return 0;
 260         }
 261         if (!(child->flags & PF_PTRACED))
 262                 return -ESRCH;
 263         if (child->state != TASK_STOPPED && request != PTRACE_DETACH)
 264                 return -ESRCH;
 265         if (child->p_pptr != current)
 266                 return -ESRCH;
 267 
 268         switch (request) {
 269         /* when I and D space are seperate, these will need to be fixed. */
 270                 case PTRACE_PEEKTEXT: /* read word at location addr. */ 
 271                 case PTRACE_PEEKDATA: {
 272                         int tmp,res;
 273 
 274                         res = read_long(child, addr, &tmp);
 275                         if (res < 0)
 276                                 return res;
 277                         verify_area((void *) data, 4);
 278                         put_fs_long(tmp,(unsigned long *) data);
 279                         return 0;
 280                 }
 281 
 282         /* read the word at location addr in the USER area. */
 283                 case PTRACE_PEEKUSR: {
 284                         int tmp;
 285                         addr = addr >> 2; /* temporary hack. */
 286                         if (addr < 0 || addr >= 17)
 287                                 return -EIO;
 288                         verify_area((void *) data, 4);
 289                         tmp = get_stack_long(child, 4*addr - MAGICNUMBER);
 290                         put_fs_long(tmp,(unsigned long *) data);
 291                         return 0;
 292                 }
 293 
 294       /* when I and D space are seperate, this will have to be fixed. */
 295                 case PTRACE_POKETEXT: /* write the word at location addr. */
 296                 case PTRACE_POKEDATA:
 297                         return write_long(child,addr,data);
 298 
 299                 case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
 300                         addr = addr >> 2; /* temproary hack. */
 301                         if (addr < 0 || addr >= 17)
 302                                 return -EIO;
 303                         if (addr == ORIG_EAX)
 304                                 return -EIO;
 305                         if (addr == EFL) {   /* flags. */
 306                                 data &= FLAG_MASK;
 307                                 data |= get_stack_long(child, EFL*4-MAGICNUMBER)  & ~FLAG_MASK;
 308                         }
 309                         if (put_stack_long(child, 4*addr-MAGICNUMBER, data))
 310                                 return -EIO;
 311                         return 0;
 312 
 313                 case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
 314                 case PTRACE_CONT: { /* restart after signal. */
 315                         long tmp;
 316 
 317                         if (request == PTRACE_SYSCALL)
 318                                 child->flags |= PF_TRACESYS;
 319                         else
 320                                 child->flags &= ~PF_TRACESYS;
 321                         child->signal = 0;
 322                         if (data > 0 && data <= NSIG)
 323                                 child->signal = 1<<(data-1);
 324                         child->state = TASK_RUNNING;
 325         /* make sure the single step bit is not set. */
 326                         tmp = get_stack_long(child, 4*EFL-MAGICNUMBER) & ~TRAP_FLAG;
 327                         put_stack_long(child, 4*EFL-MAGICNUMBER,tmp);
 328                         return 0;
 329                 }
 330 
 331 /*
 332  * make the child exit.  Best I can do is send it a sigkill. 
 333  * perhaps it should be put in the status that it want's to 
 334  * exit.
 335  */
 336                 case PTRACE_KILL: {
 337                         long tmp;
 338 
 339                         child->state = TASK_RUNNING;
 340                         child->signal = 1 << (SIGKILL-1);
 341         /* make sure the single step bit is not set. */
 342                         tmp = get_stack_long(child, 4*EFL-MAGICNUMBER) & ~TRAP_FLAG;
 343                         put_stack_long(child, 4*EFL-MAGICNUMBER,tmp);
 344                         return 0;
 345                 }
 346 
 347                 case PTRACE_SINGLESTEP: {  /* set the trap flag. */
 348                         long tmp;
 349 
 350                         child->flags &= ~PF_TRACESYS;
 351                         tmp = get_stack_long(child, 4*EFL-MAGICNUMBER) | TRAP_FLAG;
 352                         put_stack_long(child, 4*EFL-MAGICNUMBER,tmp);
 353                         child->state = TASK_RUNNING;
 354                         child->signal = 0;
 355                         if (data > 0 && data <= NSIG)
 356                                 child->signal= 1<<(data-1);
 357         /* give it a chance to run. */
 358                         return 0;
 359                 }
 360 
 361                 case PTRACE_DETACH: { /* detach a process that was attached. */
 362                         long tmp;
 363 
 364                         child->flags &= ~(PF_PTRACED|PF_TRACESYS);
 365                         child->signal=0;
 366                         child->state = 0;
 367                         REMOVE_LINKS(child);
 368                         child->p_pptr = child->p_opptr;
 369                         SET_LINKS(child);
 370                         /* make sure the single step bit is not set. */
 371                         tmp = get_stack_long(child, 4*EFL-MAGICNUMBER) & ~TRAP_FLAG;
 372                         put_stack_long(child, 4*EFL-MAGICNUMBER,tmp);
 373                         return 0;
 374                 }
 375 
 376                 default:
 377                         return -EIO;
 378         }
 379 }

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