home *** CD-ROM | disk | FTP | other *** search
/ Il CD di internet / CD.iso / SOURCE / KERNEL-S / V1.0 / LINUX-1.0 / LINUX-1 / linux / kernel / ptrace.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-12-27  |  12.5 KB  |  481 lines

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