ptrace_32.c 6.9 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8
/*
 * linux/arch/sh/kernel/ptrace.c
 *
 * Original x86 implementation:
 *	By Ross Biro 1/23/92
 *	edited by Linus Torvalds
 *
 * SuperH version:   Copyright (C) 1999, 2000  Kaz Kojima & Niibe Yutaka
Y
Yuichi Nakamura 已提交
9
 * Audit support: Yuichi Nakamura <ynakam@hitachisoft.jp>
L
Linus Torvalds 已提交
10 11 12 13 14 15 16 17 18 19
 */
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/smp.h>
#include <linux/errno.h>
#include <linux/ptrace.h>
#include <linux/user.h>
#include <linux/slab.h>
#include <linux/security.h>
20
#include <linux/signal.h>
S
Stuart Menefy 已提交
21
#include <linux/io.h>
Y
Yuichi Nakamura 已提交
22
#include <linux/audit.h>
L
Linus Torvalds 已提交
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
#include <asm/uaccess.h>
#include <asm/pgtable.h>
#include <asm/system.h>
#include <asm/processor.h>
#include <asm/mmu_context.h>

/*
 * does not yet catch signals sent when the child dies.
 * in exit.c or in signal.c.
 */

/*
 * This routine will get a word off of the process kernel stack.
 */
static inline int get_stack_long(struct task_struct *task, int offset)
{
	unsigned char *stack;

A
Al Viro 已提交
41
	stack = (unsigned char *)task_pt_regs(task);
L
Linus Torvalds 已提交
42 43 44 45 46 47 48 49 50 51 52 53
	stack += offset;
	return (*((int *)stack));
}

/*
 * This routine will put a word on the process kernel stack.
 */
static inline int put_stack_long(struct task_struct *task, int offset,
				 unsigned long data)
{
	unsigned char *stack;

A
Al Viro 已提交
54
	stack = (unsigned char *)task_pt_regs(task);
L
Linus Torvalds 已提交
55 56 57 58 59
	stack += offset;
	*(unsigned long *) stack = data;
	return 0;
}

S
Stuart Menefy 已提交
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
static void ptrace_disable_singlestep(struct task_struct *child)
{
	clear_tsk_thread_flag(child, TIF_SINGLESTEP);

	/*
	 * Ensure the UBC is not programmed at the next context switch.
	 *
	 * Normally this is not needed but there are sequences such as
	 * singlestep, signal delivery, and continue that leave the
	 * ubc_pc non-zero leading to spurious SIGTRAPs.
	 */
	if (child->thread.ubc_pc != 0) {
		ubc_usercnt -= 1;
		child->thread.ubc_pc = 0;
	}
}

L
Linus Torvalds 已提交
77 78 79 80 81 82 83
/*
 * Called by kernel/ptrace.c when detaching..
 *
 * Make sure single step bits etc are not set.
 */
void ptrace_disable(struct task_struct *child)
{
S
Stuart Menefy 已提交
84
	ptrace_disable_singlestep(child);
L
Linus Torvalds 已提交
85 86
}

