linux-old/arch/i386/kernel/ptrace.c
<<
>>
Prefs
   1/* ptrace.c */
   2/* By Ross Biro 1/23/92 */
   3/*
   4 * Pentium III FXSR, SSE support
   5 *      Gareth Hughes <gareth@valinux.com>, May 2000
   6 */
   7
   8#include <linux/kernel.h>
   9#include <linux/sched.h>
  10#include <linux/mm.h>
  11#include <linux/smp.h>
  12#include <linux/smp_lock.h>
  13#include <linux/errno.h>
  14#include <linux/ptrace.h>
  15#include <linux/user.h>
  16
  17#include <asm/uaccess.h>
  18#include <asm/pgtable.h>
  19#include <asm/system.h>
  20#include <asm/processor.h>
  21#include <asm/i387.h>
  22#include <asm/debugreg.h>
  23
  24/*
  25 * does not yet catch signals sent when the child dies.
  26 * in exit.c or in signal.c.
  27 */
  28
  29/* determines which flags the user has access to. */
  30/* 1 = access 0 = no access */
  31#define FLAG_MASK 0x00044dd5
  32
  33/* set's the trap flag. */
  34#define TRAP_FLAG 0x100
  35
  36/*
  37 * Offset of eflags on child stack..
  38 */
  39#define EFL_OFFSET ((EFL-2)*4-sizeof(struct pt_regs))
  40
  41/*
  42 * this routine will get a word off of the processes privileged stack. 
  43 * the offset is how far from the base addr as stored in the TSS.  
  44 * this routine assumes that all the privileged stacks are in our
  45 * data space.
  46 */   
  47static inline int get_stack_long(struct task_struct *task, int offset)
  48{
  49        unsigned char *stack;
  50
  51        stack = (unsigned char *)task->thread.esp0;
  52        stack += offset;
  53        return (*((int *)stack));
  54}
  55
  56/*
  57 * this routine will put a word on the processes privileged stack. 
  58 * the offset is how far from the base addr as stored in the TSS.  
  59 * this routine assumes that all the privileged stacks are in our
  60 * data space.
  61 */
  62static inline int put_stack_long(struct task_struct *task, int offset,
  63        unsigned long data)
  64{
  65        unsigned char * stack;
  66
  67        stack = (unsigned char *) task->thread.esp0;
  68        stack += offset;
  69        *(unsigned long *) stack = data;
  70        return 0;
  71}
  72
  73static int putreg(struct task_struct *child,
  74        unsigned long regno, unsigned long value)
  75{
  76        switch (regno >> 2) {
  77                case FS:
  78                        if (value && (value & 3) != 3)
  79                                return -EIO;
  80                        child->thread.fs = value;
  81                        return 0;
  82                case GS:
  83                        if (value && (value & 3) != 3)
  84                                return -EIO;
  85                        child->thread.gs = value;
  86                        return 0;
  87                case DS:
  88                case ES:
  89                        if (value && (value & 3) != 3)
  90                                return -EIO;
  91                        value &= 0xffff;
  92                        break;
  93                case SS:
  94                case CS:
  95                        if ((value & 3) != 3)
  96                                return -EIO;
  97                        value &= 0xffff;
  98                        break;
  99                case EFL:
 100                        value &= FLAG_MASK;
 101                        value |= get_stack_long(child, EFL_OFFSET) & ~FLAG_MASK;
 102                        break;
 103        }
 104        if (regno > GS*4)
 105                regno -= 2*4;
 106        put_stack_long(child, regno - sizeof(struct pt_regs), value);
 107        return 0;
 108}
 109
 110static unsigned long getreg(struct task_struct *child,
 111        unsigned long regno)
 112{
 113        unsigned long retval = ~0UL;
 114
 115        switch (regno >> 2) {
 116                case FS:
 117                        retval = child->thread.fs;
 118                        break;
 119                case GS:
 120                        retval = child->thread.gs;
 121                        break;
 122                case DS:
 123                case ES:
 124                case SS:
 125                case CS:
 126                        retval = 0xffff;
 127                        /* fall through */
 128                default:
 129                        if (regno > GS*4)
 130                                regno -= 2*4;
 131                        regno = regno - sizeof(struct pt_regs);
 132                        retval &= get_stack_long(child, regno);
 133        }
 134        return retval;
 135}
 136
 137/*
 138 * Called by kernel/ptrace.c when detaching..
 139 *
 140 * Make sure the single step bit is not set.
 141 */
 142void ptrace_disable(struct task_struct *child)
 143{ 
 144        long tmp;
 145
 146        tmp = get_stack_long(child, EFL_OFFSET) & ~TRAP_FLAG;
 147        put_stack_long(child, EFL_OFFSET, tmp);
 148}
 149
 150asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
 151{
 152        struct task_struct *child;
 153        struct user * dummy = NULL;
 154        int i, ret;
 155
 156        lock_kernel();
 157        ret = -EPERM;
 158        if (request == PTRACE_TRACEME) {
 159                /* are we already being traced? */
 160                if (current->ptrace & PT_PTRACED)
 161                        goto out;
 162                /* set the ptrace bit in the process flags. */
 163                current->ptrace |= PT_PTRACED;
 164                ret = 0;
 165                goto out;
 166        }
 167        ret = -ESRCH;
 168        read_lock(&tasklist_lock);
 169        child = find_task_by_pid(pid);
 170        if (child)
 171                get_task_struct(child);
 172        read_unlock(&tasklist_lock);
 173        if (!child)
 174                goto out;
 175
 176        ret = -EPERM;
 177        if (pid == 1)           /* you may not mess with init */
 178                goto out_tsk;
 179
 180        if (request == PTRACE_ATTACH) {
 181                ret = ptrace_attach(child);
 182                goto out_tsk;
 183        }
 184
 185        ret = ptrace_check_attach(child, request == PTRACE_KILL);
 186        if (ret < 0)
 187                goto out_tsk;
 188
 189        switch (request) {
 190        /* when I and D space are separate, these will need to be fixed. */
 191        case PTRACE_PEEKTEXT: /* read word at location addr. */ 
 192        case PTRACE_PEEKDATA: {
 193                unsigned long tmp;
 194                int copied;
 195
 196                copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
 197                ret = -EIO;
 198                if (copied != sizeof(tmp))
 199                        break;
 200                ret = put_user(tmp,(unsigned long *) data);
 201                break;
 202        }
 203
 204        /* read the word at location addr in the USER area. */
 205        case PTRACE_PEEKUSR: {
 206                unsigned long tmp;
 207
 208                ret = -EIO;
 209                if ((addr & 3) || addr < 0 || 
 210                    addr > sizeof(struct user) - 3)
 211                        break;
 212
 213                tmp = 0;  /* Default return condition */
 214                if(addr < FRAME_SIZE*sizeof(long))
 215                        tmp = getreg(child, addr);
 216                if(addr >= (long) &dummy->u_debugreg[0] &&
 217                   addr <= (long) &dummy->u_debugreg[7]){
 218                        addr -= (long) &dummy->u_debugreg[0];
 219                        addr = addr >> 2;
 220                        tmp = child->thread.debugreg[addr];
 221                }
 222                ret = put_user(tmp,(unsigned long *) data);
 223                break;
 224        }
 225
 226        /* when I and D space are separate, this will have to be fixed. */
 227        case PTRACE_POKETEXT: /* write the word at location addr. */
 228        case PTRACE_POKEDATA:
 229                ret = 0;
 230                if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data))
 231                        break;
 232                ret = -EIO;
 233                break;
 234
 235        case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
 236                ret = -EIO;
 237                if ((addr & 3) || addr < 0 || 
 238                    addr > sizeof(struct user) - 3)
 239                        break;
 240
 241                if (addr < FRAME_SIZE*sizeof(long)) {
 242                        ret = putreg(child, addr, data);
 243                        break;
 244                }
 245                /* We need to be very careful here.  We implicitly
 246                   want to modify a portion of the task_struct, and we
 247                   have to be selective about what portions we allow someone
 248                   to modify. */
 249
 250                  ret = -EIO;
 251                  if(addr >= (long) &dummy->u_debugreg[0] &&
 252                     addr <= (long) &dummy->u_debugreg[7]){
 253
 254                          if(addr == (long) &dummy->u_debugreg[4]) break;
 255                          if(addr == (long) &dummy->u_debugreg[5]) break;
 256                          if(addr < (long) &dummy->u_debugreg[4] &&
 257                             ((unsigned long) data) >= TASK_SIZE-3) break;
 258                          
 259                          if(addr == (long) &dummy->u_debugreg[7]) {
 260                                  data &= ~DR_CONTROL_RESERVED;
 261                                  for(i=0; i<4; i++)
 262                                          if ((0x5f54 >> ((data >> (16 + 4*i)) & 0xf)) & 1)
 263                                                  goto out_tsk;
 264                          }
 265
 266                          addr -= (long) &dummy->u_debugreg;
 267                          addr = addr >> 2;
 268                          child->thread.debugreg[addr] = data;
 269                          ret = 0;
 270                  }
 271                  break;
 272
 273        case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
 274        case PTRACE_CONT: { /* restart after signal. */
 275                long tmp;
 276
 277                ret = -EIO;
 278                if ((unsigned long) data > _NSIG)
 279                        break;
 280                if (request == PTRACE_SYSCALL)
 281                        child->ptrace |= PT_TRACESYS;
 282                else
 283                        child->ptrace &= ~PT_TRACESYS;
 284                child->exit_code = data;
 285        /* make sure the single step bit is not set. */
 286                tmp = get_stack_long(child, EFL_OFFSET) & ~TRAP_FLAG;
 287                put_stack_long(child, EFL_OFFSET,tmp);
 288                wake_up_process(child);
 289                ret = 0;
 290                break;
 291        }
 292
 293/*
 294 * make the child exit.  Best I can do is send it a sigkill. 
 295 * perhaps it should be put in the status that it wants to 
 296 * exit.
 297 */
 298        case PTRACE_KILL: {
 299                long tmp;
 300
 301                ret = 0;
 302                if (child->state == TASK_ZOMBIE)        /* already dead */
 303                        break;
 304                child->exit_code = SIGKILL;
 305                /* make sure the single step bit is not set. */
 306                tmp = get_stack_long(child, EFL_OFFSET) & ~TRAP_FLAG;
 307                put_stack_long(child, EFL_OFFSET, tmp);
 308                wake_up_process(child);
 309                break;
 310        }
 311
 312        case PTRACE_SINGLESTEP: {  /* set the trap flag. */
 313                long tmp;
 314
 315                ret = -EIO;
 316                if ((unsigned long) data > _NSIG)
 317                        break;
 318                child->ptrace &= ~PT_TRACESYS;
 319                if ((child->ptrace & PT_DTRACE) == 0) {
 320                        /* Spurious delayed TF traps may occur */
 321                        child->ptrace |= PT_DTRACE;
 322                }
 323                tmp = get_stack_long(child, EFL_OFFSET) | TRAP_FLAG;
 324                put_stack_long(child, EFL_OFFSET, tmp);
 325                child->exit_code = data;
 326                /* give it a chance to run. */
 327                wake_up_process(child);
 328                ret = 0;
 329                break;
 330        }
 331
 332        case PTRACE_DETACH:
 333                /* detach a process that was attached. */
 334                ret = ptrace_detach(child, data);
 335                break;
 336
 337        case PTRACE_GETREGS: { /* Get all gp regs from the child. */
 338                if (!access_ok(VERIFY_WRITE, (unsigned *)data, FRAME_SIZE*sizeof(long))) {
 339                        ret = -EIO;
 340                        break;
 341                }
 342                for ( i = 0; i < FRAME_SIZE*sizeof(long); i += sizeof(long) ) {
 343                        __put_user(getreg(child, i),(unsigned long *) data);
 344                        data += sizeof(long);
 345                }
 346                ret = 0;
 347                break;
 348        }
 349
 350        case PTRACE_SETREGS: { /* Set all gp regs in the child. */
 351                unsigned long tmp;
 352                if (!access_ok(VERIFY_READ, (unsigned *)data, FRAME_SIZE*sizeof(long))) {
 353                        ret = -EIO;
 354                        break;
 355                }
 356                for ( i = 0; i < FRAME_SIZE*sizeof(long); i += sizeof(long) ) {
 357                        __get_user(tmp, (unsigned long *) data);
 358                        putreg(child, i, tmp);
 359                        data += sizeof(long);
 360                }
 361                ret = 0;
 362                break;
 363        }
 364
 365        case PTRACE_GETFPREGS: { /* Get the child FPU state. */
 366                if (!access_ok(VERIFY_WRITE, (unsigned *)data,
 367                               sizeof(struct user_i387_struct))) {
 368                        ret = -EIO;
 369                        break;
 370                }
 371                ret = 0;
 372                if ( !child->used_math )
 373                        load_empty_fpu(child);
 374                get_fpregs((struct user_i387_struct *)data, child);
 375                break;
 376        }
 377
 378        case PTRACE_SETFPREGS: { /* Set the child FPU state. */
 379                if (!access_ok(VERIFY_READ, (unsigned *)data,
 380                               sizeof(struct user_i387_struct))) {
 381                        ret = -EIO;
 382                        break;
 383                }
 384                child->used_math = 1;
 385                set_fpregs(child, (struct user_i387_struct *)data);
 386                ret = 0;
 387                break;
 388        }
 389
 390        case PTRACE_GETFPXREGS: { /* Get the child extended FPU state. */
 391                if (!access_ok(VERIFY_WRITE, (unsigned *)data,
 392                               sizeof(struct user_fxsr_struct))) {
 393                        ret = -EIO;
 394                        break;
 395                }
 396                if ( !child->used_math )
 397                        load_empty_fpu(child);
 398                ret = get_fpxregs((struct user_fxsr_struct *)data, child);
 399                break;
 400        }
 401
 402        case PTRACE_SETFPXREGS: { /* Set the child extended FPU state. */
 403                if (!access_ok(VERIFY_READ, (unsigned *)data,
 404                               sizeof(struct user_fxsr_struct))) {
 405                        ret = -EIO;
 406                        break;
 407                }
 408                child->used_math = 1;
 409                ret = set_fpxregs(child, (struct user_fxsr_struct *)data);
 410                break;
 411        }
 412
 413        case PTRACE_SETOPTIONS: {
 414                if (data & PTRACE_O_TRACESYSGOOD)
 415                        child->ptrace |= PT_TRACESYSGOOD;
 416                else
 417                        child->ptrace &= ~PT_TRACESYSGOOD;
 418                ret = 0;
 419                break;
 420        }
 421
 422        default:
 423                ret = -EIO;
 424                break;
 425        }
 426out_tsk:
 427        free_task_struct(child);
 428out:
 429        unlock_kernel();
 430        return ret;
 431}
 432
 433asmlinkage void syscall_trace(void)
 434{
 435        if ((current->ptrace & (PT_PTRACED|PT_TRACESYS)) !=
 436                        (PT_PTRACED|PT_TRACESYS))
 437                return;
 438        /* the 0x80 provides a way for the tracing parent to distinguish
 439           between a syscall stop and SIGTRAP delivery */
 440        current->exit_code = SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
 441                                        ? 0x80 : 0);
 442        current->state = TASK_STOPPED;
 443        notify_parent(current, SIGCHLD);
 444        schedule();
 445        /*
 446         * this isn't the same as continuing with a signal, but it will do
 447         * for normal use.  strace only continues with a signal if the
 448         * stopping signal is not SIGTRAP.  -brl
 449         */
 450        if (current->exit_code) {
 451                send_sig(current->exit_code, current, 1);
 452                current->exit_code = 0;
 453        }
 454}
 455
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.