lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [day] [month] [year] [list]
Message-Id: <1173724493508-git-send-email-hskinnemoe@atmel.com>
Date:	Mon, 12 Mar 2007 19:34:53 +0100
From:	Haavard Skinnemoen <hskinnemoe@...el.com>
To:	roland@...hat.com
Cc:	Andrew Morton <akpm@...ux-foundation.org>,
	linux-kernel@...r.kernel.org,
	Haavard Skinnemoen <hskinnemoen@...el.com>
Subject: [PATCH -mm] AVR32: Implement utrace support

From: Haavard Skinnemoen <hskinnemoen@...el.com>

Rip out most of the ptrace code for AVR32 and replace it with the
much nicer utrace stuff. It builds in all possible combinations of
CONFIG_UTRACE and CONFIG_PTRACE, and it seems to work as far as I've
tested it with strace and some simple debugging with gdb.

Signed-off-by: Haavard Skinnemoen <hskinnemoen@...el.com>
---
 arch/avr32/kernel/entry-avr32b.S |   10 +-
 arch/avr32/kernel/process.c      |    2 -
 arch/avr32/kernel/ptrace.c       |  324 ++++++++++---------------------------
 include/asm-avr32/tracehook.h    |   69 ++++++++
 4 files changed, 165 insertions(+), 240 deletions(-)

diff --git a/arch/avr32/kernel/entry-avr32b.S b/arch/avr32/kernel/entry-avr32b.S
index eeb6679..81eaf20 100644
--- a/arch/avr32/kernel/entry-avr32b.S
+++ b/arch/avr32/kernel/entry-avr32b.S
@@ -229,15 +229,21 @@ ret_from_fork:
 	rjmp    syscall_exit_cont
 
 syscall_trace_enter:
-	pushm	r8-r12
+	mov	r12, sp		/* regs		*/
+	mov	r11, 0		/* is_exit	*/
 	rcall	syscall_trace
-	popm	r8-r12
+
+	/* syscall_trace may update r8, so reload r8-r12 from regs. */
+	sub	lr, sp, -REG_R12
+	ldm	lr, r8-r12
 	rjmp	syscall_trace_cont
 
 syscall_exit_work:
 	bld	r1, TIF_SYSCALL_TRACE
 	brcc	1f
 	unmask_interrupts
+	mov	r12, sp
+	mov	r11, 1
 	rcall	syscall_trace
 	mask_interrupts
 	ld.w	r1, r0[TI_flags]
diff --git a/arch/avr32/kernel/process.c b/arch/avr32/kernel/process.c
index 0b43259..f7cf378 100644
--- a/arch/avr32/kernel/process.c
+++ b/arch/avr32/kernel/process.c
@@ -229,8 +229,6 @@ asmlinkage int sys_execve(char __user *ufilename, char __user *__user *uargv,
 		goto out;
 
 	error = do_execve(filename, uargv, uenvp, regs);
-	if (error == 0)
-		current->ptrace &= ~PT_DTRACE;
 	putname(filename);
 
 out:
diff --git a/arch/avr32/kernel/ptrace.c b/arch/avr32/kernel/ptrace.c
index 6f4388f..396b2f9 100644
--- a/arch/avr32/kernel/ptrace.c
+++ b/arch/avr32/kernel/ptrace.c
@@ -5,23 +5,23 @@
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
  */
-#undef DEBUG
+
+#include <linux/compile.h>
+#include <linux/elf.h>
+#include <linux/errno.h>
 #include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/mm.h>
-#include <linux/smp_lock.h>
+#include <linux/module.h>
+#include <linux/notifier.h>
 #include <linux/ptrace.h>
-#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/tracehook.h>
 #include <linux/user.h>
-#include <linux/security.h>
-#include <linux/unistd.h>
-#include <linux/notifier.h>
 
-#include <asm/traps.h>
-#include <asm/uaccess.h>
-#include <asm/ocd.h>
-#include <asm/mmu_context.h>
 #include <asm/kdebug.h>
+#include <asm/mmu_context.h>
+#include <asm/ocd.h>
+
+#ifdef CONFIG_UTRACE
 
 static struct pt_regs *get_user_regs(struct task_struct *tsk)
 {
@@ -29,261 +29,113 @@ static struct pt_regs *get_user_regs(struct task_struct *tsk)
 				  THREAD_SIZE - sizeof(struct pt_regs));
 }
 