87
long arch_ptrace(struct task_struct *child, long request, long addr, long data)
L
Linus Torvalds 已提交
88 89 90 91 92 93
{
	struct user * dummy = NULL;
	int ret;

	switch (request) {
	/* when I and D space are separate, these will need to be fixed. */
S
Stuart Menefy 已提交
94
	case PTRACE_PEEKTEXT: /* read word at location addr. */
A
Alexey Dobriyan 已提交
95 96
	case PTRACE_PEEKDATA:
		ret = generic_ptrace_peekdata(child, addr, data);
97
		break;
L
Linus Torvalds 已提交
98 99 100 101 102 103

	/* read the word at location addr in the USER area. */
	case PTRACE_PEEKUSR: {
		unsigned long tmp;

		ret = -EIO;
S
Stuart Menefy 已提交
104
		if ((addr & 3) || addr < 0 ||
L
Linus Torvalds 已提交
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
		    addr > sizeof(struct user) - 3)
			break;

		if (addr < sizeof(struct pt_regs))
			tmp = get_stack_long(child, addr);
		else if (addr >= (long) &dummy->fpu &&
			 addr < (long) &dummy->u_fpvalid) {
			if (!tsk_used_math(child)) {
				if (addr == (long)&dummy->fpu.fpscr)
					tmp = FPSCR_INIT;
				else
					tmp = 0;
			} else
				tmp = ((long *)&child->thread.fpu)
					[(addr - (long)&dummy->fpu) >> 2];
		} else if (addr == (long) &dummy->u_fpvalid)
			tmp = !!tsk_used_math(child);
		else
			tmp = 0;
124
		ret = put_user(tmp, (unsigned long __user *)data);
L
Linus Torvalds 已提交
125 126 127 128 129 130
		break;
	}

	/* when I and D space are separate, this will have to be fixed. */
	case PTRACE_POKETEXT: /* write the word at location addr. */
	case PTRACE_POKEDATA:
A
Alexey Dobriyan 已提交
131
		ret = generic_ptrace_pokedata(child, addr, data);
L
Linus Torvalds 已提交
132 133 134 135
		break;

	case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
		ret = -EIO;
S
Stuart Menefy 已提交
136
		if ((addr & 3) || addr < 0 ||
L
Linus Torvalds 已提交
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
		    addr > sizeof(struct user) - 3)
			break;

		if (addr < sizeof(struct pt_regs))
			ret = put_stack_long(child, addr, data);
		else if (addr >= (long) &dummy->fpu &&
			 addr < (long) &dummy->u_fpvalid) {
			set_stopped_child_used_math(child);
			((long *)&child->thread.fpu)
				[(addr - (long)&dummy->fpu) >> 2] = data;
			ret = 0;
		} else if (addr == (long) &dummy->u_fpvalid) {
			conditional_stopped_child_used_math(data, child);
			ret = 0;
		}
		break;

	case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
	case PTRACE_CONT: { /* restart after signal. */
		ret = -EIO;
157
		if (!valid_signal(data))
L
Linus Torvalds 已提交
158 159 160 161 162
			break;
		if (request == PTRACE_SYSCALL)
			set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
		else
			clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
S
Stuart Menefy 已提交
163 164 165

		ptrace_disable_singlestep(child);

L
Linus Torvalds 已提交
166 167 168 169 170 171 172
		child->exit_code = data;
		wake_up_process(child);
		ret = 0;
		break;
	}

/*
S
Stuart Menefy 已提交
173 174
 * make the child exit.  Best I can do is send it a sigkill.
 * perhaps it should be put in the status that it wants to
L
Linus Torvalds 已提交
175 176 177 178 179 180
 * exit.
 */
	case PTRACE_KILL: {
		ret = 0;
		if (child->exit_state == EXIT_ZOMBIE)	/* already dead */
			break;
S
Stuart Menefy 已提交
181
		ptrace_disable_singlestep(child);
L
Linus Torvalds 已提交
182 183 184 185 186 187 188
		child->exit_code = SIGKILL;
		wake_up_process(child);
		break;
	}

	case PTRACE_SINGLESTEP: {  /* set the trap flag. */
		long pc;
189
		struct pt_regs *regs = NULL;
L
Linus Torvalds 已提交
190 191

		ret = -EIO;
192
		if (!valid_signal(data))
L
Linus Torvalds 已提交
193 194 195 196 197 198 199
			break;
		clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
		if ((child->ptrace & PT_DTRACE) == 0) {
			/* Spurious delayed TF traps may occur */
			child->ptrace |= PT_DTRACE;
		}

200
		pc = get_stack_long(child, (long)&regs->pc);
L
Linus Torvalds 已提交
201 202 203 204 205 206

		/* Next scheduling will set up UBC */
		if (child->thread.ubc_pc == 0)
			ubc_usercnt += 1;
		child->thread.ubc_pc = pc;

S
Stuart Menefy 已提交
207
		set_tsk_thread_flag(child, TIF_SINGLESTEP);
L
Linus Torvalds 已提交
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247
		child->exit_code = data;
		/* give it a chance to run. */
		wake_up_process(child);
		ret = 0;
		break;
	}

#ifdef CONFIG_SH_DSP
	case PTRACE_GETDSPREGS: {
		unsigned long dp;

		ret = -EIO;
		dp = ((unsigned long) child) + THREAD_SIZE -
			 sizeof(struct pt_dspregs);
		if (*((int *) (dp - 4)) == SR_FD) {
			copy_to_user(addr, (void *) dp,
				sizeof(struct pt_dspregs));
			ret = 0;
		}
		break;
	}

	case PTRACE_SETDSPREGS: {
		unsigned long dp;

		ret = -EIO;
		dp = ((unsigned long) child) + THREAD_SIZE -
			 sizeof(struct pt_dspregs);
		if (*((int *) (dp - 4)) == SR_FD) {
			copy_from_user((void *) dp, addr,
				sizeof(struct pt_dspregs));
			ret = 0;
		}
		break;
	}
#endif
	default:
		ret = ptrace_request(child, request, addr, data);
		break;
	}
248

L
Linus Torvalds 已提交
249 250 251
	return ret;
}

Y
Yuichi Nakamura 已提交
252
asmlinkage void do_syscall_trace(struct pt_regs *regs, int entryexit)
L
Linus Torvalds 已提交
253 254 255
{
	struct task_struct *tsk = current;

Y
Yuichi Nakamura 已提交
256 257 258 259
	if (unlikely(current->audit_context) && entryexit)
		audit_syscall_exit(AUDITSC_RESULT(regs->regs[0]),
				   regs->regs[0]);

S
Stuart Menefy 已提交
260 261
	if (!test_thread_flag(TIF_SYSCALL_TRACE) &&
	    !test_thread_flag(TIF_SINGLESTEP))
Y
Yuichi Nakamura 已提交
262
		goto out;
L
Linus Torvalds 已提交
263
	if (!(tsk->ptrace & PT_PTRACED))
Y
Yuichi Nakamura 已提交
264 265
		goto out;

L
Linus Torvalds 已提交
266 267
	/* the 0x80 provides a way for the tracing parent to distinguish
	   between a syscall stop and SIGTRAP delivery */
S
Stuart Menefy 已提交
268 269
	ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) &&
				 !test_thread_flag(TIF_SINGLESTEP) ? 0x80 : 0));
L
Linus Torvalds 已提交
270 271 272 273 274 275 276 277 278 279

	/*
	 * this isn't the same as continuing with a signal, but it will do
	 * for normal use.  strace only continues with a signal if the
	 * stopping signal is not SIGTRAP.  -brl
	 */
	if (tsk->exit_code) {
		send_sig(tsk->exit_code, tsk, 1);
		tsk->exit_code = 0;
	}
Y
Yuichi Nakamura 已提交
280 281 282 283 284 285 286

out:
	if (unlikely(current->audit_context) && !entryexit)
		audit_syscall_entry(AUDIT_ARCH_SH, regs->regs[3],
				    regs->regs[4], regs->regs[5],
				    regs->regs[6], regs->regs[7]);

L
Linus Torvalds 已提交
287
}