2007-11-29 11:57:35

by Roland McGrath

[permalink] [raw]
Subject: [PATCH x86/mm 01/11] x86-32 thread_struct.debugreg


This replaces the debugreg[7] member of thread_struct with individual
members debugreg0, etc. This saves two words for the dummies 4 and 5,
and harmonizes the code between 32 and 64.

Signed-off-by: Roland McGrath <[email protected]>
---
arch/x86/kernel/process_32.c | 31 ++++++++++++++++++++-----------
arch/x86/kernel/ptrace_32.c | 29 ++++++++++++++++++++++++-----
arch/x86/kernel/signal_32.c | 4 ++--
arch/x86/kernel/traps_32.c | 4 ++--
arch/x86/power/cpu.c | 14 +++++++-------
include/asm-x86/processor_32.h | 7 ++++++-
6 files changed, 61 insertions(+), 28 deletions(-)

diff --git a/arch/x86/kernel/process_32.c b/arch/x86/kernel/process_32.c
index 8234054..d37bf10 100644
--- a/arch/x86/kernel/process_32.c
+++ b/arch/x86/kernel/process_32.c
@@ -425,7 +425,12 @@ void flush_thread(void)
{
struct task_struct *tsk = current;

- memset(tsk->thread.debugreg, 0, sizeof(unsigned long)*8);
+ tsk->thread.debugreg0 = 0;
+ tsk->thread.debugreg1 = 0;
+ tsk->thread.debugreg2 = 0;
+ tsk->thread.debugreg3 = 0;
+ tsk->thread.debugreg6 = 0;
+ tsk->thread.debugreg7 = 0;
memset(tsk->thread.tls_array, 0, sizeof(tsk->thread.tls_array));
clear_tsk_thread_flag(tsk, TIF_DEBUG);
/*
@@ -502,7 +507,6 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long sp,
*/
void dump_thread(struct pt_regs * regs, struct user * dump)
{
- int i;
u16 gs;

/* changed the size calculations - should hopefully work better. lbt */
@@ -513,8 +517,14 @@ void dump_thread(struct pt_regs * regs, struct user * dump)
dump->u_dsize = ((unsigned long) (current->mm->brk + (PAGE_SIZE-1))) >> PAGE_SHIFT;
dump->u_dsize -= dump->u_tsize;
dump->u_ssize = 0;
- for (i = 0; i < 8; i++)
- dump->u_debugreg[i] = current->thread.debugreg[i];
+ dump->u_debugreg[0] = current->thread.debugreg0;
+ dump->u_debugreg[1] = current->thread.debugreg1;
+ dump->u_debugreg[2] = current->thread.debugreg2;
+ dump->u_debugreg[3] = current->thread.debugreg3;
+ dump->u_debugreg[4] = 0;
+ dump->u_debugreg[5] = 0;
+ dump->u_debugreg[6] = current->thread.debugreg6;
+ dump->u_debugreg[7] = current->thread.debugreg7;

if (dump->start_stack < TASK_SIZE)
dump->u_ssize = ((unsigned long) (TASK_SIZE - dump->start_stack)) >> PAGE_SHIFT;
@@ -592,13 +602,13 @@ __switch_to_xtra(struct task_struct *prev_p, struct task_struct *next_p,
wrmsr(MSR_IA32_DEBUGCTLMSR, next->debugctlmsr, 0);

if (test_tsk_thread_flag(next_p, TIF_DEBUG)) {
- set_debugreg(next->debugreg[0], 0);
- set_debugreg(next->debugreg[1], 1);
- set_debugreg(next->debugreg[2], 2);
- set_debugreg(next->debugreg[3], 3);
+ set_debugreg(next->debugreg0, 0);
+ set_debugreg(next->debugreg1, 1);
+ set_debugreg(next->debugreg2, 2);
+ set_debugreg(next->debugreg3, 3);
/* no 4 and 5 */
- set_debugreg(next->debugreg[6], 6);
- set_debugreg(next->debugreg[7], 7);
+ set_debugreg(next->debugreg6, 6);
+ set_debugreg(next->debugreg7, 7);
}

#ifdef CONFIG_SECCOMP
@@ -849,4 +859,3 @@ unsigned long arch_randomize_brk(struct mm_struct *mm)
unsigned long range_end = mm->brk + 0x02000000;
return randomize_range(mm->brk, range_end, 0) ? : mm->brk;
}
-
diff --git a/arch/x86/kernel/ptrace_32.c b/arch/x86/kernel/ptrace_32.c
index 2607130..fed83d0 100644
--- a/arch/x86/kernel/ptrace_32.c
+++ b/arch/x86/kernel/ptrace_32.c
@@ -133,19 +133,39 @@ static unsigned long getreg(struct task_struct *child, unsigned long regno)
*/
static unsigned long ptrace_get_debugreg(struct task_struct *child, int n)
{
- return child->thread.debugreg[n];
+ switch (n) {
+ case 0: return child->thread.debugreg0;
+ case 1: return child->thread.debugreg1;
+ case 2: return child->thread.debugreg2;
+ case 3: return child->thread.debugreg3;
+ case 6: return child->thread.debugreg6;
+ case 7: return child->thread.debugreg7;
+ }
+ return 0;
}

static int ptrace_set_debugreg(struct task_struct *child,
int n, unsigned long data)
{
+ int i;
+
if (unlikely(n == 4 || n == 5))
return -EIO;

if (n < 4 && unlikely(data >= TASK_SIZE - 3))
return -EIO;

- if (n == 7) {
+ switch (n) {
+ case 0: child->thread.debugreg0 = data; break;
+ case 1: child->thread.debugreg1 = data; break;
+ case 2: child->thread.debugreg2 = data; break;
+ case 3: child->thread.debugreg3 = data; break;
+
+ case 6:
+ child->thread.debugreg6 = data;
+ break;
+
+ case 7:
/*
* Sanity-check data. Take one half-byte at once with
* check = (val >> (16 + 4*i)) & 0xf. It contains the
@@ -176,19 +196,18 @@ static int ptrace_set_debugreg(struct task_struct *child,
* 64-bit kernel), so the x86_64 mask value is 0x5454.
* See the AMD manual no. 24593 (AMD64 System Programming)
*/
- int i;
data &= ~DR_CONTROL_RESERVED;
for (i = 0; i < 4; i++)
if ((0x5f54 >> ((data >> (16 + 4*i)) & 0xf)) & 1)
return -EIO;
+ child->thread.debugreg7 = data;
if (data)
set_tsk_thread_flag(child, TIF_DEBUG);
else
clear_tsk_thread_flag(child, TIF_DEBUG);
+ break;
}

- child->thread.debugreg[n] = data;
-
return 0;
}

diff --git a/arch/x86/kernel/signal_32.c b/arch/x86/kernel/signal_32.c
index e8ab332..985c197 100644
--- a/arch/x86/kernel/signal_32.c
+++ b/arch/x86/kernel/signal_32.c
@@ -605,8 +605,8 @@ static void fastcall do_signal(struct pt_regs *regs)
* have been cleared if the watchpoint triggered
* inside the kernel.
*/
- if (unlikely(current->thread.debugreg[7]))
- set_debugreg(current->thread.debugreg[7], 7);
+ if (unlikely(current->thread.debugreg7))
+ set_debugreg(current->thread.debugreg7, 7);

/* Whee! Actually deliver the signal. */
if (handle_signal(signr, &info, &ka, oldset, regs) == 0) {
diff --git a/arch/x86/kernel/traps_32.c b/arch/x86/kernel/traps_32.c
index f34842a..7858ccb 100644
--- a/arch/x86/kernel/traps_32.c
+++ b/arch/x86/kernel/traps_32.c
@@ -853,7 +853,7 @@ fastcall void __kprobes do_debug(struct pt_regs * regs, long error_code)

/* Mask out spurious debug traps due to lazy DR7 setting */
if (condition & (DR_TRAP0|DR_TRAP1|DR_TRAP2|DR_TRAP3)) {
- if (!tsk->thread.debugreg[7])
+ if (!tsk->thread.debugreg7)
goto clear_dr7;
}

@@ -861,7 +861,7 @@ fastcall void __kprobes do_debug(struct pt_regs * regs, long error_code)
goto debug_vm86;

/* Save debug status register where ptrace can see it */
- tsk->thread.debugreg[6] = condition;
+ tsk->thread.debugreg6 = condition;

/*
* Single-stepping through TF: make sure we ignore any events in
diff --git a/arch/x86/power/cpu.c b/arch/x86/power/cpu.c
index 998fd3e..5a98dc3 100644
--- a/arch/x86/power/cpu.c
+++ b/arch/x86/power/cpu.c
@@ -74,14 +74,14 @@ static void fix_processor_context(void)
/*
* Now maybe reload the debug registers
*/
- if (current->thread.debugreg[7]){
- set_debugreg(current->thread.debugreg[0], 0);
- set_debugreg(current->thread.debugreg[1], 1);
- set_debugreg(current->thread.debugreg[2], 2);
- set_debugreg(current->thread.debugreg[3], 3);
+ if (current->thread.debugreg7) {
+ set_debugreg(current->thread.debugreg0, 0);
+ set_debugreg(current->thread.debugreg1, 1);
+ set_debugreg(current->thread.debugreg2, 2);
+ set_debugreg(current->thread.debugreg3, 3);
/* no 4 and 5 */
- set_debugreg(current->thread.debugreg[6], 6);
- set_debugreg(current->thread.debugreg[7], 7);
+ set_debugreg(current->thread.debugreg6, 6);
+ set_debugreg(current->thread.debugreg7, 7);
}

}
diff --git a/include/asm-x86/processor_32.h b/include/asm-x86/processor_32.h
index c85400f..d50a4b4 100644
--- a/include/asm-x86/processor_32.h
+++ b/include/asm-x86/processor_32.h
@@ -353,7 +353,12 @@ struct thread_struct {
unsigned long fs;
unsigned long gs;
/* Hardware debugging registers */
- unsigned long debugreg[8]; /* %%db0-7 debug registers */
+ unsigned long debugreg0;
+ unsigned long debugreg1;
+ unsigned long debugreg2;
+ unsigned long debugreg3;
+ unsigned long debugreg6;
+ unsigned long debugreg7;
/* fault info */
unsigned long cr2, trap_no, error_code;
/* floating point info */


2007-11-29 11:59:37

by Roland McGrath

[permalink] [raw]
Subject: [PATCH x86/mm 02/11] x86: ptrace_32 renamed


This renames ptrace_32.c back to ptrace.c, in preparation
for merging the 32/64 versions of these files.

Signed-off-by: Roland McGrath <[email protected]>
---
arch/x86/kernel/Makefile_32 | 3 +-
arch/x86/kernel/ptrace.c | 484 +++++++++++++++++++++++++++++++++++++++++++
arch/x86/kernel/ptrace_32.c | 484 -------------------------------------------
3 files changed, 486 insertions(+), 485 deletions(-)

diff --git a/arch/x86/kernel/Makefile_32 b/arch/x86/kernel/Makefile_32
index 369b297..8946cc8 100644
--- a/arch/x86/kernel/Makefile_32
+++ b/arch/x86/kernel/Makefile_32
@@ -6,10 +6,11 @@ extra-y := head_32.o init_task.o vmlinux.lds
CPPFLAGS_vmlinux.lds += -Ui386

obj-y := process_32.o signal_32.o entry_32.o traps_32.o irq_32.o \
- ptrace_32.o time_32.o ioport_32.o ldt.o setup_32.o i8259_32.o sys_i386_32.o \
+ time_32.o ioport_32.o ldt.o setup_32.o i8259_32.o sys_i386_32.o \
pci-dma_32.o i386_ksyms_32.o i387_32.o bootflag.o e820_32.o\
quirks.o i8237.o topology.o alternative.o i8253.o tsc_32.o rtc.o

+obj-y += ptrace.o
obj-y += tls.o
obj-y += step.o
obj-$(CONFIG_STACKTRACE) += stacktrace.o
diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c
new file mode 100644
index 0000000..fed83d0
--- /dev/null
+++ b/arch/x86/kernel/ptrace.c
@@ -0,0 +1,484 @@
+/* By Ross Biro 1/23/92 */
+/*
+ * Pentium III FXSR, SSE support
+ * Gareth Hughes <[email protected]>, May 2000
+ */
+
+#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/security.h>
+#include <linux/audit.h>
+#include <linux/seccomp.h>
+#include <linux/signal.h>
+
+#include <asm/uaccess.h>
+#include <asm/pgtable.h>
+#include <asm/system.h>
+#include <asm/processor.h>
+#include <asm/i387.h>
+#include <asm/debugreg.h>
+#include <asm/ldt.h>
+#include <asm/desc.h>
+
+/*
+ * does not yet catch signals sent when the child dies.
+ * in exit.c or in signal.c.
+ */
+
+/*
+ * Determines which flags the user has access to [1 = access, 0 = no access].
+ * Prohibits changing ID(21), VIP(20), VIF(19), VM(17), NT(14), IOPL(12-13), IF(9).
+ * Also masks reserved bits (31-22, 15, 5, 3, 1).
+ */
+#define FLAG_MASK 0x00050dd5
+
+static long *pt_regs_access(struct pt_regs *regs, unsigned long regno)
+{
+ BUILD_BUG_ON(offsetof(struct pt_regs, bx) != 0);
+ if (regno > FS)
+ --regno;
+ return &regs->bx + regno;
+}
+
+static int putreg(struct task_struct *child,
+ unsigned long regno, unsigned long value)
+{
+ struct pt_regs *regs = task_pt_regs(child);
+ regno >>= 2;
+ switch (regno) {
+ case GS:
+ if (value && (value & 3) != 3)
+ return -EIO;
+ child->thread.gs = value;
+ if (child == current)
+ /*
+ * The user-mode %gs is not affected by
+ * kernel entry, so we must update the CPU.
+ */
+ loadsegment(gs, value);
+ return 0;
+ case DS:
+ case ES:
+ case FS:
+ if (value && (value & 3) != 3)
+ return -EIO;
+ value &= 0xffff;
+ break;
+ case SS:
+ case CS:
+ if ((value & 3) != 3)
+ return -EIO;
+ value &= 0xffff;
+ break;
+ case EFL:
+ value &= FLAG_MASK;
+ /*
+ * If the user value contains TF, mark that
+ * it was not "us" (the debugger) that set it.
+ * If not, make sure it stays set if we had.
+ */
+ if (value & X86_EFLAGS_TF)
+ clear_tsk_thread_flag(child, TIF_FORCED_TF);
+ else if (test_tsk_thread_flag(child, TIF_FORCED_TF))
+ value |= X86_EFLAGS_TF;
+ value |= regs->flags & ~FLAG_MASK;
+ break;
+ }
+ *pt_regs_access(regs, regno) = value;
+ return 0;
+}
+
+static unsigned long getreg(struct task_struct *child, unsigned long regno)
+{
+ struct pt_regs *regs = task_pt_regs(child);
+ unsigned long retval = ~0UL;
+
+ regno >>= 2;
+ switch (regno) {
+ case EFL:
+ /*
+ * If the debugger set TF, hide it from the readout.
+ */
+ retval = regs->flags;
+ if (test_tsk_thread_flag(child, TIF_FORCED_TF))
+ retval &= ~X86_EFLAGS_TF;
+ break;
+ case GS:
+ retval = child->thread.gs;
+ if (child == current)
+ savesegment(gs, retval);
+ break;
+ case DS:
+ case ES:
+ case FS:
+ case SS:
+ case CS:
+ retval = 0xffff;
+ /* fall through */
+ default:
+ retval &= *pt_regs_access(regs, regno);
+ }
+ return retval;
+}
+
+/*
+ * This function is trivial and will be inlined by the compiler.
+ * Having it separates the implementation details of debug
+ * registers from the interface details of ptrace.
+ */
+static unsigned long ptrace_get_debugreg(struct task_struct *child, int n)
+{
+ switch (n) {
+ case 0: return child->thread.debugreg0;
+ case 1: return child->thread.debugreg1;
+ case 2: return child->thread.debugreg2;
+ case 3: return child->thread.debugreg3;
+ case 6: return child->thread.debugreg6;
+ case 7: return child->thread.debugreg7;
+ }
+ return 0;
+}
+
+static int ptrace_set_debugreg(struct task_struct *child,
+ int n, unsigned long data)
+{
+ int i;
+
+ if (unlikely(n == 4 || n == 5))
+ return -EIO;
+
+ if (n < 4 && unlikely(data >= TASK_SIZE - 3))
+ return -EIO;
+
+ switch (n) {
+ case 0: child->thread.debugreg0 = data; break;
+ case 1: child->thread.debugreg1 = data; break;
+ case 2: child->thread.debugreg2 = data; break;
+ case 3: child->thread.debugreg3 = data; break;
+
+ case 6:
+ child->thread.debugreg6 = data;
+ break;
+
+ case 7:
+ /*
+ * Sanity-check data. Take one half-byte at once with
+ * check = (val >> (16 + 4*i)) & 0xf. It contains the
+ * R/Wi and LENi bits; bits 0 and 1 are R/Wi, and bits
+ * 2 and 3 are LENi. Given a list of invalid values,
+ * we do mask |= 1 << invalid_value, so that
+ * (mask >> check) & 1 is a correct test for invalid
+ * values.
+ *
+ * R/Wi contains the type of the breakpoint /
+ * watchpoint, LENi contains the length of the watched
+ * data in the watchpoint case.
+ *
+ * The invalid values are:
+ * - LENi == 0x10 (undefined), so mask |= 0x0f00.
+ * - R/Wi == 0x10 (break on I/O reads or writes), so
+ * mask |= 0x4444.
+ * - R/Wi == 0x00 && LENi != 0x00, so we have mask |=
+ * 0x1110.
+ *
+ * Finally, mask = 0x0f00 | 0x4444 | 0x1110 == 0x5f54.
+ *
+ * See the Intel Manual "System Programming Guide",
+ * 15.2.4
+ *
+ * Note that LENi == 0x10 is defined on x86_64 in long
+ * mode (i.e. even for 32-bit userspace software, but
+ * 64-bit kernel), so the x86_64 mask value is 0x5454.
+ * See the AMD manual no. 24593 (AMD64 System Programming)
+ */
+ data &= ~DR_CONTROL_RESERVED;
+ for (i = 0; i < 4; i++)
+ if ((0x5f54 >> ((data >> (16 + 4*i)) & 0xf)) & 1)
+ return -EIO;
+ child->thread.debugreg7 = data;
+ if (data)
+ set_tsk_thread_flag(child, TIF_DEBUG);
+ else
+ clear_tsk_thread_flag(child, TIF_DEBUG);
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ * Called by kernel/ptrace.c when detaching..
+ *
+ * Make sure the single step bit is not set.
+ */
+void ptrace_disable(struct task_struct *child)
+{
+ user_disable_single_step(child);
+ clear_tsk_thread_flag(child, TIF_SYSCALL_EMU);
+}
+
+long arch_ptrace(struct task_struct *child, long request, long addr, long data)
+{
+ struct user * dummy = NULL;
+ int i, ret;
+ unsigned long __user *datap = (unsigned long __user *)data;
+
+ switch (request) {
+ /* when I and D space are separate, these will need to be fixed. */
+ case PTRACE_PEEKTEXT: /* read word at location addr. */
+ case PTRACE_PEEKDATA:
+ ret = generic_ptrace_peekdata(child, addr, data);
+ break;
+
+ /* read the word at location addr in the USER area. */
+ case PTRACE_PEEKUSR: {
+ unsigned long tmp;
+
+ ret = -EIO;
+ if ((addr & 3) || addr < 0 ||
+ addr > sizeof(struct user) - 3)
+ break;
+
+ tmp = 0; /* Default return condition */
+ if(addr < FRAME_SIZE*sizeof(long))
+ tmp = getreg(child, addr);
+ if(addr >= (long) &dummy->u_debugreg[0] &&
+ addr <= (long) &dummy->u_debugreg[7]){
+ addr -= (long) &dummy->u_debugreg[0];
+ addr = addr >> 2;
+ tmp = ptrace_get_debugreg(child, addr);
+ }
+ ret = put_user(tmp, datap);
+ 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:
+ ret = generic_ptrace_pokedata(child, addr, data);
+ break;
+
+ case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
+ ret = -EIO;
+ if ((addr & 3) || addr < 0 ||
+ addr > sizeof(struct user) - 3)
+ break;
+
+ if (addr < FRAME_SIZE*sizeof(long)) {
+ ret = putreg(child, addr, data);
+ break;
+ }
+ /* We need to be very careful here. We implicitly
+ want to modify a portion of the task_struct, and we
+ have to be selective about what portions we allow someone
+ to modify. */
+
+ ret = -EIO;
+ if(addr >= (long) &dummy->u_debugreg[0] &&
+ addr <= (long) &dummy->u_debugreg[7]){
+ addr -= (long) &dummy->u_debugreg;
+ addr = addr >> 2;
+ ret = ptrace_set_debugreg(child, addr, data);
+ }
+ break;
+
+ case PTRACE_GETREGS: { /* Get all gp regs from the child. */
+ if (!access_ok(VERIFY_WRITE, datap, FRAME_SIZE*sizeof(long))) {
+ ret = -EIO;
+ break;
+ }
+ for ( i = 0; i < FRAME_SIZE*sizeof(long); i += sizeof(long) ) {
+ __put_user(getreg(child, i), datap);
+ datap++;
+ }
+ ret = 0;
+ break;
+ }
+
+ case PTRACE_SETREGS: { /* Set all gp regs in the child. */
+ unsigned long tmp;
+ if (!access_ok(VERIFY_READ, datap, FRAME_SIZE*sizeof(long))) {
+ ret = -EIO;
+ break;
+ }
+ for ( i = 0; i < FRAME_SIZE*sizeof(long); i += sizeof(long) ) {
+ __get_user(tmp, datap);
+ putreg(child, i, tmp);
+ datap++;
+ }
+ ret = 0;
+ break;
+ }
+
+ case PTRACE_GETFPREGS: { /* Get the child FPU state. */
+ if (!access_ok(VERIFY_WRITE, datap,
+ sizeof(struct user_i387_struct))) {
+ ret = -EIO;
+ break;
+ }
+ ret = 0;
+ if (!tsk_used_math(child))
+ init_fpu(child);
+ get_fpregs((struct user_i387_struct __user *)data, child);
+ break;
+ }
+
+ case PTRACE_SETFPREGS: { /* Set the child FPU state. */
+ if (!access_ok(VERIFY_READ, datap,
+ sizeof(struct user_i387_struct))) {
+ ret = -EIO;
+ break;
+ }
+ set_stopped_child_used_math(child);
+ set_fpregs(child, (struct user_i387_struct __user *)data);
+ ret = 0;
+ break;
+ }
+
+ case PTRACE_GETFPXREGS: { /* Get the child extended FPU state. */
+ if (!access_ok(VERIFY_WRITE, datap,
+ sizeof(struct user_fxsr_struct))) {
+ ret = -EIO;
+ break;
+ }
+ if (!tsk_used_math(child))
+ init_fpu(child);
+ ret = get_fpxregs((struct user_fxsr_struct __user *)data, child);
+ break;
+ }
+
+ case PTRACE_SETFPXREGS: { /* Set the child extended FPU state. */
+ if (!access_ok(VERIFY_READ, datap,
+ sizeof(struct user_fxsr_struct))) {
+ ret = -EIO;
+ break;
+ }
+ set_stopped_child_used_math(child);
+ ret = set_fpxregs(child, (struct user_fxsr_struct __user *)data);
+ break;
+ }
+
+ case PTRACE_GET_THREAD_AREA:
+ if (addr < 0)
+ return -EIO;
+ ret = do_get_thread_area(child, addr,
+ (struct user_desc __user *) data);
+ break;
+
+ case PTRACE_SET_THREAD_AREA:
+ if (addr < 0)
+ return -EIO;
+ ret = do_set_thread_area(child, addr,
+ (struct user_desc __user *) data, 0);
+ break;
+
+ default:
+ ret = ptrace_request(child, request, addr, data);
+ break;
+ }
+
+ return ret;
+}
+
+void send_sigtrap(struct task_struct *tsk, struct pt_regs *regs, int error_code)
+{
+ struct siginfo info;
+
+ tsk->thread.trap_no = 1;
+ tsk->thread.error_code = error_code;
+
+ memset(&info, 0, sizeof(info));
+ info.si_signo = SIGTRAP;
+ info.si_code = TRAP_BRKPT;
+
+ /* User-mode ip? */
+ info.si_addr = user_mode_vm(regs) ? (void __user *) regs->ip : NULL;
+
+ /* Send us the fake SIGTRAP */
+ force_sig_info(SIGTRAP, &info, tsk);
+}
+
+/* notification of system call entry/exit
+ * - triggered by current->work.syscall_trace
+ */
+__attribute__((regparm(3)))
+int do_syscall_trace(struct pt_regs *regs, int entryexit)
+{
+ int is_sysemu = test_thread_flag(TIF_SYSCALL_EMU);
+ /*
+ * With TIF_SYSCALL_EMU set we want to ignore TIF_SINGLESTEP for syscall
+ * interception
+ */
+ int is_singlestep = !is_sysemu && test_thread_flag(TIF_SINGLESTEP);
+ int ret = 0;
+
+ /* do the secure computing check first */
+ if (!entryexit)
+ secure_computing(regs->orig_ax);
+
+ if (unlikely(current->audit_context)) {
+ if (entryexit)
+ audit_syscall_exit(AUDITSC_RESULT(regs->ax),
+ regs->ax);
+ /* Debug traps, when using PTRACE_SINGLESTEP, must be sent only
+ * on the syscall exit path. Normally, when TIF_SYSCALL_AUDIT is
+ * not used, entry.S will call us only on syscall exit, not
+ * entry; so when TIF_SYSCALL_AUDIT is used we must avoid
+ * calling send_sigtrap() on syscall entry.
+ *
+ * Note that when PTRACE_SYSEMU_SINGLESTEP is used,
+ * is_singlestep is false, despite his name, so we will still do
+ * the correct thing.
+ */
+ else if (is_singlestep)
+ goto out;
+ }
+
+ if (!(current->ptrace & PT_PTRACED))
+ goto out;
+
+ /* If a process stops on the 1st tracepoint with SYSCALL_TRACE
+ * and then is resumed with SYSEMU_SINGLESTEP, it will come in
+ * here. We have to check this and return */
+ if (is_sysemu && entryexit)
+ return 0;
+
+ /* Fake a debug trap */
+ if (is_singlestep)
+ send_sigtrap(current, regs, 0);
+
+ if (!test_thread_flag(TIF_SYSCALL_TRACE) && !is_sysemu)
+ goto out;
+
+ /* the 0x80 provides a way for the tracing parent to distinguish
+ between a syscall stop and SIGTRAP delivery */
+ /* Note that the debugger could change the result of test_thread_flag!*/
+ ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) ? 0x80:0));
+
+ /*
+ * 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) {
+ send_sig(current->exit_code, current, 1);
+ current->exit_code = 0;
+ }
+ ret = is_sysemu;
+out:
+ if (unlikely(current->audit_context) && !entryexit)
+ audit_syscall_entry(AUDIT_ARCH_I386, regs->orig_ax,
+ regs->bx, regs->cx, regs->dx, regs->si);
+ if (ret == 0)
+ return 0;
+
+ regs->orig_ax = -1; /* force skip of syscall restarting */
+ if (unlikely(current->audit_context))
+ audit_syscall_exit(AUDITSC_RESULT(regs->ax), regs->ax);
+ return 1;
+}
diff --git a/arch/x86/kernel/ptrace_32.c b/arch/x86/kernel/ptrace_32.c
deleted file mode 100644
index fed83d0..0000000
--- a/arch/x86/kernel/ptrace_32.c
+++ /dev/null
@@ -1,484 +0,0 @@
-/* By Ross Biro 1/23/92 */
-/*
- * Pentium III FXSR, SSE support
- * Gareth Hughes <[email protected]>, May 2000
- */
-
-#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/security.h>
-#include <linux/audit.h>
-#include <linux/seccomp.h>
-#include <linux/signal.h>
-
-#include <asm/uaccess.h>
-#include <asm/pgtable.h>
-#include <asm/system.h>
-#include <asm/processor.h>
-#include <asm/i387.h>
-#include <asm/debugreg.h>
-#include <asm/ldt.h>
-#include <asm/desc.h>
-
-/*
- * does not yet catch signals sent when the child dies.
- * in exit.c or in signal.c.
- */
-
-/*
- * Determines which flags the user has access to [1 = access, 0 = no access].
- * Prohibits changing ID(21), VIP(20), VIF(19), VM(17), NT(14), IOPL(12-13), IF(9).
- * Also masks reserved bits (31-22, 15, 5, 3, 1).
- */
-#define FLAG_MASK 0x00050dd5
-
-static long *pt_regs_access(struct pt_regs *regs, unsigned long regno)
-{
- BUILD_BUG_ON(offsetof(struct pt_regs, bx) != 0);
- if (regno > FS)
- --regno;
- return &regs->bx + regno;
-}
-
-static int putreg(struct task_struct *child,
- unsigned long regno, unsigned long value)
-{
- struct pt_regs *regs = task_pt_regs(child);
- regno >>= 2;
- switch (regno) {
- case GS:
- if (value && (value & 3) != 3)
- return -EIO;
- child->thread.gs = value;
- if (child == current)
- /*
- * The user-mode %gs is not affected by
- * kernel entry, so we must update the CPU.
- */
- loadsegment(gs, value);
- return 0;
- case DS:
- case ES:
- case FS:
- if (value && (value & 3) != 3)
- return -EIO;
- value &= 0xffff;
- break;
- case SS:
- case CS:
- if ((value & 3) != 3)
- return -EIO;
- value &= 0xffff;
- break;
- case EFL:
- value &= FLAG_MASK;
- /*
- * If the user value contains TF, mark that
- * it was not "us" (the debugger) that set it.
- * If not, make sure it stays set if we had.
- */
- if (value & X86_EFLAGS_TF)
- clear_tsk_thread_flag(child, TIF_FORCED_TF);
- else if (test_tsk_thread_flag(child, TIF_FORCED_TF))
- value |= X86_EFLAGS_TF;
- value |= regs->flags & ~FLAG_MASK;
- break;
- }
- *pt_regs_access(regs, regno) = value;
- return 0;
-}
-
-static unsigned long getreg(struct task_struct *child, unsigned long regno)
-{
- struct pt_regs *regs = task_pt_regs(child);
- unsigned long retval = ~0UL;
-
- regno >>= 2;
- switch (regno) {
- case EFL:
- /*
- * If the debugger set TF, hide it from the readout.
- */
- retval = regs->flags;
- if (test_tsk_thread_flag(child, TIF_FORCED_TF))
- retval &= ~X86_EFLAGS_TF;
- break;
- case GS:
- retval = child->thread.gs;
- if (child == current)
- savesegment(gs, retval);
- break;
- case DS:
- case ES:
- case FS:
- case SS:
- case CS:
- retval = 0xffff;
- /* fall through */
- default:
- retval &= *pt_regs_access(regs, regno);
- }
- return retval;
-}
-
-/*
- * This function is trivial and will be inlined by the compiler.
- * Having it separates the implementation details of debug
- * registers from the interface details of ptrace.
- */
-static unsigned long ptrace_get_debugreg(struct task_struct *child, int n)
-{
- switch (n) {
- case 0: return child->thread.debugreg0;
- case 1: return child->thread.debugreg1;
- case 2: return child->thread.debugreg2;
- case 3: return child->thread.debugreg3;
- case 6: return child->thread.debugreg6;
- case 7: return child->thread.debugreg7;
- }
- return 0;
-}
-
-static int ptrace_set_debugreg(struct task_struct *child,
- int n, unsigned long data)
-{
- int i;
-
- if (unlikely(n == 4 || n == 5))
- return -EIO;
-
- if (n < 4 && unlikely(data >= TASK_SIZE - 3))
- return -EIO;
-
- switch (n) {
- case 0: child->thread.debugreg0 = data; break;
- case 1: child->thread.debugreg1 = data; break;
- case 2: child->thread.debugreg2 = data; break;
- case 3: child->thread.debugreg3 = data; break;
-
- case 6:
- child->thread.debugreg6 = data;
- break;
-
- case 7:
- /*
- * Sanity-check data. Take one half-byte at once with
- * check = (val >> (16 + 4*i)) & 0xf. It contains the
- * R/Wi and LENi bits; bits 0 and 1 are R/Wi, and bits
- * 2 and 3 are LENi. Given a list of invalid values,
- * we do mask |= 1 << invalid_value, so that
- * (mask >> check) & 1 is a correct test for invalid
- * values.
- *
- * R/Wi contains the type of the breakpoint /
- * watchpoint, LENi contains the length of the watched
- * data in the watchpoint case.
- *
- * The invalid values are:
- * - LENi == 0x10 (undefined), so mask |= 0x0f00.
- * - R/Wi == 0x10 (break on I/O reads or writes), so
- * mask |= 0x4444.
- * - R/Wi == 0x00 && LENi != 0x00, so we have mask |=
- * 0x1110.
- *
- * Finally, mask = 0x0f00 | 0x4444 | 0x1110 == 0x5f54.
- *
- * See the Intel Manual "System Programming Guide",
- * 15.2.4
- *
- * Note that LENi == 0x10 is defined on x86_64 in long
- * mode (i.e. even for 32-bit userspace software, but
- * 64-bit kernel), so the x86_64 mask value is 0x5454.
- * See the AMD manual no. 24593 (AMD64 System Programming)
- */
- data &= ~DR_CONTROL_RESERVED;
- for (i = 0; i < 4; i++)
- if ((0x5f54 >> ((data >> (16 + 4*i)) & 0xf)) & 1)
- return -EIO;
- child->thread.debugreg7 = data;
- if (data)
- set_tsk_thread_flag(child, TIF_DEBUG);
- else
- clear_tsk_thread_flag(child, TIF_DEBUG);
- break;
- }
-
- return 0;
-}
-
-/*
- * Called by kernel/ptrace.c when detaching..
- *
- * Make sure the single step bit is not set.
- */
-void ptrace_disable(struct task_struct *child)
-{
- user_disable_single_step(child);
- clear_tsk_thread_flag(child, TIF_SYSCALL_EMU);
-}
-
-long arch_ptrace(struct task_struct *child, long request, long addr, long data)
-{
- struct user * dummy = NULL;
- int i, ret;
- unsigned long __user *datap = (unsigned long __user *)data;
-
- switch (request) {
- /* when I and D space are separate, these will need to be fixed. */
- case PTRACE_PEEKTEXT: /* read word at location addr. */
- case PTRACE_PEEKDATA:
- ret = generic_ptrace_peekdata(child, addr, data);
- break;
-
- /* read the word at location addr in the USER area. */
- case PTRACE_PEEKUSR: {
- unsigned long tmp;
-
- ret = -EIO;
- if ((addr & 3) || addr < 0 ||
- addr > sizeof(struct user) - 3)
- break;
-
- tmp = 0; /* Default return condition */
- if(addr < FRAME_SIZE*sizeof(long))
- tmp = getreg(child, addr);
- if(addr >= (long) &dummy->u_debugreg[0] &&
- addr <= (long) &dummy->u_debugreg[7]){
- addr -= (long) &dummy->u_debugreg[0];
- addr = addr >> 2;
- tmp = ptrace_get_debugreg(child, addr);
- }
- ret = put_user(tmp, datap);
- 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:
- ret = generic_ptrace_pokedata(child, addr, data);
- break;
-
- case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
- ret = -EIO;
- if ((addr & 3) || addr < 0 ||
- addr > sizeof(struct user) - 3)
- break;
-
- if (addr < FRAME_SIZE*sizeof(long)) {
- ret = putreg(child, addr, data);
- break;
- }
- /* We need to be very careful here. We implicitly
- want to modify a portion of the task_struct, and we
- have to be selective about what portions we allow someone
- to modify. */
-
- ret = -EIO;
- if(addr >= (long) &dummy->u_debugreg[0] &&
- addr <= (long) &dummy->u_debugreg[7]){
- addr -= (long) &dummy->u_debugreg;
- addr = addr >> 2;
- ret = ptrace_set_debugreg(child, addr, data);
- }
- break;
-
- case PTRACE_GETREGS: { /* Get all gp regs from the child. */
- if (!access_ok(VERIFY_WRITE, datap, FRAME_SIZE*sizeof(long))) {
- ret = -EIO;
- break;
- }
- for ( i = 0; i < FRAME_SIZE*sizeof(long); i += sizeof(long) ) {
- __put_user(getreg(child, i), datap);
- datap++;
- }
- ret = 0;
- break;
- }
-
- case PTRACE_SETREGS: { /* Set all gp regs in the child. */
- unsigned long tmp;
- if (!access_ok(VERIFY_READ, datap, FRAME_SIZE*sizeof(long))) {
- ret = -EIO;
- break;
- }
- for ( i = 0; i < FRAME_SIZE*sizeof(long); i += sizeof(long) ) {
- __get_user(tmp, datap);
- putreg(child, i, tmp);
- datap++;
- }
- ret = 0;
- break;
- }
-
- case PTRACE_GETFPREGS: { /* Get the child FPU state. */
- if (!access_ok(VERIFY_WRITE, datap,
- sizeof(struct user_i387_struct))) {
- ret = -EIO;
- break;
- }
- ret = 0;
- if (!tsk_used_math(child))
- init_fpu(child);
- get_fpregs((struct user_i387_struct __user *)data, child);
- break;
- }
-
- case PTRACE_SETFPREGS: { /* Set the child FPU state. */
- if (!access_ok(VERIFY_READ, datap,
- sizeof(struct user_i387_struct))) {
- ret = -EIO;
- break;
- }
- set_stopped_child_used_math(child);
- set_fpregs(child, (struct user_i387_struct __user *)data);
- ret = 0;
- break;
- }
-
- case PTRACE_GETFPXREGS: { /* Get the child extended FPU state. */
- if (!access_ok(VERIFY_WRITE, datap,
- sizeof(struct user_fxsr_struct))) {
- ret = -EIO;
- break;
- }
- if (!tsk_used_math(child))
- init_fpu(child);
- ret = get_fpxregs((struct user_fxsr_struct __user *)data, child);
- break;
- }
-
- case PTRACE_SETFPXREGS: { /* Set the child extended FPU state. */
- if (!access_ok(VERIFY_READ, datap,
- sizeof(struct user_fxsr_struct))) {
- ret = -EIO;
- break;
- }
- set_stopped_child_used_math(child);
- ret = set_fpxregs(child, (struct user_fxsr_struct __user *)data);
- break;
- }
-
- case PTRACE_GET_THREAD_AREA:
- if (addr < 0)
- return -EIO;
- ret = do_get_thread_area(child, addr,
- (struct user_desc __user *) data);
- break;
-
- case PTRACE_SET_THREAD_AREA:
- if (addr < 0)
- return -EIO;
- ret = do_set_thread_area(child, addr,
- (struct user_desc __user *) data, 0);
- break;
-
- default:
- ret = ptrace_request(child, request, addr, data);
- break;
- }
-
- return ret;
-}
-
-void send_sigtrap(struct task_struct *tsk, struct pt_regs *regs, int error_code)
-{
- struct siginfo info;
-
- tsk->thread.trap_no = 1;
- tsk->thread.error_code = error_code;
-
- memset(&info, 0, sizeof(info));
- info.si_signo = SIGTRAP;
- info.si_code = TRAP_BRKPT;
-
- /* User-mode ip? */
- info.si_addr = user_mode_vm(regs) ? (void __user *) regs->ip : NULL;
-
- /* Send us the fake SIGTRAP */
- force_sig_info(SIGTRAP, &info, tsk);
-}
-
-/* notification of system call entry/exit
- * - triggered by current->work.syscall_trace
- */
-__attribute__((regparm(3)))
-int do_syscall_trace(struct pt_regs *regs, int entryexit)
-{
- int is_sysemu = test_thread_flag(TIF_SYSCALL_EMU);
- /*
- * With TIF_SYSCALL_EMU set we want to ignore TIF_SINGLESTEP for syscall
- * interception
- */
- int is_singlestep = !is_sysemu && test_thread_flag(TIF_SINGLESTEP);
- int ret = 0;
-
- /* do the secure computing check first */
- if (!entryexit)
- secure_computing(regs->orig_ax);
-
- if (unlikely(current->audit_context)) {
- if (entryexit)
- audit_syscall_exit(AUDITSC_RESULT(regs->ax),
- regs->ax);
- /* Debug traps, when using PTRACE_SINGLESTEP, must be sent only
- * on the syscall exit path. Normally, when TIF_SYSCALL_AUDIT is
- * not used, entry.S will call us only on syscall exit, not
- * entry; so when TIF_SYSCALL_AUDIT is used we must avoid
- * calling send_sigtrap() on syscall entry.
- *
- * Note that when PTRACE_SYSEMU_SINGLESTEP is used,
- * is_singlestep is false, despite his name, so we will still do
- * the correct thing.
- */
- else if (is_singlestep)
- goto out;
- }
-
- if (!(current->ptrace & PT_PTRACED))
- goto out;
-
- /* If a process stops on the 1st tracepoint with SYSCALL_TRACE
- * and then is resumed with SYSEMU_SINGLESTEP, it will come in
- * here. We have to check this and return */
- if (is_sysemu && entryexit)
- return 0;
-
- /* Fake a debug trap */
- if (is_singlestep)
- send_sigtrap(current, regs, 0);
-
- if (!test_thread_flag(TIF_SYSCALL_TRACE) && !is_sysemu)
- goto out;
-
- /* the 0x80 provides a way for the tracing parent to distinguish
- between a syscall stop and SIGTRAP delivery */
- /* Note that the debugger could change the result of test_thread_flag!*/
- ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) ? 0x80:0));
-
- /*
- * 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) {
- send_sig(current->exit_code, current, 1);
- current->exit_code = 0;
- }
- ret = is_sysemu;
-out:
- if (unlikely(current->audit_context) && !entryexit)
- audit_syscall_entry(AUDIT_ARCH_I386, regs->orig_ax,
- regs->bx, regs->cx, regs->dx, regs->si);
- if (ret == 0)
- return 0;
-
- regs->orig_ax = -1; /* force skip of syscall restarting */
- if (unlikely(current->audit_context))
- audit_syscall_exit(AUDITSC_RESULT(regs->ax), regs->ax);
- return 1;
-}

2007-11-29 11:59:51

by Roland McGrath

[permalink] [raw]
Subject: [PATCH x86/mm 03/11] x86: ptrace FLAG_MASK cleanup


This cleans up the FLAG_MASK macro to use symbolic constants instead of a
magic number.

Signed-off-by: Roland McGrath <[email protected]>
---
arch/x86/kernel/ptrace.c | 11 ++++++++---
1 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c
index fed83d0..b71226d 100644
--- a/arch/x86/kernel/ptrace.c
+++ b/arch/x86/kernel/ptrace.c
@@ -32,10 +32,15 @@

/*
* Determines which flags the user has access to [1 = access, 0 = no access].
- * Prohibits changing ID(21), VIP(20), VIF(19), VM(17), NT(14), IOPL(12-13), IF(9).
- * Also masks reserved bits (31-22, 15, 5, 3, 1).
*/
-#define FLAG_MASK 0x00050dd5
+#define FLAG_MASK_32 ((unsigned long) \
+ (X86_EFLAGS_CF | X86_EFLAGS_PF | \
+ X86_EFLAGS_AF | X86_EFLAGS_ZF | \
+ X86_EFLAGS_SF | X86_EFLAGS_TF | \
+ X86_EFLAGS_DF | X86_EFLAGS_OF | \
+ X86_EFLAGS_RF | X86_EFLAGS_AC))
+
+#define FLAG_MASK FLAG_MASK_32

static long *pt_regs_access(struct pt_regs *regs, unsigned long regno)
{

2007-11-29 12:00:10

by Roland McGrath

[permalink] [raw]
Subject: [PATCH x86/mm 04/11] x86 ptrace getreg/putreg cleanup


This cleans up the getreg/putreg functions to move the special cases
(segment registers and eflags) out into their own subroutines.

Signed-off-by: Roland McGrath <[email protected]>
---
arch/x86/kernel/ptrace.c | 162 +++++++++++++++++++++++++++-------------------
1 files changed, 96 insertions(+), 66 deletions(-)

diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c
index b71226d..eaec75a 100644
--- a/arch/x86/kernel/ptrace.c
+++ b/arch/x86/kernel/ptrace.c
@@ -45,92 +45,122 @@
static long *pt_regs_access(struct pt_regs *regs, unsigned long regno)
{
BUILD_BUG_ON(offsetof(struct pt_regs, bx) != 0);
+ regno >>= 2;
if (regno > FS)
--regno;
return &regs->bx + regno;
}

-static int putreg(struct task_struct *child,
- unsigned long regno, unsigned long value)
+static u16 get_segment_reg(struct task_struct *task, unsigned long offset)
{
- struct pt_regs *regs = task_pt_regs(child);
- regno >>= 2;
- switch (regno) {
- case GS:
- if (value && (value & 3) != 3)
- return -EIO;
- child->thread.gs = value;
- if (child == current)
+ /*
+ * Returning the value truncates it to 16 bits.
+ */
+ unsigned int retval;
+ if (offset != offsetof(struct user_regs_struct, gs))
+ retval = *pt_regs_access(task_pt_regs(task), offset);
+ else {
+ retval = task->thread.gs;
+ if (task == current)
+ savesegment(gs, retval);
+ }
+ return retval;
+}
+
+static int set_segment_reg(struct task_struct *task,
+ unsigned long offset, u16 value)
+{
+ /*
+ * The value argument was already truncated to 16 bits.
+ */
+ if (value && (value & 3) != 3)
+ return -EIO;
+
+ if (offset != offsetof(struct user_regs_struct, gs))
+ *pt_regs_access(task_pt_regs(task), offset) = value;
+ else {
+ task->thread.gs = value;
+ if (task == current)
/*
* The user-mode %gs is not affected by
* kernel entry, so we must update the CPU.
*/
loadsegment(gs, value);
- return 0;
- case DS:
- case ES:
- case FS:
- if (value && (value & 3) != 3)
- return -EIO;
- value &= 0xffff;
- break;
- case SS:
- case CS:
- if ((value & 3) != 3)
- return -EIO;
- value &= 0xffff;
- break;
- case EFL:
- value &= FLAG_MASK;
- /*
- * If the user value contains TF, mark that
- * it was not "us" (the debugger) that set it.
- * If not, make sure it stays set if we had.
- */
- if (value & X86_EFLAGS_TF)
- clear_tsk_thread_flag(child, TIF_FORCED_TF);
- else if (test_tsk_thread_flag(child, TIF_FORCED_TF))
- value |= X86_EFLAGS_TF;
- value |= regs->flags & ~FLAG_MASK;
- break;
}
- *pt_regs_access(regs, regno) = value;
+
return 0;
}

-static unsigned long getreg(struct task_struct *child, unsigned long regno)
+static unsigned long get_flags(struct task_struct *task)
{
- struct pt_regs *regs = task_pt_regs(child);
- unsigned long retval = ~0UL;
+ unsigned long retval = task_pt_regs(task)->flags;
+
+ /*
+ * If the debugger set TF, hide it from the readout.
+ */
+ if (test_tsk_thread_flag(task, TIF_FORCED_TF))
+ retval &= ~X86_EFLAGS_TF;

- regno >>= 2;
- switch (regno) {
- case EFL:
- /*
- * If the debugger set TF, hide it from the readout.
- */
- retval = regs->flags;
- if (test_tsk_thread_flag(child, TIF_FORCED_TF))
- retval &= ~X86_EFLAGS_TF;
- break;
- case GS:
- retval = child->thread.gs;
- if (child == current)
- savesegment(gs, retval);
- break;
- case DS:
- case ES:
- case FS:
- case SS:
- case CS:
- retval = 0xffff;
- /* fall through */
- default:
- retval &= *pt_regs_access(regs, regno);
- }
return retval;
}

+static int set_flags(struct task_struct *task, unsigned long value)
+{
+ struct pt_regs *regs = task_pt_regs(task);
+
+ /*
+ * If the user value contains TF, mark that
+ * it was not "us" (the debugger) that set it.
+ * If not, make sure it stays set if we had.
+ */
+ if (value & X86_EFLAGS_TF)
+ clear_tsk_thread_flag(task, TIF_FORCED_TF);
+ else if (test_tsk_thread_flag(task, TIF_FORCED_TF))
+ value |= X86_EFLAGS_TF;
+
+ regs->flags = (regs->flags & ~FLAG_MASK) | (value & FLAG_MASK);
+
+ return 0;
+}
+
+static int putreg(struct task_struct *child,
+ unsigned long offset, unsigned long value)
+{
+ switch (offset) {
+ case offsetof(struct user_regs_struct, cs):
+ case offsetof(struct user_regs_struct, ds):
+ case offsetof(struct user_regs_struct, es):
+ case offsetof(struct user_regs_struct, fs):
+ case offsetof(struct user_regs_struct, gs):
+ case offsetof(struct user_regs_struct, ss):
+ return set_segment_reg(child, offset, value);
+
+ case offsetof(struct user_regs_struct, flags):
+ return set_flags(child, value);
+ }
+
+ *pt_regs_access(task_pt_regs(child), offset) = value;
+ return 0;
+}
+
+static unsigned long getreg(struct task_struct *task, unsigned long offset)
+{
+ switch (offset) {
+ case offsetof(struct user_regs_struct, cs):
+ case offsetof(struct user_regs_struct, ds):
+ case offsetof(struct user_regs_struct, es):
+ case offsetof(struct user_regs_struct, fs):
+ case offsetof(struct user_regs_struct, gs):
+ case offsetof(struct user_regs_struct, ss):
+ return get_segment_reg(task, offset);
+
+ case offsetof(struct user_regs_struct, flags):
+ return get_flags(task);
+ }
+
+ return *pt_regs_access(task_pt_regs(task), offset);
+}
+
/*
* This function is trivial and will be inlined by the compiler.
* Having it separates the implementation details of debug

2007-11-29 12:00:37

by Roland McGrath

[permalink] [raw]
Subject: [PATCH x86/mm 05/11] x86 ptrace getreg/putreg merge


This merges 64-bit support into the low-level register access
functions in arch/x86/kernel/ptrace.c, paving the way to share
this file between 32-bit and 64-bit builds.

Signed-off-by: Roland McGrath <[email protected]>
---
arch/x86/kernel/ptrace.c | 215 +++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 211 insertions(+), 4 deletions(-)

diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c
index eaec75a..f42f8d2 100644
--- a/arch/x86/kernel/ptrace.c
+++ b/arch/x86/kernel/ptrace.c
@@ -24,6 +24,8 @@
#include <asm/debugreg.h>
#include <asm/ldt.h>
#include <asm/desc.h>
+#include <asm/prctl.h>
+#include <asm/proto.h>

/*
* does not yet catch signals sent when the child dies.
@@ -40,6 +42,14 @@
X86_EFLAGS_DF | X86_EFLAGS_OF | \
X86_EFLAGS_RF | X86_EFLAGS_AC))

+/*
+ * Determines whether a value may be installed in a segment register.
+ */
+#define invalid_selector(value) \
+ ((value) != 0 && ((value) & SEGMENT_RPL_MASK) != USER_RPL)
+
+#ifdef CONFIG_X86_32
+
#define FLAG_MASK FLAG_MASK_32

static long *pt_regs_access(struct pt_regs *regs, unsigned long regno)
@@ -73,7 +83,7 @@ static int set_segment_reg(struct task_struct *task,
/*
* The value argument was already truncated to 16 bits.
*/
- if (value && (value & 3) != 3)
+ if (invalid_selector(value))
return -EIO;

if (offset != offsetof(struct user_regs_struct, gs))
@@ -91,6 +101,142 @@ static int set_segment_reg(struct task_struct *task,
return 0;
}

+static unsigned long debugreg_addr_limit(struct task_struct *task)
+{
+ return TASK_SIZE - 3;
+}
+
+#else /* CONFIG_X86_64 */
+
+#define FLAG_MASK (FLAG_MASK_32 | X86_EFLAGS_NT)
+
+static unsigned long *pt_regs_access(struct pt_regs *regs, unsigned long offset)
+{
+ BUILD_BUG_ON(offsetof(struct pt_regs, r15) != 0);
+ return &regs->r15 + (offset / sizeof(regs->r15));
+}
+
+static u16 get_segment_reg(struct task_struct *task, unsigned long offset)
+{
+ /*
+ * Returning the value truncates it to 16 bits.
+ */
+ unsigned int seg;
+
+ switch (offset) {
+ case offsetof(struct user_regs_struct, fs):
+ if (task == current) {
+ /* Older gas can't assemble movq %?s,%r?? */
+ asm("movl %%fs,%0" : "=r" (seg));
+ return seg;
+ }
+ return task->thread.fsindex;
+ case offsetof(struct user_regs_struct, gs):
+ if (task == current) {
+ asm("movl %%gs,%0" : "=r" (seg));
+ return seg;
+ }
+ return task->thread.gsindex;
+ case offsetof(struct user_regs_struct, ds):
+ if (task == current) {
+ asm("movl %%ds,%0" : "=r" (seg));
+ return seg;
+ }
+ return task->thread.ds;
+ case offsetof(struct user_regs_struct, es):
+ if (task == current) {
+ asm("movl %%es,%0" : "=r" (seg));
+ return seg;
+ }
+ return task->thread.es;
+
+ case offsetof(struct user_regs_struct, cs):
+ case offsetof(struct user_regs_struct, ss):
+ break;
+ }
+ return *pt_regs_access(task_pt_regs(task), offset);
+}
+
+static int set_segment_reg(struct task_struct *task,
+ unsigned long offset, u16 value)
+{
+ /*
+ * The value argument was already truncated to 16 bits.
+ */
+ if (invalid_selector(value))
+ return -EIO;
+
+ switch (offset) {
+ case offsetof(struct user_regs_struct,fs):
+ /*
+ * If this is setting fs as for normal 64-bit use but
+ * setting fs_base has implicitly changed it, leave it.
+ */
+ if ((value == FS_TLS_SEL && task->thread.fsindex == 0 &&
+ task->thread.fs != 0) ||
+ (value == 0 && task->thread.fsindex == FS_TLS_SEL &&
+ task->thread.fs == 0))
+ break;
+ task->thread.fsindex = value;
+ if (task == current)
+ loadsegment(fs, task->thread.fsindex);
+ break;
+ case offsetof(struct user_regs_struct,gs):
+ /*
+ * If this is setting gs as for normal 64-bit use but
+ * setting gs_base has implicitly changed it, leave it.
+ */
+ if ((value == GS_TLS_SEL && task->thread.gsindex == 0 &&
+ task->thread.gs != 0) ||
+ (value == 0 && task->thread.gsindex == GS_TLS_SEL &&
+ task->thread.gs == 0))
+ break;
+ task->thread.gsindex = value;
+ if (task == current)
+ load_gs_index(task->thread.gsindex);
+ break;
+ case offsetof(struct user_regs_struct,ds):
+ task->thread.ds = value;
+ if (task == current)
+ loadsegment(ds, task->thread.ds);
+ break;
+ case offsetof(struct user_regs_struct,es):
+ task->thread.es = value;
+ if (task == current)
+ loadsegment(es, task->thread.es);
+ break;
+
+ /*
+ * Can't actually change these in 64-bit mode.
+ */
+ case offsetof(struct user_regs_struct,cs):
+#ifdef CONFIG_IA32_EMULATION
+ if (test_tsk_thread_flag(task, TIF_IA32))
+ task_pt_regs(task)->cs = value;
+ break;
+#endif
+ case offsetof(struct user_regs_struct,ss):
+#ifdef CONFIG_IA32_EMULATION
+ if (test_tsk_thread_flag(task, TIF_IA32))
+ task_pt_regs(task)->ss = value;
+ break;
+#endif
+ }
+
+ return 0;
+}
+
+static unsigned long debugreg_addr_limit(struct task_struct *task)
+{
+#ifdef CONFIG_IA32_EMULATION
+ if (test_tsk_thread_flag(task, TIF_IA32))
+ return IA32_PAGE_OFFSET - 3;
+#endif
+ return TASK_SIZE64 - 7;
+}
+
+#endif /* CONFIG_X86_32 */
+
static unsigned long get_flags(struct task_struct *task)
{
unsigned long retval = task_pt_regs(task)->flags;
@@ -137,6 +283,29 @@ static int putreg(struct task_struct *child,

case offsetof(struct user_regs_struct, flags):
return set_flags(child, value);
+
+#ifdef CONFIG_X86_64
+ case offsetof(struct user_regs_struct,fs_base):
+ if (value >= TASK_SIZE_OF(child))
+ return -EIO;
+ /*
+ * When changing the segment base, use do_arch_prctl
+ * to set either thread.fs or thread.fsindex and the
+ * corresponding GDT slot.
+ */
+ if (child->thread.fs != value)
+ return do_arch_prctl(child, ARCH_SET_FS, value);
+ return 0;
+ case offsetof(struct user_regs_struct,gs_base):
+ /*
+ * Exactly the same here as the %fs handling above.
+ */
+ if (value >= TASK_SIZE_OF(child))
+ return -EIO;
+ if (child->thread.gs != value)
+ return do_arch_prctl(child, ARCH_SET_GS, value);
+ return 0;
+#endif
}

*pt_regs_access(task_pt_regs(child), offset) = value;
@@ -156,6 +325,37 @@ static unsigned long getreg(struct task_struct *task, unsigned long offset)

case offsetof(struct user_regs_struct, flags):
return get_flags(task);
+
+#ifdef CONFIG_X86_64
+ case offsetof(struct user_regs_struct, fs_base): {
+ /*
+ * do_arch_prctl may have used a GDT slot instead of
+ * the MSR. To userland, it appears the same either
+ * way, except the %fs segment selector might not be 0.
+ */
+ unsigned int seg = task->thread.fsindex;
+ if (task->thread.fs != 0)
+ return task->thread.fs;
+ if (task == current)
+ asm("movl %%fs,%0" : "=r" (seg));
+ if (seg != FS_TLS_SEL)
+ return 0;
+ return get_desc_base(&task->thread.tls_array[FS_TLS]);
+ }
+ case offsetof(struct user_regs_struct, gs_base): {
+ /*
+ * Exactly the same here as the %fs handling above.
+ */
+ unsigned int seg = task->thread.gsindex;
+ if (task->thread.gs != 0)
+ return task->thread.gs;
+ if (task == current)
+ asm("movl %%gs,%0" : "=r" (seg));
+ if (seg != GS_TLS_SEL)
+ return 0;
+ return get_desc_base(&task->thread.tls_array[GS_TLS]);
+ }
+#endif
}

return *pt_regs_access(task_pt_regs(task), offset);
@@ -187,7 +387,7 @@ static int ptrace_set_debugreg(struct task_struct *child,
if (unlikely(n == 4 || n == 5))
return -EIO;

- if (n < 4 && unlikely(data >= TASK_SIZE - 3))
+ if (n < 4 && unlikely(data >= debugreg_addr_limit(child)))
return -EIO;

switch (n) {
@@ -197,6 +397,8 @@ static int ptrace_set_debugreg(struct task_struct *child,
case 3: child->thread.debugreg3 = data; break;

case 6:
+ if ((data & ~0xffffffffUL) != 0)
+ return -EIO;
child->thread.debugreg6 = data;
break;

@@ -215,7 +417,7 @@ static int ptrace_set_debugreg(struct task_struct *child,
* data in the watchpoint case.
*
* The invalid values are:
- * - LENi == 0x10 (undefined), so mask |= 0x0f00.
+ * - LENi == 0x10 (undefined), so mask |= 0x0f00. [32-bit]
* - R/Wi == 0x10 (break on I/O reads or writes), so
* mask |= 0x4444.
* - R/Wi == 0x00 && LENi != 0x00, so we have mask |=
@@ -231,9 +433,14 @@ static int ptrace_set_debugreg(struct task_struct *child,
* 64-bit kernel), so the x86_64 mask value is 0x5454.
* See the AMD manual no. 24593 (AMD64 System Programming)
*/
+#ifdef CONFIG_X86_32
+#define DR7_MASK 0x5f54
+#else
+#define DR7_MASK 0x5554
+#endif
data &= ~DR_CONTROL_RESERVED;
for (i = 0; i < 4; i++)
- if ((0x5f54 >> ((data >> (16 + 4*i)) & 0xf)) & 1)
+ if ((DR7_MASK >> ((data >> (16 + 4*i)) & 0xf)) & 1)
return -EIO;
child->thread.debugreg7 = data;
if (data)

2007-11-29 12:00:54

by Roland McGrath

[permalink] [raw]
Subject: [PATCH x86/mm 08/11] x86 ia32 ptrace getreg/putreg merge


This reimplements the 64-bit IA32-emulation register access
functions in arch/x86/kernel/ptrace.c, where they can share
some guts with the native access functions directly.

These functions are not used yet, but this paves the way to move
IA32 ptrace support into this file to share its local functions.

Signed-off-by: Roland McGrath <[email protected]>
---
arch/x86/kernel/ptrace.c | 126 ++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 126 insertions(+), 0 deletions(-)

diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c
index 2eac631..bac5058 100644
--- a/arch/x86/kernel/ptrace.c
+++ b/arch/x86/kernel/ptrace.c
@@ -632,6 +632,132 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
return ret;
}

+#ifdef CONFIG_IA32_EMULATION
+
+#include <asm/user32.h>
+
+#define R32(l,q) \
+ case offsetof(struct user32, regs.l): \
+ regs->q = value; break
+
+#define SEG32(rs) \
+ case offsetof(struct user32, regs.rs): \
+ return set_segment_reg(child, \
+ offsetof(struct user_regs_struct, rs), \
+ value); \
+ break
+
+static int putreg32(struct task_struct *child, unsigned regno, u32 value)
+{
+ struct pt_regs *regs = task_pt_regs(child);
+
+ switch (regno) {
+
+ SEG32(cs);
+ SEG32(ds);
+ SEG32(es);
+ SEG32(fs);
+ SEG32(gs);
+ SEG32(ss);
+
+ R32(ebx, bx);
+ R32(ecx, cx);
+ R32(edx, dx);
+ R32(edi, di);
+ R32(esi, si);
+ R32(ebp, bp);
+ R32(eax, ax);
+ R32(orig_eax, orig_ax);
+ R32(eip, ip);
+ R32(esp, sp);
+
+ case offsetof(struct user32, regs.eflags):
+ return set_flags(child, value);
+
+ case offsetof(struct user32, u_debugreg[0]) ...
+ offsetof(struct user32, u_debugreg[7]):
+ regno -= offsetof(struct user32, u_debugreg[0]);
+ return ptrace_set_debugreg(child, regno / 4, value);
+
+ default:
+ if (regno > sizeof(struct user32) || (regno & 3))
+ return -EIO;
+
+ /*
+ * Other dummy fields in the virtual user structure
+ * are ignored
+ */
+ break;
+ }
+ return 0;
+}
+
+#undef R32
+#undef SEG32
+
+#define R32(l,q) \
+ case offsetof(struct user32, regs.l): \
+ *val = regs->q; break
+
+#define SEG32(rs) \
+ case offsetof(struct user32, regs.rs): \
+ *val = get_segment_reg(child, \
+ offsetof(struct user_regs_struct, rs)); \
+ break
+
+static int getreg32(struct task_struct *child, unsigned regno, u32 *val)
+{
+ struct pt_regs *regs = task_pt_regs(child);
+
+ switch (regno) {
+
+ SEG32(ds);
+ SEG32(es);
+ SEG32(fs);
+ SEG32(gs);
+
+ R32(cs, cs);
+ R32(ss, ss);
+ R32(ebx, bx);
+ R32(ecx, cx);
+ R32(edx, dx);
+ R32(edi, di);
+ R32(esi, si);
+ R32(ebp, bp);
+ R32(eax, ax);
+ R32(orig_eax, orig_ax);
+ R32(eip, ip);
+ R32(esp, sp);
+
+ case offsetof(struct user32, regs.eflags):
+ *val = get_flags(child);
+ break;
+
+ case offsetof(struct user32, u_debugreg[0]) ...
+ offsetof(struct user32, u_debugreg[7]):
+ regno -= offsetof(struct user32, u_debugreg[0]);
+ *val = ptrace_get_debugreg(child, regno / 4);
+ break;
+
+ default:
+ if (regno > sizeof(struct user32) || (regno & 3))
+ return -EIO;
+
+ /*
+ * Other dummy fields in the virtual user structure
+ * are ignored
+ */
+ *val = 0;
+ break;
+ }
+ return 0;
+}
+
+#undef R32
+#undef SEG32
+
+#endif /* CONFIG_IA32_EMULATION */
+
#ifdef CONFIG_X86_32

void send_sigtrap(struct task_struct *tsk, struct pt_regs *regs, int error_code)

2007-11-29 12:01:22

by Roland McGrath

[permalink] [raw]
Subject: [PATCH x86/mm 09/11] x86 ia32 ptrace arch merge


This moves the sys32_ptrace code into arch/x86/kernel/ptrace.c,
verbatim except for a few hard-coded sizes replaced with sizeof.
Here this code can use the shared local functions in this file.

Signed-off-by: Roland McGrath <[email protected]>
---
arch/x86/kernel/ptrace.c | 214 ++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 214 insertions(+), 0 deletions(-)

diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c
index bac5058..b3433e1 100644
--- a/arch/x86/kernel/ptrace.c
+++ b/arch/x86/kernel/ptrace.c
@@ -634,6 +634,10 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)

#ifdef CONFIG_IA32_EMULATION

+#include <linux/compat.h>
+#include <linux/syscalls.h>
+#include <asm/ia32.h>
+#include <asm/fpu32.h>
#include <asm/user32.h>

#define R32(l,q) \
@@ -756,6 +760,216 @@ static int getreg32(struct task_struct *child, unsigned regno, u32 *val)
#undef R32
#undef SEG32

+static long ptrace32_siginfo(unsigned request, u32 pid, u32 addr, u32 data)
+{
+ siginfo_t __user *si = compat_alloc_user_space(sizeof(siginfo_t));
+ compat_siginfo_t __user *si32 = compat_ptr(data);
+ siginfo_t ssi;
+ int ret;
+
+ if (request == PTRACE_SETSIGINFO) {
+ memset(&ssi, 0, sizeof(siginfo_t));
+ ret = copy_siginfo_from_user32(&ssi, si32);
+ if (ret)
+ return ret;
+ if (copy_to_user(si, &ssi, sizeof(siginfo_t)))
+ return -EFAULT;
+ }
+ ret = sys_ptrace(request, pid, addr, (unsigned long)si);
+ if (ret)
+ return ret;
+ if (request == PTRACE_GETSIGINFO) {
+ if (copy_from_user(&ssi, si, sizeof(siginfo_t)))
+ return -EFAULT;
+ ret = copy_siginfo_to_user32(si32, &ssi);
+ }
+ return ret;
+}
+
+asmlinkage long sys32_ptrace(long request, u32 pid, u32 addr, u32 data)
+{
+ struct task_struct *child;
+ struct pt_regs *childregs;
+ void __user *datap = compat_ptr(data);
+ int ret;
+ __u32 val;
+
+ switch (request) {
+ case PTRACE_TRACEME:
+ case PTRACE_ATTACH:
+ case PTRACE_KILL:
+ case PTRACE_CONT:
+ case PTRACE_SINGLESTEP:
+ case PTRACE_SINGLEBLOCK:
+ case PTRACE_DETACH:
+ case PTRACE_SYSCALL:
+ case PTRACE_OLDSETOPTIONS:
+ case PTRACE_SETOPTIONS:
+ case PTRACE_SET_THREAD_AREA:
+ case PTRACE_GET_THREAD_AREA:
+ return sys_ptrace(request, pid, addr, data);
+
+ default:
+ return -EINVAL;
+
+ case PTRACE_PEEKTEXT:
+ case PTRACE_PEEKDATA:
+ case PTRACE_POKEDATA:
+ case PTRACE_POKETEXT:
+ case PTRACE_POKEUSR:
+ case PTRACE_PEEKUSR:
+ case PTRACE_GETREGS:
+ case PTRACE_SETREGS:
+ case PTRACE_SETFPREGS:
+ case PTRACE_GETFPREGS:
+ case PTRACE_SETFPXREGS:
+ case PTRACE_GETFPXREGS:
+ case PTRACE_GETEVENTMSG:
+ break;
+
+ case PTRACE_SETSIGINFO:
+ case PTRACE_GETSIGINFO:
+ return ptrace32_siginfo(request, pid, addr, data);
+ }
+
+ child = ptrace_get_task_struct(pid);
+ if (IS_ERR(child))
+ return PTR_ERR(child);
+
+ ret = ptrace_check_attach(child, request == PTRACE_KILL);
+ if (ret < 0)
+ goto out;
+
+ childregs = task_pt_regs(child);
+
+ switch (request) {
+ case PTRACE_PEEKDATA:
+ case PTRACE_PEEKTEXT:
+ ret = 0;
+ if (access_process_vm(child, addr, &val, sizeof(u32), 0) !=
+ sizeof(u32))
+ ret = -EIO;
+ else
+ ret = put_user(val, (unsigned int __user *)datap);
+ break;
+
+ case PTRACE_POKEDATA:
+ case PTRACE_POKETEXT:
+ ret = 0;
+ if (access_process_vm(child, addr, &data, sizeof(u32), 1) !=
+ sizeof(u32))
+ ret = -EIO;
+ break;
+
+ case PTRACE_PEEKUSR:
+ ret = getreg32(child, addr, &val);
+ if (ret == 0)
+ ret = put_user(val, (__u32 __user *)datap);
+ break;
+
+ case PTRACE_POKEUSR:
+ ret = putreg32(child, addr, data);
+ break;
+
+ case PTRACE_GETREGS: { /* Get all gp regs from the child. */
+ int i;
+
+ if (!access_ok(VERIFY_WRITE, datap, 16*4)) {
+ ret = -EIO;
+ break;
+ }
+ ret = 0;
+ for (i = 0; i < sizeof(struct user_regs_struct32); i += sizeof(__u32)) {
+ getreg32(child, i, &val);
+ ret |= __put_user(val, (u32 __user *)datap);
+ datap += sizeof(u32);
+ }
+ break;
+ }
+
+ case PTRACE_SETREGS: { /* Set all gp regs in the child. */
+ unsigned long tmp;
+ int i;
+
+ if (!access_ok(VERIFY_READ, datap, 16*4)) {
+ ret = -EIO;
+ break;
+ }
+ ret = 0;
+ for (i = 0; i < sizeof(struct user_regs_struct32); i += sizeof(u32)) {
+ ret |= __get_user(tmp, (u32 __user *)datap);
+ putreg32(child, i, tmp);
+ datap += sizeof(u32);
+ }
+ break;
+ }
+
+ case PTRACE_GETFPREGS:
+ ret = -EIO;
+ if (!access_ok(VERIFY_READ, compat_ptr(data),
+ sizeof(struct user_i387_struct)))
+ break;
+ save_i387_ia32(child, datap, childregs, 1);
+ ret = 0;
+ break;
+
+ case PTRACE_SETFPREGS:
+ ret = -EIO;
+ if (!access_ok(VERIFY_WRITE, datap,
+ sizeof(struct user_i387_struct)))
+ break;
+ ret = 0;
+ /* don't check EFAULT to be bug-to-bug compatible to i386 */
+ restore_i387_ia32(child, datap, 1);
+ break;
+
+ case PTRACE_GETFPXREGS: {
+ struct user32_fxsr_struct __user *u = datap;
+
+ init_fpu(child);
+ ret = -EIO;
+ if (!access_ok(VERIFY_WRITE, u, sizeof(*u)))
+ break;
+ ret = -EFAULT;
+ if (__copy_to_user(u, &child->thread.i387.fxsave, sizeof(*u)))
+ break;
+ ret = __put_user(childregs->cs, &u->fcs);
+ ret |= __put_user(child->thread.ds, &u->fos);
+ break;
+ }
+ case PTRACE_SETFPXREGS: {
+ struct user32_fxsr_struct __user *u = datap;
+
+ unlazy_fpu(child);
+ ret = -EIO;
+ if (!access_ok(VERIFY_READ, u, sizeof(*u)))
+ break;
+ /*
+ * no checking to be bug-to-bug compatible with i386.
+ * but silence warning
+ */
+ if (__copy_from_user(&child->thread.i387.fxsave, u, sizeof(*u)))
+ ;
+ set_stopped_child_used_math(child);
+ child->thread.i387.fxsave.mxcsr &= mxcsr_feature_mask;
+ ret = 0;
+ break;
+ }
+
+ case PTRACE_GETEVENTMSG:
+ ret = put_user(child->ptrace_message,
+ (unsigned int __user *)compat_ptr(data));
+ break;
+
+ default:
+ BUG();
+ }
+
+ out:
+ put_task_struct(child);
+ return ret;
+}
+
#endif /* CONFIG_IA32_EMULATION */

#ifdef CONFIG_X86_32

2007-11-29 12:01:39

by Roland McGrath

[permalink] [raw]
Subject: [PATCH x86/mm 06/11] x86 ptrace arch merge


This adds 64-bit support to arch_ptrace in arch/x86/kernel/ptrace.c,
so this function can be used for native ptrace on both 32 and 64.

Signed-off-by: Roland McGrath <[email protected]>
---
arch/x86/kernel/ptrace.c | 65 ++++++++++++++++++++++++---------------------
1 files changed, 35 insertions(+), 30 deletions(-)

diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c
index f42f8d2..f4355f3 100644
--- a/arch/x86/kernel/ptrace.c
+++ b/arch/x86/kernel/ptrace.c
@@ -461,12 +461,13 @@ static int ptrace_set_debugreg(struct task_struct *child,
void ptrace_disable(struct task_struct *child)
{
user_disable_single_step(child);
+#ifdef TIF_SYSCALL_EMU
clear_tsk_thread_flag(child, TIF_SYSCALL_EMU);
+#endif
}

long arch_ptrace(struct task_struct *child, long request, long addr, long data)
{
- struct user * dummy = NULL;
int i, ret;
unsigned long __user *datap = (unsigned long __user *)data;

@@ -482,18 +483,17 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
unsigned long tmp;

ret = -EIO;
- if ((addr & 3) || addr < 0 ||
- addr > sizeof(struct user) - 3)
+ if ((addr & (sizeof(data) - 1)) || addr < 0 ||
+ addr >= sizeof(struct user))
break;

tmp = 0; /* Default return condition */
- if(addr < FRAME_SIZE*sizeof(long))
+ if (addr < sizeof(struct user_regs_struct))
tmp = getreg(child, addr);
- if(addr >= (long) &dummy->u_debugreg[0] &&
- addr <= (long) &dummy->u_debugreg[7]){
- addr -= (long) &dummy->u_debugreg[0];
- addr = addr >> 2;
- tmp = ptrace_get_debugreg(child, addr);
+ else if (addr >= offsetof(struct user, u_debugreg[0]) &&
+ addr <= offsetof(struct user, u_debugreg[7])) {
+ addr -= offsetof(struct user, u_debugreg[0]);
+ tmp = ptrace_get_debugreg(child, addr / sizeof(data));
}
ret = put_user(tmp, datap);
break;
@@ -507,34 +507,26 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)

case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
ret = -EIO;
- if ((addr & 3) || addr < 0 ||
- addr > sizeof(struct user) - 3)
+ if ((addr & (sizeof(data) - 1)) || addr < 0 ||
+ addr >= sizeof(struct user))
break;

- if (addr < FRAME_SIZE*sizeof(long)) {
+ if (addr < sizeof(struct user_regs_struct))
ret = putreg(child, addr, data);
- break;
+ else if (addr >= offsetof(struct user, u_debugreg[0]) &&
+ addr <= offsetof(struct user, u_debugreg[7])) {
+ addr -= offsetof(struct user, u_debugreg[0]);
+ ret = ptrace_set_debugreg(child,
+ addr / sizeof(data), data);
}
- /* We need to be very careful here. We implicitly
- want to modify a portion of the task_struct, and we
- have to be selective about what portions we allow someone
- to modify. */
-
- ret = -EIO;
- if(addr >= (long) &dummy->u_debugreg[0] &&
- addr <= (long) &dummy->u_debugreg[7]){
- addr -= (long) &dummy->u_debugreg;
- addr = addr >> 2;
- ret = ptrace_set_debugreg(child, addr, data);
- }
- break;
+ break;

case PTRACE_GETREGS: { /* Get all gp regs from the child. */
- if (!access_ok(VERIFY_WRITE, datap, FRAME_SIZE*sizeof(long))) {
+ if (!access_ok(VERIFY_WRITE, datap, sizeof(struct user_regs_struct))) {
ret = -EIO;
break;
}
- for ( i = 0; i < FRAME_SIZE*sizeof(long); i += sizeof(long) ) {
+ for (i = 0; i < sizeof(struct user_regs_struct); i += sizeof(long)) {
__put_user(getreg(child, i), datap);
datap++;
}
@@ -544,11 +536,11 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)

case PTRACE_SETREGS: { /* Set all gp regs in the child. */
unsigned long tmp;
- if (!access_ok(VERIFY_READ, datap, FRAME_SIZE*sizeof(long))) {
+ if (!access_ok(VERIFY_READ, datap, sizeof(struct user_regs_struct))) {
ret = -EIO;
break;
}
- for ( i = 0; i < FRAME_SIZE*sizeof(long); i += sizeof(long) ) {
+ for (i = 0; i < sizeof(struct user_regs_struct); i += sizeof(long)) {
__get_user(tmp, datap);
putreg(child, i, tmp);
datap++;
@@ -582,6 +574,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
break;
}

+#ifdef CONFIG_X86_32
case PTRACE_GETFPXREGS: { /* Get the child extended FPU state. */
if (!access_ok(VERIFY_WRITE, datap,
sizeof(struct user_fxsr_struct))) {
@@ -604,7 +597,9 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
ret = set_fpxregs(child, (struct user_fxsr_struct __user *)data);
break;
}
+#endif

+#if defined CONFIG_X86_32 || defined CONFIG_IA32_EMULATION
case PTRACE_GET_THREAD_AREA:
if (addr < 0)
return -EIO;
@@ -618,6 +613,16 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
ret = do_set_thread_area(child, addr,
(struct user_desc __user *) data, 0);
break;
+#endif
+
+#ifdef CONFIG_X86_64
+ /* normal 64bit interface to access TLS data.
+ Works just like arch_prctl, except that the arguments
+ are reversed. */
+ case PTRACE_ARCH_PRCTL:
+ ret = do_arch_prctl(child, data, addr);
+ break;
+#endif

default:
ret = ptrace_request(child, request, addr, data);

2007-11-29 12:01:54

by Roland McGrath

[permalink] [raw]
Subject: [PATCH x86/mm 10/11] x86 ptrace merge complete


This switches over the 64-bit build to use the shared ptrace code,
instead of the old ptrace_64.c and arch/x86/ia32/ptrace32.c code.

Signed-off-by: Roland McGrath <[email protected]>
---
arch/x86/ia32/Makefile | 2 +-
arch/x86/kernel/Makefile_64 | 3 ++-
include/asm-x86/ptrace.h | 3 ---
3 files changed, 3 insertions(+), 5 deletions(-)

diff --git a/arch/x86/ia32/Makefile b/arch/x86/ia32/Makefile
index ea60886..ec71cfe 100644
--- a/arch/x86/ia32/Makefile
+++ b/arch/x86/ia32/Makefile
@@ -3,7 +3,7 @@
#

obj-$(CONFIG_IA32_EMULATION) := ia32entry.o sys_ia32.o ia32_signal.o \
- ia32_binfmt.o fpu32.o ptrace32.o
+ ia32_binfmt.o fpu32.o

sysv-$(CONFIG_SYSVIPC) := ipc32.o
obj-$(CONFIG_IA32_EMULATION) += $(sysv-y)
diff --git a/arch/x86/kernel/Makefile_64 b/arch/x86/kernel/Makefile_64
index d8d0a28..8c24601 100644
--- a/arch/x86/kernel/Makefile_64
+++ b/arch/x86/kernel/Makefile_64
@@ -7,12 +7,13 @@ CPPFLAGS_vmlinux.lds += -Ux86_64
EXTRA_AFLAGS := -traditional

obj-y := process_64.o signal_64.o entry_64.o traps_64.o irq_64.o \
- ptrace_64.o time_64.o ioport_64.o ldt.o setup_64.o i8259_64.o sys_x86_64.o \
+ time_64.o ioport_64.o ldt.o setup_64.o i8259_64.o sys_x86_64.o \
x8664_ksyms_64.o i387_64.o syscall_64.o vsyscall_64.o \
setup64.o bootflag.o e820_64.o reboot_64.o quirks.o i8237.o \
pci-dma_64.o pci-nommu_64.o alternative.o hpet.o tsc_64.o bugs_64.o \
i8253.o rtc.o

+obj-y += ptrace.o
obj-y += step.o

obj-$(CONFIG_IA32_EMULATION) += tls.o
diff --git a/include/asm-x86/ptrace.h b/include/asm-x86/ptrace.h
index 1b7a8b8..9228870 100644
--- a/include/asm-x86/ptrace.h
+++ b/include/asm-x86/ptrace.h
@@ -164,9 +164,6 @@ void signal_fault(struct pt_regs *regs, void __user *frame, char *where);

struct task_struct;

-extern unsigned long ptrace_get_debugreg(struct task_struct *child, int n);
-extern int ptrace_set_debugreg(struct task_struct *child, int n, unsigned long);
-
extern unsigned long
convert_rip_to_linear(struct task_struct *child, struct pt_regs *regs);

2007-11-29 12:02:38

by Roland McGrath

[permalink] [raw]
Subject: [PATCH x86/mm 07/11] x86 ptrace merge syscall trace


This moves the 64-bit syscall tracing functions into ptrace.c,
so that ptrace_64.c becomes entirely obsolete.

Signed-off-by: Roland McGrath <[email protected]>
---
arch/x86/kernel/ptrace.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 64 insertions(+), 0 deletions(-)

diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c
index f4355f3..2eac631 100644
--- a/arch/x86/kernel/ptrace.c
+++ b/arch/x86/kernel/ptrace.c
@@ -632,6 +632,8 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
return ret;
}

+#ifdef CONFIG_X86_32
+
void send_sigtrap(struct task_struct *tsk, struct pt_regs *regs, int error_code)
{
struct siginfo info;
@@ -729,3 +731,65 @@ out:
audit_syscall_exit(AUDITSC_RESULT(regs->ax), regs->ax);
return 1;
}
+
+#else /* CONFIG_X86_64 */
+
+static void syscall_trace(struct pt_regs *regs)
+{
+
+#if 0
+ printk("trace %s ip %lx sp %lx ax %d origrax %d caller %lx tiflags %x ptrace %x\n",
+ current->comm,
+ regs->ip, regs->sp, regs->ax, regs->orig_ax, __builtin_return_address(0),
+ current_thread_info()->flags, current->ptrace);
+#endif
+
+ ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
+ ? 0x80 : 0));
+ /*
+ * 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) {
+ send_sig(current->exit_code, current, 1);
+ current->exit_code = 0;
+ }
+}
+
+asmlinkage void syscall_trace_enter(struct pt_regs *regs)
+{
+ /* do the secure computing check first */
+ secure_computing(regs->orig_ax);
+
+ if (test_thread_flag(TIF_SYSCALL_TRACE)
+ && (current->ptrace & PT_PTRACED))
+ syscall_trace(regs);
+
+ if (unlikely(current->audit_context)) {
+ if (test_thread_flag(TIF_IA32)) {
+ audit_syscall_entry(AUDIT_ARCH_I386,
+ regs->orig_ax,
+ regs->bx, regs->cx,
+ regs->dx, regs->si);
+ } else {
+ audit_syscall_entry(AUDIT_ARCH_X86_64,
+ regs->orig_ax,
+ regs->di, regs->si,
+ regs->dx, regs->r10);
+ }
+ }
+}
+
+asmlinkage void syscall_trace_leave(struct pt_regs *regs)
+{
+ if (unlikely(current->audit_context))
+ audit_syscall_exit(AUDITSC_RESULT(regs->ax), regs->ax);
+
+ if ((test_thread_flag(TIF_SYSCALL_TRACE)
+ || test_thread_flag(TIF_SINGLESTEP))
+ && (current->ptrace & PT_PTRACED))
+ syscall_trace(regs);
+}
+
+#endif /* CONFIG_X86_32 */

2007-11-29 12:02:57

by Roland McGrath

[permalink] [raw]
Subject: [PATCH x86/mm 11/11] x86 ptrace merge removals


This removes the old separate 64-bit and ia32 ptrace source files.
They are no longer used.

Signed-off-by: Roland McGrath <[email protected]>
---
arch/x86/ia32/ptrace32.c | 411 -------------------------------------
arch/x86/kernel/ptrace_64.c | 470 -------------------------------------------
2 files changed, 0 insertions(+), 881 deletions(-)

diff --git a/arch/x86/ia32/ptrace32.c b/arch/x86/ia32/ptrace32.c
deleted file mode 100644
index d5663e2..0000000
--- a/arch/x86/ia32/ptrace32.c
+++ /dev/null
@@ -1,411 +0,0 @@
-/*
- * 32bit ptrace for x86-64.
- *
- * Copyright 2001,2002 Andi Kleen, SuSE Labs.
- * Some parts copied from arch/i386/kernel/ptrace.c. See that file for earlier
- * copyright.
- *
- * This allows to access 64bit processes too; but there is no way to
- * see the extended register contents.
- */
-
-#include <linux/kernel.h>
-#include <linux/stddef.h>
-#include <linux/sched.h>
-#include <linux/syscalls.h>
-#include <linux/unistd.h>
-#include <linux/mm.h>
-#include <linux/err.h>
-#include <linux/ptrace.h>
-#include <asm/ptrace.h>
-#include <asm/compat.h>
-#include <asm/uaccess.h>
-#include <asm/user32.h>
-#include <asm/user.h>
-#include <asm/errno.h>
-#include <asm/debugreg.h>
-#include <asm/i387.h>
-#include <asm/fpu32.h>
-#include <asm/ia32.h>
-
-/*
- * Determines which flags the user has access to [1 = access, 0 = no access].
- * Prohibits changing ID(21), VIP(20), VIF(19), VM(17), IOPL(12-13), IF(9).
- * Also masks reserved bits (31-22, 15, 5, 3, 1).
- */
-#define FLAG_MASK 0x54dd5UL
-
-#define R32(l,q) \
- case offsetof(struct user32, regs.l): \
- regs->q = val; break;
-
-static int putreg32(struct task_struct *child, unsigned regno, u32 val)
-{
- struct pt_regs *regs = task_pt_regs(child);
-
- switch (regno) {
- case offsetof(struct user32, regs.fs):
- if (val && (val & 3) != 3)
- return -EIO;
- child->thread.fsindex = val & 0xffff;
- if (child == current)
- loadsegment(fs, child->thread.fsindex);
- break;
- case offsetof(struct user32, regs.gs):
- if (val && (val & 3) != 3)
- return -EIO;
- child->thread.gsindex = val & 0xffff;
- if (child == current)
- load_gs_index(child->thread.gsindex);
- break;
- case offsetof(struct user32, regs.ds):
- if (val && (val & 3) != 3)
- return -EIO;
- child->thread.ds = val & 0xffff;
- if (child == current)
- loadsegment(ds, child->thread.ds);
- break;
- case offsetof(struct user32, regs.es):
- child->thread.es = val & 0xffff;
- if (child == current)
- loadsegment(es, child->thread.ds);
- break;
- case offsetof(struct user32, regs.ss):
- if ((val & 3) != 3)
- return -EIO;
- regs->ss = val & 0xffff;
- break;
- case offsetof(struct user32, regs.cs):
- if ((val & 3) != 3)
- return -EIO;
- regs->cs = val & 0xffff;
- break;
-
- R32(ebx, bx);
- R32(ecx, cx);
- R32(edx, dx);
- R32(edi, di);
- R32(esi, si);
- R32(ebp, bp);
- R32(eax, ax);
- R32(orig_eax, orig_ax);
- R32(eip, ip);
- R32(esp, sp);
-
- case offsetof(struct user32, regs.eflags):
- val &= FLAG_MASK;
- /*
- * If the user value contains TF, mark that
- * it was not "us" (the debugger) that set it.
- * If not, make sure it stays set if we had.
- */
- if (val & X86_EFLAGS_TF)
- clear_tsk_thread_flag(child, TIF_FORCED_TF);
- else if (test_tsk_thread_flag(child, TIF_FORCED_TF))
- val |= X86_EFLAGS_TF;
- regs->flags = val | (regs->flags & ~FLAG_MASK);
- break;
-
- case offsetof(struct user32, u_debugreg[0]) ...
- offsetof(struct user32, u_debugreg[7]):
- regno -= offsetof(struct user32, u_debugreg[0]);
- return ptrace_set_debugreg(child, regno / 4, val);
-
- default:
- if (regno > sizeof(struct user32) || (regno & 3))
- return -EIO;
-
- /*
- * Other dummy fields in the virtual user structure
- * are ignored
- */
- break;
- }
- return 0;
-}
-
-#undef R32
-
-#define R32(l,q) \
- case offsetof(struct user32, regs.l): \
- *val = regs->q; break
-
-static int getreg32(struct task_struct *child, unsigned regno, u32 *val)
-{
- struct pt_regs *regs = task_pt_regs(child);
-
- switch (regno) {
- case offsetof(struct user32, regs.fs):
- *val = child->thread.fsindex;
- if (child == current)
- asm("movl %%fs,%0" : "=r" (*val));
- break;
- case offsetof(struct user32, regs.gs):
- *val = child->thread.gsindex;
- if (child == current)
- asm("movl %%gs,%0" : "=r" (*val));
- break;
- case offsetof(struct user32, regs.ds):
- *val = child->thread.ds;
- if (child == current)
- asm("movl %%ds,%0" : "=r" (*val));
- break;
- case offsetof(struct user32, regs.es):
- *val = child->thread.es;
- if (child == current)
- asm("movl %%es,%0" : "=r" (*val));
- break;
-
- R32(cs, cs);
- R32(ss, ss);
- R32(ebx, bx);
- R32(ecx, cx);
- R32(edx, dx);
- R32(edi, di);
- R32(esi, si);
- R32(ebp, bp);
- R32(eax, ax);
- R32(orig_eax, orig_ax);
- R32(eip, ip);
- R32(esp, sp);
-
- case offsetof(struct user32, regs.eflags):
- /*
- * If the debugger set TF, hide it from the readout.
- */
- *val = regs->flags;
- if (test_tsk_thread_flag(child, TIF_FORCED_TF))
- *val &= ~X86_EFLAGS_TF;
- break;
-
- case offsetof(struct user32, u_debugreg[0]) ...
- offsetof(struct user32, u_debugreg[7]):
- regno -= offsetof(struct user32, u_debugreg[0]);
- *val = ptrace_get_debugreg(child, regno / 4);
- break;
-
- default:
- if (regno > sizeof(struct user32) || (regno & 3))
- return -EIO;
-
- /*
- * Other dummy fields in the virtual user structure
- * are ignored
- */
- *val = 0;
- break;
- }
- return 0;
-}
-
-#undef R32
-
-static long ptrace32_siginfo(unsigned request, u32 pid, u32 addr, u32 data)
-{
- siginfo_t __user *si = compat_alloc_user_space(sizeof(siginfo_t));
- compat_siginfo_t __user *si32 = compat_ptr(data);
- siginfo_t ssi;
- int ret;
-
- if (request == PTRACE_SETSIGINFO) {
- memset(&ssi, 0, sizeof(siginfo_t));
- ret = copy_siginfo_from_user32(&ssi, si32);
- if (ret)
- return ret;
- if (copy_to_user(si, &ssi, sizeof(siginfo_t)))
- return -EFAULT;
- }
- ret = sys_ptrace(request, pid, addr, (unsigned long)si);
- if (ret)
- return ret;
- if (request == PTRACE_GETSIGINFO) {
- if (copy_from_user(&ssi, si, sizeof(siginfo_t)))
- return -EFAULT;
- ret = copy_siginfo_to_user32(si32, &ssi);
- }
- return ret;
-}
-
-asmlinkage long sys32_ptrace(long request, u32 pid, u32 addr, u32 data)
-{
- struct task_struct *child;
- struct pt_regs *childregs;
- void __user *datap = compat_ptr(data);
- int ret;
- __u32 val;
-
- switch (request) {
- case PTRACE_TRACEME:
- case PTRACE_ATTACH:
- case PTRACE_KILL:
- case PTRACE_CONT:
- case PTRACE_SINGLESTEP:
- case PTRACE_SINGLEBLOCK:
- case PTRACE_DETACH:
- case PTRACE_SYSCALL:
- case PTRACE_OLDSETOPTIONS:
- case PTRACE_SETOPTIONS:
- case PTRACE_SET_THREAD_AREA:
- case PTRACE_GET_THREAD_AREA:
- return sys_ptrace(request, pid, addr, data);
-
- default:
- return -EINVAL;
-
- case PTRACE_PEEKTEXT:
- case PTRACE_PEEKDATA:
- case PTRACE_POKEDATA:
- case PTRACE_POKETEXT:
- case PTRACE_POKEUSR:
- case PTRACE_PEEKUSR:
- case PTRACE_GETREGS:
- case PTRACE_SETREGS:
- case PTRACE_SETFPREGS:
- case PTRACE_GETFPREGS:
- case PTRACE_SETFPXREGS:
- case PTRACE_GETFPXREGS:
- case PTRACE_GETEVENTMSG:
- break;
-
- case PTRACE_SETSIGINFO:
- case PTRACE_GETSIGINFO:
- return ptrace32_siginfo(request, pid, addr, data);
- }
-
- child = ptrace_get_task_struct(pid);
- if (IS_ERR(child))
- return PTR_ERR(child);
-
- ret = ptrace_check_attach(child, request == PTRACE_KILL);
- if (ret < 0)
- goto out;
-
- childregs = task_pt_regs(child);
-
- switch (request) {
- case PTRACE_PEEKDATA:
- case PTRACE_PEEKTEXT:
- ret = 0;
- if (access_process_vm(child, addr, &val, sizeof(u32), 0) !=
- sizeof(u32))
- ret = -EIO;
- else
- ret = put_user(val, (unsigned int __user *)datap);
- break;
-
- case PTRACE_POKEDATA:
- case PTRACE_POKETEXT:
- ret = 0;
- if (access_process_vm(child, addr, &data, sizeof(u32), 1) !=
- sizeof(u32))
- ret = -EIO;
- break;
-
- case PTRACE_PEEKUSR:
- ret = getreg32(child, addr, &val);
- if (ret == 0)
- ret = put_user(val, (__u32 __user *)datap);
- break;
-
- case PTRACE_POKEUSR:
- ret = putreg32(child, addr, data);
- break;
-
- case PTRACE_GETREGS: { /* Get all gp regs from the child. */
- int i;
-
- if (!access_ok(VERIFY_WRITE, datap, 16*4)) {
- ret = -EIO;
- break;
- }
- ret = 0;
- for (i = 0; i <= 16*4; i += sizeof(__u32)) {
- getreg32(child, i, &val);
- ret |= __put_user(val, (u32 __user *)datap);
- datap += sizeof(u32);
- }
- break;
- }
-
- case PTRACE_SETREGS: { /* Set all gp regs in the child. */
- unsigned long tmp;
- int i;
-
- if (!access_ok(VERIFY_READ, datap, 16*4)) {
- ret = -EIO;
- break;
- }
- ret = 0;
- for (i = 0; i <= 16*4; i += sizeof(u32)) {
- ret |= __get_user(tmp, (u32 __user *)datap);
- putreg32(child, i, tmp);
- datap += sizeof(u32);
- }
- break;
- }
-
- case PTRACE_GETFPREGS:
- ret = -EIO;
- if (!access_ok(VERIFY_READ, compat_ptr(data),
- sizeof(struct user_i387_struct)))
- break;
- save_i387_ia32(child, datap, childregs, 1);
- ret = 0;
- break;
-
- case PTRACE_SETFPREGS:
- ret = -EIO;
- if (!access_ok(VERIFY_WRITE, datap,
- sizeof(struct user_i387_struct)))
- break;
- ret = 0;
- /* don't check EFAULT to be bug-to-bug compatible to i386 */
- restore_i387_ia32(child, datap, 1);
- break;
-
- case PTRACE_GETFPXREGS: {
- struct user32_fxsr_struct __user *u = datap;
-
- init_fpu(child);
- ret = -EIO;
- if (!access_ok(VERIFY_WRITE, u, sizeof(*u)))
- break;
- ret = -EFAULT;
- if (__copy_to_user(u, &child->thread.i387.fxsave, sizeof(*u)))
- break;
- ret = __put_user(childregs->cs, &u->fcs);
- ret |= __put_user(child->thread.ds, &u->fos);
- break;
- }
- case PTRACE_SETFPXREGS: {
- struct user32_fxsr_struct __user *u = datap;
-
- unlazy_fpu(child);
- ret = -EIO;
- if (!access_ok(VERIFY_READ, u, sizeof(*u)))
- break;
- /*
- * no checking to be bug-to-bug compatible with i386.
- * but silence warning
- */
- if (__copy_from_user(&child->thread.i387.fxsave, u, sizeof(*u)))
- ;
- set_stopped_child_used_math(child);
- child->thread.i387.fxsave.mxcsr &= mxcsr_feature_mask;
- ret = 0;
- break;
- }
-
- case PTRACE_GETEVENTMSG:
- ret = put_user(child->ptrace_message,
- (unsigned int __user *)compat_ptr(data));
- break;
-
- default:
- BUG();
- }
-
- out:
- put_task_struct(child);
- return ret;
-}
diff --git a/arch/x86/kernel/ptrace_64.c b/arch/x86/kernel/ptrace_64.c
deleted file mode 100644
index 5979dbe..0000000
--- a/arch/x86/kernel/ptrace_64.c
+++ /dev/null
@@ -1,470 +0,0 @@
-/* By Ross Biro 1/23/92 */
-/*
- * Pentium III FXSR, SSE support
- * Gareth Hughes <[email protected]>, May 2000
- *
- * x86-64 port 2000-2002 Andi Kleen
- */
-
-#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/security.h>
-#include <linux/audit.h>
-#include <linux/seccomp.h>
-#include <linux/signal.h>
-
-#include <asm/uaccess.h>
-#include <asm/pgtable.h>
-#include <asm/system.h>
-#include <asm/processor.h>
-#include <asm/prctl.h>
-#include <asm/i387.h>
-#include <asm/debugreg.h>
-#include <asm/ldt.h>
-#include <asm/desc.h>
-#include <asm/proto.h>
-#include <asm/ia32.h>
-
-/*
- * does not yet catch signals sent when the child dies.
- * in exit.c or in signal.c.
- */
-
-/*
- * Determines which flags the user has access to [1 = access, 0 = no access].
- * Prohibits changing ID(21), VIP(20), VIF(19), VM(17), IOPL(12-13), IF(9).
- * Also masks reserved bits (63-22, 15, 5, 3, 1).
- */
-#define FLAG_MASK 0x54dd5UL
-
-/*
- * Called by kernel/ptrace.c when detaching..
- *
- * Make sure the single step bit is not set.
- */
-void ptrace_disable(struct task_struct *child)
-{
- user_disable_single_step(child);
-}
-
-static unsigned long *pt_regs_access(struct pt_regs *regs, unsigned long offset)
-{
- BUILD_BUG_ON(offsetof(struct pt_regs, r15) != 0);
- return &regs->r15 + (offset / sizeof(regs->r15));
-}
-
-static int putreg(struct task_struct *child,
- unsigned long regno, unsigned long value)
-{
- struct pt_regs *regs = task_pt_regs(child);
- switch (regno) {
- case offsetof(struct user_regs_struct,fs):
- if (value && (value & 3) != 3)
- return -EIO;
- child->thread.fsindex = value & 0xffff;
- if (child == current)
- loadsegment(fs, child->thread.fsindex);
- return 0;
- case offsetof(struct user_regs_struct,gs):
- if (value && (value & 3) != 3)
- return -EIO;
- child->thread.gsindex = value & 0xffff;
- if (child == current)
- load_gs_index(child->thread.gsindex);
- return 0;
- case offsetof(struct user_regs_struct,ds):
- if (value && (value & 3) != 3)
- return -EIO;
- child->thread.ds = value & 0xffff;
- if (child == current)
- loadsegment(ds, child->thread.ds);
- return 0;
- case offsetof(struct user_regs_struct,es):
- if (value && (value & 3) != 3)
- return -EIO;
- child->thread.es = value & 0xffff;
- if (child == current)
- loadsegment(es, child->thread.es);
- return 0;
- case offsetof(struct user_regs_struct,ss):
- if ((value & 3) != 3)
- return -EIO;
- value &= 0xffff;
- return 0;
- case offsetof(struct user_regs_struct,fs_base):
- if (value >= TASK_SIZE_OF(child))
- return -EIO;
- /*
- * When changing the segment base, use do_arch_prctl
- * to set either thread.fs or thread.fsindex and the
- * corresponding GDT slot.
- */
- if (child->thread.fs != value)
- return do_arch_prctl(child, ARCH_SET_FS, value);
- return 0;
- case offsetof(struct user_regs_struct,gs_base):
- /*
- * Exactly the same here as the %fs handling above.
- */
- if (value >= TASK_SIZE_OF(child))
- return -EIO;
- if (child->thread.gs != value)
- return do_arch_prctl(child, ARCH_SET_GS, value);
- return 0;
- case offsetof(struct user_regs_struct,flags):
- value &= FLAG_MASK;
- /*
- * If the user value contains TF, mark that
- * it was not "us" (the debugger) that set it.
- * If not, make sure it stays set if we had.
- */
- if (value & X86_EFLAGS_TF)
- clear_tsk_thread_flag(child, TIF_FORCED_TF);
- else if (test_tsk_thread_flag(child, TIF_FORCED_TF))
- value |= X86_EFLAGS_TF;
- value |= regs->flags & ~FLAG_MASK;
- break;
- case offsetof(struct user_regs_struct,cs):
- if ((value & 3) != 3)
- return -EIO;
- value &= 0xffff;
- break;
- }
- *pt_regs_access(regs, regno) = value;
- return 0;
-}
-
-static unsigned long getreg(struct task_struct *child, unsigned long regno)
-{
- struct pt_regs *regs = task_pt_regs(child);
- unsigned long val;
- unsigned int seg;
- switch (regno) {
- case offsetof(struct user_regs_struct, fs):
- if (child == current) {
- /* Older gas can't assemble movq %?s,%r?? */
- asm("movl %%fs,%0" : "=r" (seg));
- return seg;
- }
- return child->thread.fsindex;
- case offsetof(struct user_regs_struct, gs):
- if (child == current) {
- asm("movl %%gs,%0" : "=r" (seg));
- return seg;
- }
- return child->thread.gsindex;
- case offsetof(struct user_regs_struct, ds):
- if (child == current) {
- asm("movl %%ds,%0" : "=r" (seg));
- return seg;
- }
- return child->thread.ds;
- case offsetof(struct user_regs_struct, es):
- if (child == current) {
- asm("movl %%es,%0" : "=r" (seg));
- return seg;
- }
- return child->thread.es;
- case offsetof(struct user_regs_struct, fs_base):
- /*
- * do_arch_prctl may have used a GDT slot instead of
- * the MSR. To userland, it appears the same either
- * way, except the %fs segment selector might not be 0.
- */
- if (child->thread.fs != 0)
- return child->thread.fs;
- seg = child->thread.fsindex;
- if (child == current)
- asm("movl %%fs,%0" : "=r" (seg));
- if (seg != FS_TLS_SEL)
- return 0;
- return get_desc_base(&child->thread.tls_array[FS_TLS]);
- case offsetof(struct user_regs_struct, gs_base):
- /*
- * Exactly the same here as the %fs handling above.
- */
- if (child->thread.gs != 0)
- return child->thread.gs;
- seg = child->thread.gsindex;
- if (child == current)
- asm("movl %%gs,%0" : "=r" (seg));
- if (seg != GS_TLS_SEL)
- return 0;
- return get_desc_base(&child->thread.tls_array[GS_TLS]);
- case offsetof(struct user_regs_struct, flags):
- /*
- * If the debugger set TF, hide it from the readout.
- */
- val = regs->flags;
- if (test_tsk_thread_flag(child, TIF_IA32))
- val &= 0xffffffff;
- if (test_tsk_thread_flag(child, TIF_FORCED_TF))
- val &= ~X86_EFLAGS_TF;
- return val;
- default:
- val = *pt_regs_access(regs, regno);
- if (test_tsk_thread_flag(child, TIF_IA32))
- val &= 0xffffffff;
- return val;
- }
-
-}
-
-unsigned long ptrace_get_debugreg(struct task_struct *child, int n)
-{
- switch (n) {
- case 0: return child->thread.debugreg0;
- case 1: return child->thread.debugreg1;
- case 2: return child->thread.debugreg2;
- case 3: return child->thread.debugreg3;
- case 6: return child->thread.debugreg6;
- case 7: return child->thread.debugreg7;
- }
- return 0;
-}
-
-int ptrace_set_debugreg(struct task_struct *child, int n, unsigned long data)
-{
- int i;
-
- if (n < 4) {
- int dsize = test_tsk_thread_flag(child, TIF_IA32) ? 3 : 7;
- if (unlikely(data >= TASK_SIZE_OF(child) - dsize))
- return -EIO;
- }
-
- switch (n) {
- case 0: child->thread.debugreg0 = data; break;
- case 1: child->thread.debugreg1 = data; break;
- case 2: child->thread.debugreg2 = data; break;
- case 3: child->thread.debugreg3 = data; break;
-
- case 6:
- if (data >> 32)
- return -EIO;
- child->thread.debugreg6 = data;
- break;
-
- case 7:
- /*
- * See ptrace_32.c for an explanation of this awkward check.
- */
- data &= ~DR_CONTROL_RESERVED;
- for (i = 0; i < 4; i++)
- if ((0x5554 >> ((data >> (16 + 4*i)) & 0xf)) & 1)
- return -EIO;
- child->thread.debugreg7 = data;
- if (data)
- set_tsk_thread_flag(child, TIF_DEBUG);
- else
- clear_tsk_thread_flag(child, TIF_DEBUG);
- break;
- }
-
- return 0;
-}
-
-long arch_ptrace(struct task_struct *child, long request, long addr, long data)
-{
- long ret;
- unsigned ui;
-
- switch (request) {
- /* when I and D space are separate, these will need to be fixed. */
- case PTRACE_PEEKTEXT: /* read word at location addr. */
- case PTRACE_PEEKDATA:
- ret = generic_ptrace_peekdata(child, addr, data);
- break;
-
- /* read the word at location addr in the USER area. */
- case PTRACE_PEEKUSR: {
- unsigned long tmp;
-
- ret = -EIO;
- if ((addr & 7) ||
- addr > sizeof(struct user) - 7)
- break;
-
- tmp = 0;
- if (addr < sizeof(struct user_regs_struct))
- tmp = getreg(child, addr);
- else if (addr >= offsetof(struct user, u_debugreg[0])) {
- addr -= offsetof(struct user, u_debugreg[0]);
- tmp = ptrace_get_debugreg(child, addr / sizeof(long));
- }
-
- ret = put_user(tmp,(unsigned long __user *) data);
- 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:
- ret = generic_ptrace_pokedata(child, addr, data);
- break;
-
- case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
- ret = -EIO;
- if ((addr & 7) ||
- addr > sizeof(struct user) - 7)
- break;
-
- if (addr < sizeof(struct user_regs_struct))
- ret = putreg(child, addr, data);
- else if (addr >= offsetof(struct user, u_debugreg[0])) {
- addr -= offsetof(struct user, u_debugreg[0]);
- ret = ptrace_set_debugreg(child,
- addr / sizeof(long), data);
- }
- break;
-
-#ifdef CONFIG_IA32_EMULATION
- /* This makes only sense with 32bit programs. Allow a
- 64bit debugger to fully examine them too. Better
- don't use it against 64bit processes, use
- PTRACE_ARCH_PRCTL instead. */
- case PTRACE_GET_THREAD_AREA:
- if (addr < 0)
- return -EIO;
- ret = do_get_thread_area(child, addr,
- (struct user_desc __user *) data);
-
- break;
- case PTRACE_SET_THREAD_AREA:
- if (addr < 0)
- return -EIO;
- ret = do_set_thread_area(child, addr,
- (struct user_desc __user *) data, 0);
- break;
-#endif
- /* normal 64bit interface to access TLS data.
- Works just like arch_prctl, except that the arguments
- are reversed. */
- case PTRACE_ARCH_PRCTL:
- ret = do_arch_prctl(child, data, addr);
- break;
-
- case PTRACE_GETREGS: { /* Get all gp regs from the child. */
- if (!access_ok(VERIFY_WRITE, (unsigned __user *)data,
- sizeof(struct user_regs_struct))) {
- ret = -EIO;
- break;
- }
- ret = 0;
- for (ui = 0; ui < sizeof(struct user_regs_struct); ui += sizeof(long)) {
- ret |= __put_user(getreg(child, ui),(unsigned long __user *) data);
- data += sizeof(long);
- }
- break;
- }
-
- case PTRACE_SETREGS: { /* Set all gp regs in the child. */
- unsigned long tmp;
- if (!access_ok(VERIFY_READ, (unsigned __user *)data,
- sizeof(struct user_regs_struct))) {
- ret = -EIO;
- break;
- }
- ret = 0;
- for (ui = 0; ui < sizeof(struct user_regs_struct); ui += sizeof(long)) {
- ret = __get_user(tmp, (unsigned long __user *) data);
- if (ret)
- break;
- ret = putreg(child, ui, tmp);
- if (ret)
- break;
- data += sizeof(long);
- }
- break;
- }
-
- case PTRACE_GETFPREGS: { /* Get the child extended FPU state. */
- if (!access_ok(VERIFY_WRITE, (unsigned __user *)data,
- sizeof(struct user_i387_struct))) {
- ret = -EIO;
- break;
- }
- ret = get_fpregs((struct user_i387_struct __user *)data, child);
- break;
- }
-
- case PTRACE_SETFPREGS: { /* Set the child extended FPU state. */
- if (!access_ok(VERIFY_READ, (unsigned __user *)data,
- sizeof(struct user_i387_struct))) {
- ret = -EIO;
- break;
- }
- set_stopped_child_used_math(child);
- ret = set_fpregs(child, (struct user_i387_struct __user *)data);
- break;
- }
-
- default:
- ret = ptrace_request(child, request, addr, data);
- break;
- }
- return ret;
-}
-
-static void syscall_trace(struct pt_regs *regs)
-{
-
-#if 0
- printk("trace %s ip %lx sp %lx ax %d origrax %d caller %lx tiflags %x ptrace %x\n",
- current->comm,
- regs->ip, regs->sp, regs->ax, regs->orig_ax, __builtin_return_address(0),
- current_thread_info()->flags, current->ptrace);
-#endif
-
- ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
- ? 0x80 : 0));
- /*
- * 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) {
- send_sig(current->exit_code, current, 1);
- current->exit_code = 0;
- }
-}
-
-asmlinkage void syscall_trace_enter(struct pt_regs *regs)
-{
- /* do the secure computing check first */
- secure_computing(regs->orig_ax);
-
- if (test_thread_flag(TIF_SYSCALL_TRACE)
- && (current->ptrace & PT_PTRACED))
- syscall_trace(regs);
-
- if (unlikely(current->audit_context)) {
- if (test_thread_flag(TIF_IA32)) {
- audit_syscall_entry(AUDIT_ARCH_I386,
- regs->orig_ax,
- regs->bx, regs->cx,
- regs->dx, regs->si);
- } else {
- audit_syscall_entry(AUDIT_ARCH_X86_64,
- regs->orig_ax,
- regs->di, regs->si,
- regs->dx, regs->r10);
- }
- }
-}
-
-asmlinkage void syscall_trace_leave(struct pt_regs *regs)
-{
- if (unlikely(current->audit_context))
- audit_syscall_exit(AUDITSC_RESULT(regs->ax), regs->ax);
-
- if ((test_thread_flag(TIF_SYSCALL_TRACE)
- || test_thread_flag(TIF_SINGLESTEP))
- && (current->ptrace & PT_PTRACED))
- syscall_trace(regs);
-}

2007-11-29 12:24:18

by Ingo Molnar

[permalink] [raw]
Subject: Re: [PATCH x86/mm 01/11] x86-32 thread_struct.debugreg


thanks, i've merged your 11 patches - they passed a basic build and boot
test as well.

Roland, you've done a lot of gdb / strace / glibc development, what
would you suggest for us to use as a ptrace regression checker? The
problem is that ptrace is not normally used on a default bootup of a
distro, and some of the ptrace features are really arcane. UML is an
extensive ptrace user, so running it might be a good start, but do you
know of any, more directed testsuite that is expected to hit all (or at
least a substantial percentage of) the various ptrace features that we
are affecting with these ptrace patches?

btw., your cleanup patches are having a nice effect on code quality as
well:

errors lines of code errors/KLOC
[before] arch/x86/ 5231 116998 44.7
[after] arch/x86/ 5132 116656 43.9

(the 'errors' column is the sum of all .c files as per the error count
of scripts/checkpatch.pl --file output)

it's refreshing to see life being brought back into the ptrace code
again :-)

Ingo

2007-11-29 14:07:42

by Jeff Dike

[permalink] [raw]
Subject: Re: [PATCH x86/mm 11/11] x86 ptrace merge removals

On Thu, Nov 29, 2007 at 04:00:57AM -0800, Roland McGrath wrote:
...lots of ptrace merging...

Can you make sure that UML still runs when you're done with ptrace?

Jeff

--
Work email - jdike at linux dot intel dot com

2007-11-29 17:28:24

by Andrew Morton

[permalink] [raw]
Subject: Re: [PATCH x86/mm 05/11] x86 ptrace getreg/putreg merge

On Thu, 29 Nov 2007 03:59:54 -0800 (PST) Roland McGrath <[email protected]> wrote:

> +/*
> + * Determines whether a value may be installed in a segment register.
> + */
> +#define invalid_selector(value) \
> + ((value) != 0 && ((value) & SEGMENT_RPL_MASK) != USER_RPL)

This didn't need to be implemented as a macro hence it shouldn't have been.

It references its arg more than once and is dangerous. Calling
invalid_selector(foo++) will increment foo by one or two, depending upon
foo's value.

2007-11-29 17:29:59

by Andrew Morton

[permalink] [raw]
Subject: Re: [PATCH x86/mm 06/11] x86 ptrace arch merge

On Thu, 29 Nov 2007 04:00:14 -0800 (PST) Roland McGrath <[email protected]> wrote:

> --- a/arch/x86/kernel/ptrace.c
> +++ b/arch/x86/kernel/ptrace.c
> @@ -461,12 +461,13 @@ static int ptrace_set_debugreg(struct task_struct *child,
> void ptrace_disable(struct task_struct *child)
> {
> user_disable_single_step(child);
> +#ifdef TIF_SYSCALL_EMU
> clear_tsk_thread_flag(child, TIF_SYSCALL_EMU);
> +#endif

Is TIF_SYSCALL_EMU ever undefined here?

2007-11-29 17:37:50

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH x86/mm 08/11] x86 ia32 ptrace getreg/putreg merge

On Thu, Nov 29, 2007 at 04:00:31AM -0800, Roland McGrath wrote:
> +#define R32(l,q) \
> + case offsetof(struct user32, regs.l): \
> + regs->q = value; break
> +
> +#define SEG32(rs) \
> + case offsetof(struct user32, regs.rs): \
> + return set_segment_reg(child, \
> + offsetof(struct user_regs_struct, rs), \
> + value); \
> + break

The code would be a lot more readable if you just opencoded this in the
caller instead of these obsfucated macros.

> +
> +static int putreg32(struct task_struct *child, unsigned regno, u32 value)
> +{
> + struct pt_regs *regs = task_pt_regs(child);
> +
> + switch (regno) {
> +
> + SEG32(cs);
> + SEG32(ds);
> + SEG32(es);
> + SEG32(fs);
> + SEG32(gs);
> + SEG32(ss);
> +
> + R32(ebx, bx);
> + R32(ecx, cx);
> + R32(edx, dx);
> + R32(edi, di);
> + R32(esi, si);
> + R32(ebp, bp);
> + R32(eax, ax);
> + R32(orig_eax, orig_ax);
> + R32(eip, ip);
> + R32(esp, sp);
> +
> + case offsetof(struct user32, regs.eflags):
> + return set_flags(child, value);
> +
> + case offsetof(struct user32, u_debugreg[0]) ...
> + offsetof(struct user32, u_debugreg[7]):
> + regno -= offsetof(struct user32, u_debugreg[0]);
> + return ptrace_set_debugreg(child, regno / 4, value);
> +
> + default:
> + if (regno > sizeof(struct user32) || (regno & 3))
> + return -EIO;
> +
> + /*
> + * Other dummy fields in the virtual user structure
> + * are ignored
> + */
> + break;
> + }
> + return 0;
> +}
> +
> +#undef R32
> +#undef SEG32
> +
> +#define R32(l,q) \
> + case offsetof(struct user32, regs.l): \
> + *val = regs->q; break
> +
> +#define SEG32(rs) \
> + case offsetof(struct user32, regs.rs): \
> + *val = get_segment_reg(child, \
> + offsetof(struct user_regs_struct, rs)); \
> + break
> +
> +static int getreg32(struct task_struct *child, unsigned regno, u32 *val)
> +{
> + struct pt_regs *regs = task_pt_regs(child);
> +
> + switch (regno) {
> +
> + SEG32(ds);
> + SEG32(es);
> + SEG32(fs);
> + SEG32(gs);
> +
> + R32(cs, cs);
> + R32(ss, ss);
> + R32(ebx, bx);
> + R32(ecx, cx);
> + R32(edx, dx);
> + R32(edi, di);
> + R32(esi, si);
> + R32(ebp, bp);
> + R32(eax, ax);
> + R32(orig_eax, orig_ax);
> + R32(eip, ip);
> + R32(esp, sp);
> +
> + case offsetof(struct user32, regs.eflags):
> + *val = get_flags(child);
> + break;
> +
> + case offsetof(struct user32, u_debugreg[0]) ...
> + offsetof(struct user32, u_debugreg[7]):
> + regno -= offsetof(struct user32, u_debugreg[0]);
> + *val = ptrace_get_debugreg(child, regno / 4);
> + break;
> +
> + default:
> + if (regno > sizeof(struct user32) || (regno & 3))
> + return -EIO;
> +
> + /*
> + * Other dummy fields in the virtual user structure
> + * are ignored
> + */
> + *val = 0;
> + break;
> + }
> + return 0;
> +}
> +
> +#undef R32
> +#undef SEG32
> +
> +#endif /* CONFIG_IA32_EMULATION */
> +
> #ifdef CONFIG_X86_32
>
> void send_sigtrap(struct task_struct *tsk, struct pt_regs *regs, int error_code)
> -
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at http://www.tux.org/lkml/
---end quoted text---

2007-11-29 18:04:20

by H. Peter Anvin

[permalink] [raw]
Subject: Re: [PATCH x86/mm 08/11] x86 ia32 ptrace getreg/putreg merge

Christoph Hellwig wrote:
> On Thu, Nov 29, 2007 at 04:00:31AM -0800, Roland McGrath wrote:
>> +#define R32(l,q) \
>> + case offsetof(struct user32, regs.l): \
>> + regs->q = value; break
>> +
>> +#define SEG32(rs) \
>> + case offsetof(struct user32, regs.rs): \
>> + return set_segment_reg(child, \
>> + offsetof(struct user_regs_struct, rs), \
>> + value); \
>> + break
>
> The code would be a lot more readable if you just opencoded this in the
> caller instead of these obsfucated macros.
>

For better or worse, though, that is style of existing code. I
personally found it easy to deal with when I went through the code recently.

-hpa

2007-11-29 19:51:36

by Ingo Molnar

[permalink] [raw]
Subject: Re: [PATCH x86/mm 08/11] x86 ia32 ptrace getreg/putreg merge


* H. Peter Anvin <[email protected]> wrote:

> Christoph Hellwig wrote:
>> On Thu, Nov 29, 2007 at 04:00:31AM -0800, Roland McGrath wrote:
>>> +#define R32(l,q) \
>>> + case offsetof(struct user32, regs.l): \
>>> + regs->q = value; break
>>> +
>>> +#define SEG32(rs) \
>>> + case offsetof(struct user32, regs.rs): \
>>> + return set_segment_reg(child, \
>>> + offsetof(struct user_regs_struct, rs), \
>>> + value); \
>>> + break
>>
>> The code would be a lot more readable if you just opencoded this in the
>> caller instead of these obsfucated macros.
>
> For better or worse, though, that is style of existing code. I
> personally found it easy to deal with when I went through the code
> recently.

yep. The ptrace code has certainly lots of inconsistent style crap piled
up during its 10 year history of only be touched with a 10 foot pole.
I'd go for small patches that continuously improve the picture within
the existing mechanisms than any "100% pure required" approach. With 48
clean patches from Roland we are already on the right granularity level
i think.

See how ptrace.c raw code quality has already increased leaps and
bounds:

errors lines of code errors/KLOC
[before]
arch/x86/kernel/ptrace_64.c 58 621 93.3
arch/x86/kernel/ptrace_32.c 39 717 54.3

[after]
arch/x86/kernel/ptrace.c 13 1135 11.4

so i'm not worried about that aspect. We are definitely "for the
better".

Ingo

2007-11-29 20:59:19

by Alexey Dobriyan

[permalink] [raw]
Subject: Re: [PATCH x86/mm 09/11] x86 ia32 ptrace arch merge

On Thu, Nov 29, 2007 at 04:00:41AM -0800, Roland McGrath wrote:
> This moves the sys32_ptrace code into arch/x86/kernel/ptrace.c,
> verbatim except for a few hard-coded sizes replaced with sizeof.
> Here this code can use the shared local functions in this file.

> --- a/arch/x86/kernel/ptrace.c
> +++ b/arch/x86/kernel/ptrace.c

> +asmlinkage long sys32_ptrace(long request, u32 pid, u32 addr, u32 data)
> +{
> + struct task_struct *child;
> + struct pt_regs *childregs;
> + void __user *datap = compat_ptr(data);
> + int ret;
> + __u32 val;
> +
> + switch (request) {
> + case PTRACE_PEEKDATA:
> + case PTRACE_PEEKTEXT:
> + ret = 0;

Dead write.

> + if (access_process_vm(child, addr, &val, sizeof(u32), 0) !=
> + sizeof(u32))
> + ret = -EIO;
> + else
> + ret = put_user(val, (unsigned int __user *)datap);
> + break;

> + case PTRACE_GETFPREGS:
> + ret = -EIO;
> + if (!access_ok(VERIFY_READ, compat_ptr(data),
> + sizeof(struct user_i387_struct)))
> + break;
> + save_i387_ia32(child, datap, childregs, 1);
> + ret = 0;
> + break;

More indentation than needed.

> + case PTRACE_GETFPXREGS: {
> + struct user32_fxsr_struct __user *u = datap;
> +
> + init_fpu(child);
> + ret = -EIO;
> + if (!access_ok(VERIFY_WRITE, u, sizeof(*u)))
> + break;
> + ret = -EFAULT;

Absent { }. :^)

> + if (__copy_to_user(u, &child->thread.i387.fxsave, sizeof(*u)))
> + break;
> + ret = __put_user(childregs->cs, &u->fcs);
> + ret |= __put_user(child->thread.ds, &u->fos);
> + break;
> + }

2007-11-29 21:34:18

by Roland McGrath

[permalink] [raw]
Subject: Re: [PATCH x86/mm 06/11] x86 ptrace arch merge

> > --- a/arch/x86/kernel/ptrace.c
> > +++ b/arch/x86/kernel/ptrace.c
> > @@ -461,12 +461,13 @@ static int ptrace_set_debugreg(struct task_struct *child,
> > void ptrace_disable(struct task_struct *child)
> > {
> > user_disable_single_step(child);
> > +#ifdef TIF_SYSCALL_EMU
> > clear_tsk_thread_flag(child, TIF_SYSCALL_EMU);
> > +#endif
>
> Is TIF_SYSCALL_EMU ever undefined here?

Yes, it not defined on CONFIG_X86_64.
(That is something to be addressed later on.)


Thanks,
Roland

2007-11-29 21:37:33

by Roland McGrath

[permalink] [raw]
Subject: Re: [PATCH x86/mm 09/11] x86 ia32 ptrace arch merge

> On Thu, Nov 29, 2007 at 04:00:41AM -0800, Roland McGrath wrote:
> > This moves the sys32_ptrace code into arch/x86/kernel/ptrace.c,
> > verbatim except for a few hard-coded sizes replaced with sizeof.
^^^^^^^^
>[...]

I just moved this code, I didn't change it. So let's leave style (or
other) problems with the existing code for a next round and not hold
up the moving/merging because of them.


Thanks,
Roland

2007-11-29 21:51:53

by Roland McGrath

[permalink] [raw]
Subject: Re: [PATCH x86/mm 01/11] x86-32 thread_struct.debugreg

> thanks, i've merged your 11 patches - they passed a basic build and boot
> test as well.

Thanks!

> Roland, you've done a lot of gdb / strace / glibc development, what
> would you suggest for us to use as a ptrace regression checker? The
> problem is that ptrace is not normally used on a default bootup of a
> distro, and some of the ptrace features are really arcane. UML is an
> extensive ptrace user, so running it might be a good start, but do you
> know of any, more directed testsuite that is expected to hit all (or at
> least a substantial percentage of) the various ptrace features that we
> are affecting with these ptrace patches?

Jan Kratochvil has helped me a great deal with ptrace testing lately.
We have started to collect a small regression test suite, see
http://sourceware.org/systemtap/wiki/utrace/tests for pointers. That
has tests for individual problems that have come up, and not anything
exhaustive for testing all ptrace functionality. The gdb test suite
is moderately torturous and more or less the standard big smoke test.
UML is also a good test, though I have never been set up to verify
anything beyond "UML seems to boot far enough to complain I don't
have a userland filesystem for it".

Most of the changes I've submitted lately are just moving code around
and not changing its logic substantially. So for most iterations
I've been reaosnably confident after just a few quick smoke tests
with simple manual uses of strace and gdb.


Thanks,
Roland

2007-11-29 22:29:52

by Roland McGrath

[permalink] [raw]
Subject: Re: [PATCH x86/mm 05/11] x86 ptrace getreg/putreg merge

> This didn't need to be implemented as a macro hence it shouldn't have been.

Ok.


diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c
index b3433e1..0000000 100644
--- a/arch/x86/kernel/ptrace.c
+++ b/arch/x86/kernel/ptrace.c
@@ -45,8 +45,10 @@
/*
* Determines whether a value may be installed in a segment register.
*/
-#define invalid_selector(value) \
- ((value) != 0 && ((value) & SEGMENT_RPL_MASK) != USER_RPL)
+static inline bool invalid_selector(u16 value)
+{
+ return unlikely(value != 0 && (value & SEGMENT_RPL_MASK) != USER_RPL);
+}

#ifdef CONFIG_X86_32

2007-11-29 22:39:08

by Roland McGrath

[permalink] [raw]
Subject: Re: [PATCH x86/mm 11/11] x86 ptrace merge removals

> On Thu, Nov 29, 2007 at 04:00:57AM -0800, Roland McGrath wrote:
> ...lots of ptrace merging...
>
> Can you make sure that UML still runs when you're done with ptrace?

I'd be glad to, especially if you give me some advice on testing (.config
for um-i386 and um-x86_64, what do try that constitutes "UML still runs").
My patches are going into the x86/mm tree. Right now (before these), UML
doesn't build for x86_64 or i386 from this tree to begin with.


Thanks,
Roland

2007-11-29 23:03:19

by Chuck Ebbert

[permalink] [raw]
Subject: Re: [PATCH x86/mm 01/11] x86-32 thread_struct.debugreg

On 11/29/2007 04:50 PM, Roland McGrath wrote:
> Jan Kratochvil has helped me a great deal with ptrace testing lately.
> We have started to collect a small regression test suite, see
> http://sourceware.org/systemtap/wiki/utrace/tests for pointers. That
> has tests for individual problems that have come up, and not anything
> exhaustive for testing all ptrace functionality.

You could contribute them to LTP?

2007-11-30 00:05:36

by Jeff Dike

[permalink] [raw]
Subject: Re: [PATCH x86/mm 11/11] x86 ptrace merge removals

On Thu, Nov 29, 2007 at 02:38:03PM -0800, Roland McGrath wrote:
> > Can you make sure that UML still runs when you're done with ptrace?
>
> I'd be glad to, especially if you give me some advice on testing (.config
> for um-i386 and um-x86_64, what do try that constitutes "UML still runs").

Use defconfig and boot it. If you break ptrace, I think it's
overwhelmingly likely that UML will stop booting. So if UML boots,
I'd say you're good to go, with one caveat. That is, UML should
report at boot that PTRACE_SYSEMU works. I put in a fallback from
PTRACE_SYSEMU to PTRACE_SYSCALL when Fedora broke PTRACE_SYSEMU.

> Right now (before these), UML
> doesn't build for x86_64 or i386 from this tree to begin with.

For current -mm, you'll need
http://marc.info/?l=linux-kernel&m=119635496908681&q=raw to build.

Jeff

--
Work email - jdike at linux dot intel dot com

2007-11-30 00:09:21

by Jeff Dike

[permalink] [raw]
Subject: Re: [PATCH x86/mm 01/11] x86-32 thread_struct.debugreg

On Thu, Nov 29, 2007 at 01:50:55PM -0800, Roland McGrath wrote:
> UML is also a good test, though I have never been set up to verify
> anything beyond "UML seems to boot far enough to complain I don't
> have a userland filesystem for it".

BTW, this doesn't exercise ptrace at all. Interesting ptrace things
only start happening when userspace runs.

Grab an interesting-looking image from http://uml.nagafix.co.uk,
uncompress it, and run
./linux ubda=the-filesystem-image

Jeff

--
Work email - jdike at linux dot intel dot com

2007-11-30 11:34:34

by Ingo Molnar

[permalink] [raw]
Subject: Re: [PATCH x86/mm 09/11] x86 ia32 ptrace arch merge


* Roland McGrath <[email protected]> wrote:

> > On Thu, Nov 29, 2007 at 04:00:41AM -0800, Roland McGrath wrote:
> > > This moves the sys32_ptrace code into arch/x86/kernel/ptrace.c,
> > > verbatim except for a few hard-coded sizes replaced with sizeof.
> ^^^^^^^^
> >[...]
>
> I just moved this code, I didn't change it. So let's leave style (or
> other) problems with the existing code for a next round and not hold
> up the moving/merging because of them.

yep - cleanups should then be posted against the unified codebase. (it's
simpler that way)

Ingo

2007-11-30 11:41:20

by Ingo Molnar

[permalink] [raw]
Subject: Re: [PATCH x86/mm 05/11] x86 ptrace getreg/putreg merge


* Roland McGrath <[email protected]> wrote:

> > This didn't need to be implemented as a macro hence it shouldn't have been.
>
> Ok.
>
>
> diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c
> index b3433e1..0000000 100644
> --- a/arch/x86/kernel/ptrace.c
> +++ b/arch/x86/kernel/ptrace.c

i have added this to x86.git.

Ingo