-static void ptrace_single_step(struct task_struct *tsk)
-{
-	pr_debug("ptrace_single_step: pid=%u, SR=0x%08lx\n",
-		 tsk->pid, tsk->thread.cpu_context.sr);
-	if (!(tsk->thread.cpu_context.sr & SR_D)) {
-		/*
-		 * Set a breakpoint at the current pc to force the
-		 * process into debug mode.  The syscall/exception
-		 * exit code will set a breakpoint at the return
-		 * address when this flag is set.
-		 */
-		pr_debug("ptrace_single_step: Setting TIF_BREAKPOINT\n");
-		set_tsk_thread_flag(tsk, TIF_BREAKPOINT);
-	}
-
-	/* The monitor code will do the actual step for us */
-	set_tsk_thread_flag(tsk, TIF_SINGLE_STEP);
-}
-
-/*
- * Called by kernel/ptrace.c when detaching
- *
- * Make sure any single step bits, etc. are not set
- */
-void ptrace_disable(struct task_struct *child)
-{
-	clear_tsk_thread_flag(child, TIF_SINGLE_STEP);
-}
-
-/*
- * Handle hitting a breakpoint
- */
-static void ptrace_break(struct task_struct *tsk, struct pt_regs *regs)
-{
-	siginfo_t info;
-
-	info.si_signo = SIGTRAP;
-	info.si_errno = 0;
-	info.si_code  = TRAP_BRKPT;
-	info.si_addr  = (void __user *)instruction_pointer(regs);
-
-	pr_debug("ptrace_break: Sending SIGTRAP to PID %u (pc = 0x%p)\n",
-		 tsk->pid, info.si_addr);
-	force_sig_info(SIGTRAP, &info, tsk);
-}
-
-/*
- * Read the word at offset "offset" into the task's "struct user". We
- * actually access the pt_regs struct stored on the kernel stack.
- */
-static int ptrace_read_user(struct task_struct *tsk, unsigned long offset,
-			    unsigned long __user *data)
+static int genregs_get(struct task_struct *target,
+		       const struct utrace_regset *regset,
+		       unsigned int pos, unsigned int count,
+		       void *kbuf, void __user *ubuf)
 {
-	unsigned long *regs;
-	unsigned long value;
-
-	pr_debug("ptrace_read_user(%p, %#lx, %p)\n",
-		 tsk, offset, data);
-
-	if (offset & 3 || offset >= sizeof(struct user)) {
-		printk("ptrace_read_user: invalid offset 0x%08lx\n", offset);
-		return -EIO;
-	}
-
-	regs = (unsigned long *)get_user_regs(tsk);
-
-	value = 0;
-	if (offset < sizeof(struct pt_regs))
-		value = regs[offset / sizeof(regs[0])];
+	struct pt_regs *regs = get_user_regs(target);
 
-	return put_user(value, data);
+	return utrace_regset_copyout(&pos, &count, &kbuf, &ubuf,
+				     regs, 0, -1);
 }
 
-/*
- * Write the word "value" to offset "offset" into the task's "struct
- * user". We actually access the pt_regs struct stored on the kernel
- * stack.
- */
-static int ptrace_write_user(struct task_struct *tsk, unsigned long offset,
-			     unsigned long value)
+static int genregs_set(struct task_struct *target,
+		       const struct utrace_regset *regset,
+		       unsigned int pos, unsigned int count,
+		       const void *kbuf, const void __user *ubuf)
 {
-	unsigned long *regs;
-
-	if (offset & 3 || offset >= sizeof(struct user)) {
-		printk("ptrace_write_user: invalid offset 0x%08lx\n", offset);
-		return -EIO;
-	}
-
-	if (offset >= sizeof(struct pt_regs))
-		return 0;
-
-	regs = (unsigned long *)get_user_regs(tsk);
-	regs[offset / sizeof(regs[0])] = value;
+	struct pt_regs *regs = get_user_regs(target);
 
-	return 0;
+	return utrace_regset_copyin(&pos, &count, &kbuf, &ubuf,
+				    regs, 0, -1);
 }
 
