linux/arch/um/kernel/ptrace.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
   3 * Licensed under the GPL
   4 */
   5
   6#include "linux/audit.h"
   7#include "linux/ptrace.h"
   8#include "linux/sched.h"
   9#include "asm/uaccess.h"
  10#include "skas_ptrace.h"
  11
  12
  13
  14void user_enable_single_step(struct task_struct *child)
  15{
  16        child->ptrace |= PT_DTRACE;
  17        child->thread.singlestep_syscall = 0;
  18
  19#ifdef SUBARCH_SET_SINGLESTEPPING
  20        SUBARCH_SET_SINGLESTEPPING(child, 1);
  21#endif
  22}
  23
  24void user_disable_single_step(struct task_struct *child)
  25{
  26        child->ptrace &= ~PT_DTRACE;
  27        child->thread.singlestep_syscall = 0;
  28
  29#ifdef SUBARCH_SET_SINGLESTEPPING
  30        SUBARCH_SET_SINGLESTEPPING(child, 0);
  31#endif
  32}
  33
  34/*
  35 * Called by kernel/ptrace.c when detaching..
  36 */
  37void ptrace_disable(struct task_struct *child)
  38{
  39        user_disable_single_step(child);
  40}
  41
  42extern int peek_user(struct task_struct * child, long addr, long data);
  43extern int poke_user(struct task_struct * child, long addr, long data);
  44
  45long arch_ptrace(struct task_struct *child, long request,
  46                 unsigned long addr, unsigned long data)
  47{
  48        int i, ret;
  49        unsigned long __user *p = (void __user *)data;
  50        void __user *vp = p;
  51
  52        switch (request) {
  53        /* read the word at location addr in the USER area. */
  54        case PTRACE_PEEKUSR:
  55                ret = peek_user(child, addr, data);
  56                break;
  57
  58        /* write the word at location addr in the USER area */
  59        case PTRACE_POKEUSR:
  60                ret = poke_user(child, addr, data);
  61                break;
  62
  63        case PTRACE_SYSEMU:
  64        case PTRACE_SYSEMU_SINGLESTEP:
  65                ret = -EIO;
  66                break;
  67
  68#ifdef PTRACE_GETREGS
  69        case PTRACE_GETREGS: { /* Get all gp regs from the child. */
  70                if (!access_ok(VERIFY_WRITE, p, MAX_REG_OFFSET)) {
  71                        ret = -EIO;
  72                        break;
  73                }
  74                for ( i = 0; i < MAX_REG_OFFSET; i += sizeof(long) ) {
  75                        __put_user(getreg(child, i), p);
  76                        p++;
  77                }
  78                ret = 0;
  79                break;
  80        }
  81#endif
  82#ifdef PTRACE_SETREGS
  83        case PTRACE_SETREGS: { /* Set all gp regs in the child. */
  84                unsigned long tmp = 0;
  85                if (!access_ok(VERIFY_READ, p, MAX_REG_OFFSET)) {
  86                        ret = -EIO;
  87                        break;
  88                }
  89                for ( i = 0; i < MAX_REG_OFFSET; i += sizeof(long) ) {
  90                        __get_user(tmp, p);
  91                        putreg(child, i, tmp);
  92                        p++;
  93                }
  94                ret = 0;
  95                break;
  96        }
  97#endif
  98        case PTRACE_GET_THREAD_AREA:
  99                ret = ptrace_get_thread_area(child, addr, vp);
 100                break;
 101
 102        case PTRACE_SET_THREAD_AREA:
 103                ret = ptrace_set_thread_area(child, addr, vp);
 104                break;
 105
 106        case PTRACE_FAULTINFO: {
 107                /*
 108                 * Take the info from thread->arch->faultinfo,
 109                 * but transfer max. sizeof(struct ptrace_faultinfo).
 110                 * On i386, ptrace_faultinfo is smaller!
 111                 */
 112                ret = copy_to_user(p, &child->thread.arch.faultinfo,
 113                                   sizeof(struct ptrace_faultinfo)) ?
 114                        -EIO : 0;
 115                break;
 116        }
 117
 118#ifdef PTRACE_LDT
 119        case PTRACE_LDT: {
 120                struct ptrace_ldt ldt;
 121
 122                if (copy_from_user(&ldt, p, sizeof(ldt))) {
 123                        ret = -EIO;
 124                        break;
 125                }
 126
 127                /*
 128                 * This one is confusing, so just punt and return -EIO for
 129                 * now
 130                 */
 131                ret = -EIO;
 132                break;
 133        }
 134#endif
 135        default:
 136                ret = ptrace_request(child, request, addr, data);
 137                if (ret == -EIO)
 138                        ret = subarch_ptrace(child, request, addr, data);
 139                break;
 140        }
 141
 142        return ret;
 143}
 144
 145static void send_sigtrap(struct task_struct *tsk, struct uml_pt_regs *regs,
 146                  int error_code)
 147{
 148        struct siginfo info;
 149
 150        memset(&info, 0, sizeof(info));
 151        info.si_signo = SIGTRAP;
 152        info.si_code = TRAP_BRKPT;
 153
 154        /* User-mode eip? */
 155        info.si_addr = UPT_IS_USER(regs) ? (void __user *) UPT_IP(regs) : NULL;
 156
 157        /* Send us the fake SIGTRAP */
 158        force_sig_info(SIGTRAP, &info, tsk);
 159}
 160
 161/*
 162 * XXX Check PT_DTRACE vs TIF_SINGLESTEP for singlestepping check and
 163 * PT_PTRACED vs TIF_SYSCALL_TRACE for syscall tracing check
 164 */
 165void syscall_trace(struct uml_pt_regs *regs, int entryexit)
 166{
 167        int is_singlestep = (current->ptrace & PT_DTRACE) && entryexit;
 168        int tracesysgood;
 169
 170        if (!entryexit)
 171                audit_syscall_entry(HOST_AUDIT_ARCH,
 172                                    UPT_SYSCALL_NR(regs),
 173                                    UPT_SYSCALL_ARG1(regs),
 174                                    UPT_SYSCALL_ARG2(regs),
 175                                    UPT_SYSCALL_ARG3(regs),
 176                                    UPT_SYSCALL_ARG4(regs));
 177        else
 178                audit_syscall_exit(regs);
 179
 180        /* Fake a debug trap */
 181        if (is_singlestep)
 182                send_sigtrap(current, regs, 0);
 183
 184        if (!test_thread_flag(TIF_SYSCALL_TRACE))
 185                return;
 186
 187        if (!(current->ptrace & PT_PTRACED))
 188                return;
 189
 190        /*
 191         * the 0x80 provides a way for the tracing parent to distinguish
 192         * between a syscall stop and SIGTRAP delivery
 193         */
 194        tracesysgood = (current->ptrace & PT_TRACESYSGOOD);
 195        ptrace_notify(SIGTRAP | (tracesysgood ? 0x80 : 0));
 196
 197        if (entryexit) /* force do_signal() --> is_syscall() */
 198                set_thread_flag(TIF_SIGPENDING);
 199
 200        /*
 201         * this isn't the same as continuing with a signal, but it will do
 202         * for normal use.  strace only continues with a signal if the
 203         * stopping signal is not SIGTRAP.  -brl
 204         */
 205        if (current->exit_code) {
 206                send_sig(current->exit_code, current, 1);
 207                current->exit_code = 0;
 208        }
 209}
 210