ptrace.c 4.2 KB
Newer Older
J
Jeff Dike 已提交
1 2
/*
 * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
L
Linus Torvalds 已提交
3 4 5
 * Licensed under the GPL
 */

6 7 8 9 10 11
#include <linux/audit.h>
#include <linux/ptrace.h>
#include <linux/sched.h>
#include <linux/tracehook.h>
#include <asm/uaccess.h>
#include <skas_ptrace.h>
L
Linus Torvalds 已提交
12

13 14 15 16 17 18 19 20 21 22 23 24 25


void user_enable_single_step(struct task_struct *child)
{
	child->ptrace |= PT_DTRACE;
	child->thread.singlestep_syscall = 0;

#ifdef SUBARCH_SET_SINGLESTEPPING
	SUBARCH_SET_SINGLESTEPPING(child, 1);
#endif
}

void user_disable_single_step(struct task_struct *child)
26
{
27
	child->ptrace &= ~PT_DTRACE;
J
Jeff Dike 已提交
28
	child->thread.singlestep_syscall = 0;
29 30

#ifdef SUBARCH_SET_SINGLESTEPPING
31
	SUBARCH_SET_SINGLESTEPPING(child, 0);
32
#endif
33
}
34

L
Linus Torvalds 已提交
35 36 37 38
/*
 * Called by kernel/ptrace.c when detaching..
 */
void ptrace_disable(struct task_struct *child)
J
Jeff Dike 已提交
39
{
40
	user_disable_single_step(child);
L
Linus Torvalds 已提交
41 42
}

43 44 45
extern int peek_user(struct task_struct * child, long addr, long data);
extern int poke_user(struct task_struct * child, long addr, long data);

46 47
long arch_ptrace(struct task_struct *child, long request,
		 unsigned long addr, unsigned long data)
L
Linus Torvalds 已提交
48 49
{
	int i, ret;
50
	unsigned long __user *p = (void __user *)data;
N
Namhyung Kim 已提交
51
	void __user *vp = p;
L
Linus Torvalds 已提交
52 53 54

	switch (request) {
	/* read the word at location addr in the USER area. */
J
Jeff Dike 已提交
55 56 57
	case PTRACE_PEEKUSR:
		ret = peek_user(child, addr, data);
		break;
L
Linus Torvalds 已提交
58

J
Jeff Dike 已提交
59 60 61 62
	/* write the word at location addr in the USER area */
	case PTRACE_POKEUSR:
		ret = poke_user(child, addr, data);
		break;
L
Linus Torvalds 已提交
63

64 65 66 67 68
	case PTRACE_SYSEMU:
	case PTRACE_SYSEMU_SINGLESTEP:
		ret = -EIO;
		break;

L
Linus Torvalds 已提交
69 70
#ifdef PTRACE_GETREGS
	case PTRACE_GETREGS: { /* Get all gp regs from the child. */
A
Al Viro 已提交
71
		if (!access_ok(VERIFY_WRITE, p, MAX_REG_OFFSET)) {
L
Linus Torvalds 已提交
72 73 74 75
			ret = -EIO;
			break;
		}
		for ( i = 0; i < MAX_REG_OFFSET; i += sizeof(long) ) {
A
Al Viro 已提交
76 77
			__put_user(getreg(child, i), p);
			p++;
L
Linus Torvalds 已提交
78 79 80 81 82 83 84 85
		}
		ret = 0;
		break;
	}
#endif
#ifdef PTRACE_SETREGS
	case PTRACE_SETREGS: { /* Set all gp regs in the child. */
		unsigned long tmp = 0;
A
Al Viro 已提交
86
		if (!access_ok(VERIFY_READ, p, MAX_REG_OFFSET)) {
L
Linus Torvalds 已提交
87 88 89 90
			ret = -EIO;
			break;
		}
		for ( i = 0; i < MAX_REG_OFFSET; i += sizeof(long) ) {
A
Al Viro 已提交
91
			__get_user(tmp, p);
L
Linus Torvalds 已提交
92
			putreg(child, i, tmp);
A
Al Viro 已提交
93
			p++;
L
Linus Torvalds 已提交
94 95 96 97 98
		}
		ret = 0;
		break;
	}
#endif
99
	case PTRACE_GET_THREAD_AREA:
N
Namhyung Kim 已提交
100
		ret = ptrace_get_thread_area(child, addr, vp);
101 102 103
		break;

	case PTRACE_SET_THREAD_AREA:
R
Richard Weinberger 已提交
104
		ret = ptrace_set_thread_area(child, addr, vp);
105 106
		break;

L
Linus Torvalds 已提交
107
	case PTRACE_FAULTINFO: {
J
Jeff Dike 已提交
108 109
		/*
		 * Take the info from thread->arch->faultinfo,
A
Al Viro 已提交
110 111 112 113
		 * but transfer max. sizeof(struct ptrace_faultinfo).
		 * On i386, ptrace_faultinfo is smaller!
		 */
		ret = copy_to_user(p, &child->thread.arch.faultinfo,
N
Namhyung Kim 已提交
114 115
				   sizeof(struct ptrace_faultinfo)) ?
			-EIO : 0;
L
Linus Torvalds 已提交
116 117 118
		break;
	}

119
#ifdef PTRACE_LDT
L
Linus Torvalds 已提交
120 121 122
	case PTRACE_LDT: {
		struct ptrace_ldt ldt;

J
Jeff Dike 已提交
123
		if (copy_from_user(&ldt, p, sizeof(ldt))) {
L
Linus Torvalds 已提交
124 125 126 127
			ret = -EIO;
			break;
		}

J
Jeff Dike 已提交
128 129
		/*
		 * This one is confusing, so just punt and return -EIO for
L
Linus Torvalds 已提交
130 131 132 133 134 135 136 137
		 * now
		 */
		ret = -EIO;
		break;
	}
#endif
	default:
		ret = ptrace_request(child, request, addr, data);
J
Jeff Dike 已提交
138 139
		if (ret == -EIO)
			ret = subarch_ptrace(child, request, addr, data);
L
Linus Torvalds 已提交
140 141
		break;
	}
142

L
Linus Torvalds 已提交
143 144 145
	return ret;
}