-static int ptrace_getregs(struct task_struct *tsk, void __user *uregs)
-{
-	struct pt_regs *regs = get_user_regs(tsk);
-
-	return copy_to_user(uregs, regs, sizeof(*regs)) ? -EFAULT : 0;
-}
-
-static int ptrace_setregs(struct task_struct *tsk, const void __user *uregs)
-{
-	struct pt_regs newregs;
-	int ret;
-
-	ret = -EFAULT;
-	if (copy_from_user(&newregs, uregs, sizeof(newregs)) == 0) {
-		struct pt_regs *regs = get_user_regs(tsk);
-
-		ret = -EINVAL;
-		if (valid_user_regs(&newregs)) {
-			*regs = newregs;
-			ret = 0;
-		}
-	}
-
-	return ret;
-}
-
-long arch_ptrace(struct task_struct *child, long request, long addr, long data)
+static const struct utrace_regset native_regsets[] = {
+	{
+		.n	= ELF_NGREG,
+		.size	= sizeof(long),
+		.align	= sizeof(long),
+		.get	= genregs_get,
+		.set	= genregs_set,
+	},
+	/*
+	 * Other register sets that probably would make sense:
+	 *   - Coprocessor registers (8 coprocs with 16 registers each)
+	 *   - TLS stuff
+	 */
+};
+
+const struct utrace_regset_view utrace_avr32_native_view = {
+	.name		= UTS_MACHINE,
+	.e_machine	= ELF_ARCH,
+	.regsets	= native_regsets,
+	.n		= ARRAY_SIZE(native_regsets),
+};
+EXPORT_SYMBOL_GPL(utrace_avr32_native_view);
+
+#ifdef CONFIG_PTRACE
+
+static const struct ptrace_layout_segment avr32_uarea[] = {
+	{ 0, ELF_NGREG * sizeof(long), 0, 0 },
+	{ 0, 0, -1, 0 },
+};
+
+int arch_ptrace(long *request, struct task_struct *child,
+		struct utrace_attached_engine *engine,
+		unsigned long addr, unsigned long data, long *val)
 {
-	unsigned long tmp;
-	int ret;
-
 	pr_debug("arch_ptrace(%ld, %d, %#lx, %#lx)\n",
-		 request, child->pid, addr, data);
+		 *request, child->pid, addr, data);
 
 	pr_debug("ptrace: Enabling monitor mode...\n");
 	__mtdr(DBGREG_DC, __mfdr(DBGREG_DC) | DC_MM | DC_DBE);
 
-	switch (request) {
-	/* Read the word at location addr in the child process */
-	case PTRACE_PEEKTEXT:
-	case PTRACE_PEEKDATA:
-		ret = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
-		if (ret == sizeof(tmp))
-			ret = put_user(tmp, (unsigned long __user *)data);
-		else
-			ret = -EIO;
-		break;
-
+	switch (*request) {
 	case PTRACE_PEEKUSR:
-		ret = ptrace_read_user(child, addr,
-				       (unsigned long __user *)data);
-		break;
-
-	/* Write the word in data at location addr */
-	case PTRACE_POKETEXT:
-	case PTRACE_POKEDATA:
-		ret = access_process_vm(child, addr, &data, sizeof(data), 1);
-		if (ret == sizeof(data))
-			ret = 0;
-		else
-			ret = -EIO;
-		break;
+		return ptrace_peekusr(child, engine, avr32_uarea, addr, data);
 
 	case PTRACE_POKEUSR:
-		ret = ptrace_write_user(child, addr, data);
-		break;
-
-	/* continue and stop at next (return from) syscall */
-	case PTRACE_SYSCALL:
-	/* restart after signal */
-	case PTRACE_CONT:
-		ret = -EIO;
-		if (!valid_signal(data))
-			break;
-		if (request == PTRACE_SYSCALL)
-			set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
-		else
-			clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
-		child->exit_code = data;
-		/* XXX: Are we sure no breakpoints are active here? */
-		wake_up_process(child);
-		ret = 0;
-		break;
-
-	/*
-	 * 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 exit.
-	 */
-	case PTRACE_KILL:
-		ret = 0;
-		if (child->exit_state == EXIT_ZOMBIE)
-			break;
-		child->exit_code = SIGKILL;
-		wake_up_process(child);
-		break;
-
-	/*
-	 * execute single instruction.
-	 */
-	case PTRACE_SINGLESTEP:
-		ret = -EIO;
-		if (!valid_signal(data))
-			break;
-		clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
-		ptrace_single_step(child);
-		child->exit_code = data;
-		wake_up_process(child);
-		ret = 0;
-		break;
-
-	/* Detach a process that was attached */
-	case PTRACE_DETACH:
-		ret = ptrace_detach(child, data);
+		return ptrace_pokeusr(child, engine, avr32_uarea, addr, data);
 		break;
 
 	case PTRACE_GETREGS:
-		ret = ptrace_getregs(child, (void __user *)data);
+		return ptrace_whole_regset(child, engine, data, 0, 0);
 		break;
 
 	case PTRACE_SETREGS:
-		ret = ptrace_setregs(child, (const void __user *)data);
-		break;
-
-	default:
-		ret = ptrace_request(child, request, addr, data);
+		return ptrace_whole_regset(child, engine, data, 0, 1);
 		break;
 	}
 
-	pr_debug("sys_ptrace returning %d (DC = 0x%08lx)\n", ret, __mfdr(DBGREG_DC));
-	return ret;
+	return -ENOSYS;
 }
+#endif /* CONFIG_PTRACE */
+#endif /* CONFIG_UTRACE */
 
