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