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