tree 70d93b073a1272cbe84f619cf6ea22dfe8ded579
parent eced545fc0150025fc960d214545a6edf20c32c2
author Andi Kleen <[EMAIL PROTECTED]> Tue Apr 12 08:25:29 2005
committer Linus Torvalds <[EMAIL PROTECTED]> Tue Apr 12 08:25:29 2005
[PATCH] x86_64: Handle programs that set TF in user space using popf while
single stepping
Ported from i386/Linus
Still won't handle other TF changing instructions like IRET or LAHF.
Prefix handling must be double checked...
Signed-off-by: Andi Kleen <[EMAIL PROTECTED]>
Signed-off-by: Andrew Morton <[EMAIL PROTECTED]>
Signed-off-by: Linus Torvalds <[EMAIL PROTECTED]>
ptrace.c | 88 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 88 insertions(+)
Index: arch/x86_64/kernel/ptrace.c
===================================================================
--- 1b991ceb7bd59916ef72cb029ef0082b93b9b8dd/arch/x86_64/kernel/ptrace.c
(mode:100644 sha1:ac57f3518a04706b86dd55ac17b7239e5819122f)
+++ 70d93b073a1272cbe84f619cf6ea22dfe8ded579/arch/x86_64/kernel/ptrace.c
(mode:100644 sha1:39525eff8f89766b1ed70922d31449ea2bc62092)
@@ -86,6 +86,84 @@
return 0;
}
+#define LDT_SEGMENT 4
+
+unsigned long convert_rip_to_linear(struct task_struct *child, struct pt_regs
*regs)
+{
+ unsigned long addr, seg;
+
+ addr = regs->rip;
+ seg = regs->cs & 0xffff;
+
+ /*
+ * We'll assume that the code segments in the GDT
+ * are all zero-based. That is largely true: the
+ * TLS segments are used for data, and the PNPBIOS
+ * and APM bios ones we just ignore here.
+ */
+ if (seg & LDT_SEGMENT) {
+ u32 *desc;
+ unsigned long base;
+
+ down(&child->mm->context.sem);
+ desc = child->mm->context.ldt + (seg & ~7);
+ base = (desc[0] >> 16) | ((desc[1] & 0xff) << 16) | (desc[1] &
0xff000000);
+
+ /* 16-bit code segment? */
+ if (!((desc[1] >> 22) & 1))
+ addr &= 0xffff;
+ addr += base;
+ up(&child->mm->context.sem);
+ }
+ return addr;
+}
+
+static int is_at_popf(struct task_struct *child, struct pt_regs *regs)
+{
+ int i, copied;
+ unsigned char opcode[16];
+ unsigned long addr = convert_rip_to_linear(child, regs);
+
+ copied = access_process_vm(child, addr, opcode, sizeof(opcode), 0);
+ for (i = 0; i < copied; i++) {
+ switch (opcode[i]) {
+ /* popf */
+ case 0x9d:
+ return 1;
+
+ /* CHECKME: 64 65 */
+
+ /* opcode and address size prefixes */
+ case 0x66: case 0x67:
+ continue;
+ /* irrelevant prefixes (segment overrides and repeats) */
+ case 0x26: case 0x2e:
+ case 0x36: case 0x3e:
+ case 0x64: case 0x65:
+ case 0xf0: case 0xf2: case 0xf3:
+ continue;
+
+ /* REX prefixes */
+ case 0x40 ... 0x4f:
+ continue;
+
+ /* CHECKME: f0, f2, f3 */
+
+ /*
+ * pushf: NOTE! We should probably not let
+ * the user see the TF bit being set. But
+ * it's more pain than it's worth to avoid
+ * it, and a debugger could emulate this
+ * all in user space if it _really_ cares.
+ */
+ case 0x9c:
+ default:
+ return 0;
+ }
+ }
+ return 0;
+}
+
static void set_singlestep(struct task_struct *child)
{
struct pt_regs *regs = get_child_regs(child);
@@ -106,6 +184,16 @@
/* Set TF on the kernel stack.. */
regs->eflags |= TRAP_FLAG;
+ /*
+ * ..but if TF is changed by the instruction we will trace,
+ * don't mark it as being "us" that set it, so that we
+ * won't clear it by hand later.
+ *
+ * AK: this is not enough, LAHF and IRET can change TF in user space
too.
+ */
+ if (is_at_popf(child, regs))
+ return;
+
child->ptrace |= PT_DTRACE;
}
-
To unsubscribe from this list: send the line "unsubscribe bk-commits-head" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at http://vger.kernel.org/majordomo-info.html