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