-asmlinkage void syscall_trace(void)
+asmlinkage void syscall_trace(struct pt_regs *regs, int is_exit)
 {
-	pr_debug("syscall_trace called\n");
 	if (!test_thread_flag(TIF_SYSCALL_TRACE))
 		return;
-	if (!(current->ptrace & PT_PTRACED))
-		return;
 
-	pr_debug("syscall_trace: notifying parent\n");
-	/* The 0x80 provides a way for the tracing parent to
-	 * distinguish between a syscall stop and SIGTRAP delivery */
-	ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
-				 ? 0x80 : 0));
+	tracehook_report_syscall(regs, is_exit);
+}
 
-	/*
-	 * 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 (current->exit_code) {
-		pr_debug("syscall_trace: sending signal %d to PID %u\n",
-			 current->exit_code, current->pid);
-		send_sig(current->exit_code, current, 1);
-		current->exit_code = 0;
-	}
+/*
+ * Handle hitting a breakpoint
+ */
+static void do_breakpoint(struct task_struct *tsk, struct pt_regs *regs)
+{
+	siginfo_t info;
+
+	info.si_signo = SIGTRAP;
+	info.si_errno = 0;
+	info.si_code  = TRAP_BRKPT;
+	info.si_addr  = (void __user *)instruction_pointer(regs);
+
+	pr_debug("ptrace_break: Sending SIGTRAP to PID %u (pc = 0x%p)\n",
+		 tsk->pid, info.si_addr);
+	force_sig_info(SIGTRAP, &info, tsk);
 }
 
 asmlinkage void do_debug_priv(struct pt_regs *regs)
@@ -362,10 +214,10 @@ asmlinkage void do_debug(struct pt_regs *regs)
 			__mtdr(DBGREG_DC, dc);
 
 			clear_thread_flag(TIF_SINGLE_STEP);
-			ptrace_break(current, regs);
+			do_breakpoint(current, regs);
 		}
 	} else {
 		/* regular breakpoint */
-		ptrace_break(current, regs);
+		do_breakpoint(current, regs);
 	}
 }
diff --git a/include/asm-avr32/tracehook.h b/include/asm-avr32/tracehook.h
new file mode 100644
index 0000000..30027ef
--- /dev/null
+++ b/include/asm-avr32/tracehook.h
@@ -0,0 +1,69 @@
+/*
+ * Tracing hooks for AVR32
+ *
+ * Copyright (C) 2007 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef _ASM_AVR32_TRACEHOOK_H
+#define _ASM_AVR32_TRACEHOOK_H
+
+#include <linux/sched.h>
+
+#define ARCH_HAS_SINGLE_STEP	1
+
+static inline void tracehook_enable_single_step(struct task_struct *tsk)
+{
+	/*
+	 * If the process is stopped in debug mode, simply set
+	 * TIF_SINGLE_STEP to tell the monitor code to set the single
+	 * step bit in DC before returning.
+	 *
+	 * Otherwise, we need to set a breakpoint at the return
+	 * address before returning to userspace. TIF_BREAKPOINT will
+	 * tell the syscall/exception exit code to do this.
+	 */
+	if (!(tsk->thread.cpu_context.sr & SR_D))
+		set_tsk_thread_flag(tsk, TIF_BREAKPOINT);
+
+	set_tsk_thread_flag(tsk, TIF_SINGLE_STEP);
+}
+
+static inline void tracehook_disable_single_step(struct task_struct *tsk)
+{
+	clear_tsk_thread_flag(tsk, TIF_BREAKPOINT);
+	clear_tsk_thread_flag(tsk, TIF_SINGLE_STEP);
+}
+
+static inline int tracehook_single_step_enabled(struct task_struct *tsk)
+{
+	return test_tsk_thread_flag(tsk, TIF_SINGLE_STEP);
+}
+
+static inline void tracehook_enable_syscall_trace(struct task_struct *tsk)
+{
+	set_tsk_thread_flag(tsk, TIF_SYSCALL_TRACE);
+}
+
+static inline void tracehook_disable_syscall_trace(struct task_struct *tsk)
+{
+	clear_tsk_thread_flag(tsk, TIF_SYSCALL_TRACE);
+}
+
+static inline void tracehook_abort_syscall(struct pt_regs *regs)
+{
+	/* Invalid system call number => return -ENOSYS */
+	regs->r8 = -1;
+}
+
+extern const struct utrace_regset_view utrace_avr32_native_view;
+
+static inline const struct utrace_regset_view *
+utrace_native_view(struct task_struct *tsk)
+{
+	return &utrace_avr32_native_view;
+}
+
+#endif /* _ASM_AVR32_TRACEHOOK_H */
-- 
1.4.4.4

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