W
WANG Cong 已提交
146
static void send_sigtrap(struct task_struct *tsk, struct uml_pt_regs *regs,
L
Linus Torvalds 已提交
147 148 149 150 151 152 153 154 155 156 157
		  int error_code)
{
	struct siginfo info;

	memset(&info, 0, sizeof(info));
	info.si_signo = SIGTRAP;
	info.si_code = TRAP_BRKPT;

	/* User-mode eip? */
	info.si_addr = UPT_IS_USER(regs) ? (void __user *) UPT_IP(regs) : NULL;

S
Simon Arlott 已提交
158
	/* Send us the fake SIGTRAP */
L
Linus Torvalds 已提交
159 160 161
	force_sig_info(SIGTRAP, &info, tsk);
}

J
Jeff Dike 已提交
162 163
/*
 * XXX Check PT_DTRACE vs TIF_SINGLESTEP for singlestepping check and
L
Linus Torvalds 已提交
164 165
 * PT_PTRACED vs TIF_SYSCALL_TRACE for syscall tracing check
 */
166
void syscall_trace_enter(struct pt_regs *regs)
L
Linus Torvalds 已提交
167
{
168
	audit_syscall_entry(UPT_SYSCALL_NR(&regs->regs),
169 170 171 172
			    UPT_SYSCALL_ARG1(&regs->regs),
			    UPT_SYSCALL_ARG2(&regs->regs),
			    UPT_SYSCALL_ARG3(&regs->regs),
			    UPT_SYSCALL_ARG4(&regs->regs));
L
Linus Torvalds 已提交
173 174 175 176

	if (!test_thread_flag(TIF_SYSCALL_TRACE))
		return;

177 178
	tracehook_report_syscall_entry(regs);
}
L
Linus Torvalds 已提交
179

180 181 182
void syscall_trace_leave(struct pt_regs *regs)
{
	int ptraced = current->ptrace;
L
Linus Torvalds 已提交
183

184
	audit_syscall_exit(regs);
L
Linus Torvalds 已提交
185

186 187 188 189 190 191 192 193 194 195 196
	/* Fake a debug trap */
	if (ptraced & PT_DTRACE)
		send_sigtrap(current, &regs->regs, 0);

	if (!test_thread_flag(TIF_SYSCALL_TRACE))
		return;

	tracehook_report_syscall_exit(regs, 0);
	/* force do_signal() --> is_syscall() */
	if (ptraced & PT_PTRACED)
		set_thread_flag(TIF_SIGPENDING);
L
Linus Torvalds 已提交
197
}