From: Linus Torvalds <torvalds@osdl.org>
To: Davide Libenzi <davidel@xmailserver.org>
Cc: Jesse Allen <the3dfxdude@gmail.com>,
Linux Kernel Mailing List <linux-kernel@vger.kernel.org>,
Andrew Morton <akpm@osdl.org>
Subject: Re: ptrace single-stepping change breaks Wine
Date: Fri, 31 Dec 2004 14:01:08 -0800 (PST) [thread overview]
Message-ID: <Pine.LNX.4.58.0412311359460.2280@ppc970.osdl.org> (raw)
In-Reply-To: <Pine.LNX.4.58.0412310753400.10974@bigblue.dev.mdolabs.com>
[-- Attachment #1: Type: TEXT/PLAIN, Size: 396 bytes --]
On Fri, 31 Dec 2004, Davide Libenzi wrote:
>
> I don't think Linus ever posted a POPF-only patch. Try to comment those
> lines in his POPF patch ...
Here the two patches are independently, if people want to take a look.
If somebody wants to split (and test) the TF-careful thing further (the
"send_sigtrap()" changes are independent, I think), that would be
wonderful... Hint hint.
Linus
[-- Attachment #2: Type: TEXT/PLAIN, Size: 8786 bytes --]
# This is a BitKeeper generated diff -Nru style patch.
#
# ChangeSet
# 2004/12/31 13:29:27-08:00 torvalds@evo.osdl.org
# Use common helper routine to send debug event SIGTRAPs
#
# Also be more careful with setting and clearing TF: don't
# set it unless we really need to, and don't clear it unless
# it was set by the kernel.
#
# include/asm-i386/ptrace.h
# 2004/12/31 13:29:16-08:00 torvalds@evo.osdl.org +1 -0
# Use common helper routine to send debug event SIGTRAPs
#
# Also be more careful with setting and clearing TF: don't
# set it unless we really need to, and don't clear it unless
# it was set by the kernel.
#
# arch/i386/kernel/traps.c
# 2004/12/31 13:29:16-08:00 torvalds@evo.osdl.org +12 -14
# Use common helper routine to send debug event SIGTRAPs
#
# Also be more careful with setting and clearing TF: don't
# set it unless we really need to, and don't clear it unless
# it was set by the kernel.
#
# arch/i386/kernel/signal.c
# 2004/12/31 13:29:16-08:00 torvalds@evo.osdl.org +7 -21
# Use common helper routine to send debug event SIGTRAPs
#
# Also be more careful with setting and clearing TF: don't
# set it unless we really need to, and don't clear it unless
# it was set by the kernel.
#
# arch/i386/kernel/ptrace.c
# 2004/12/31 13:29:15-08:00 torvalds@evo.osdl.org +58 -15
# Use common helper routine to send debug event SIGTRAPs
#
# Also be more careful with setting and clearing TF: don't
# set it unless we really need to, and don't clear it unless
# it was set by the kernel.
#
diff -Nru a/arch/i386/kernel/ptrace.c b/arch/i386/kernel/ptrace.c
--- a/arch/i386/kernel/ptrace.c 2004-12-31 13:34:35 -08:00
+++ b/arch/i386/kernel/ptrace.c 2004-12-31 13:34:35 -08:00
@@ -42,6 +42,12 @@
*/
#define EFL_OFFSET ((EFL-2)*4-sizeof(struct pt_regs))
+static inline struct pt_regs *get_child_regs(struct task_struct *task)
+{
+ void *stack_top = (void *)task->thread.esp0;
+ return stack_top - sizeof(struct pt_regs);
+}
+
/*
* this routine will get a word off of the processes privileged stack.
* the offset is how far from the base addr as stored in the TSS.
@@ -140,22 +146,35 @@
static void set_singlestep(struct task_struct *child)
{
- long eflags;
+ struct pt_regs *regs = get_child_regs(child);
+ /*
+ * Always set TIF_SINGLESTEP - this guarantees that
+ * we single-step system calls etc.. This will also
+ * cause us to set TF when returning to user mode.
+ */
set_tsk_thread_flag(child, TIF_SINGLESTEP);
- eflags = get_stack_long(child, EFL_OFFSET);
- put_stack_long(child, EFL_OFFSET, eflags | TRAP_FLAG);
+
+ /*
+ * If TF was already set, don't do anything else
+ */
+ if (regs->eflags & TRAP_FLAG)
+ return;
+
+ /* Set TF on the kernel stack, and set the flag to say so */
+ regs->eflags |= TRAP_FLAG;
child->ptrace |= PT_DTRACE;
}
static void clear_singlestep(struct task_struct *child)
{
- if (child->ptrace & PT_DTRACE) {
- long eflags;
+ /* Always clear TIF_SINGLESTEP... */
+ clear_tsk_thread_flag(child, TIF_SINGLESTEP);
- clear_tsk_thread_flag(child, TIF_SINGLESTEP);
- eflags = get_stack_long(child, EFL_OFFSET);
- put_stack_long(child, EFL_OFFSET, eflags & ~TRAP_FLAG);
+ /* But touch TF only if it was set by us.. */
+ if (child->ptrace & PT_DTRACE) {
+ struct pt_regs *regs = get_child_regs(child);
+ regs->eflags &= ~TRAP_FLAG;
child->ptrace &= ~PT_DTRACE;
}
}
@@ -553,6 +572,29 @@
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;
+
+ /*
+ * If this is a kernel mode trap, save the user PC on entry to
+ * the kernel, that's what the debugger can make sense of.
+ */
+ info.si_addr = user_mode(regs) ?
+ (void __user *) regs->eip :
+ (void __user *)tsk->thread.eip;
+
+ /* Send us the fakey SIGTRAP */
+ force_sig_info(SIGTRAP, &info, tsk);
+}
+
/* notification of system call entry/exit
* - triggered by current->work.syscall_trace
*/
@@ -568,15 +610,16 @@
audit_syscall_exit(current, regs->eax);
}
- if (!test_thread_flag(TIF_SYSCALL_TRACE) &&
- !test_thread_flag(TIF_SINGLESTEP))
- return;
if (!(current->ptrace & PT_PTRACED))
return;
- /* the 0x80 provides a way for the tracing parent to distinguish
- between a syscall stop and SIGTRAP delivery */
- ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) &&
- !test_thread_flag(TIF_SINGLESTEP) ? 0x80 : 0));
+
+ /* Fake a debug trap */
+ if (test_thread_flag(TIF_SINGLESTEP))
+ send_sigtrap(current, regs, 0);
+
+ if (!test_thread_flag(TIF_SYSCALL_TRACE))
+ return;
+ ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) ? 0x80 : 0));
/*
* this isn't the same as continuing with a signal, but it will do
diff -Nru a/arch/i386/kernel/signal.c b/arch/i386/kernel/signal.c
--- a/arch/i386/kernel/signal.c 2004-12-31 13:34:35 -08:00
+++ b/arch/i386/kernel/signal.c 2004-12-31 13:34:35 -08:00
@@ -270,7 +270,6 @@
struct pt_regs *regs, unsigned long mask)
{
int tmp, err = 0;
- unsigned long eflags;
tmp = 0;
__asm__("movl %%gs,%0" : "=r"(tmp): "0"(tmp));
@@ -292,16 +291,7 @@
err |= __put_user(current->thread.error_code, &sc->err);
err |= __put_user(regs->eip, &sc->eip);
err |= __put_user(regs->xcs, (unsigned int __user *)&sc->cs);
-
- /*
- * Iff TF was set because the program is being single-stepped by a
- * debugger, don't save that information on the signal stack.. We
- * don't want debugging to change state.
- */
- eflags = regs->eflags;
- if (current->ptrace & PT_DTRACE)
- eflags &= ~TF_MASK;
- err |= __put_user(eflags, &sc->eflags);
+ err |= __put_user(regs->eflags, &sc->eflags);
err |= __put_user(regs->esp, &sc->esp_at_signal);
err |= __put_user(regs->xss, (unsigned int __user *)&sc->ss);
@@ -424,11 +414,9 @@
* The tracer may want to single-step inside the
* handler too.
*/
- if (regs->eflags & TF_MASK) {
- regs->eflags &= ~TF_MASK;
- if (current->ptrace & PT_DTRACE)
- ptrace_notify(SIGTRAP);
- }
+ regs->eflags &= ~TF_MASK;
+ if (test_thread_flag(TIF_SINGLESTEP))
+ ptrace_notify(SIGTRAP);
#if DEBUG_SIG
printk("SIG deliver (%s:%d): sp=%p pc=%p ra=%p\n",
@@ -519,11 +507,9 @@
* The tracer may want to single-step inside the
* handler too.
*/
- if (regs->eflags & TF_MASK) {
- regs->eflags &= ~TF_MASK;
- if (current->ptrace & PT_DTRACE)
- ptrace_notify(SIGTRAP);
- }
+ regs->eflags &= ~TF_MASK;
+ if (test_thread_flag(TIF_SINGLESTEP))
+ ptrace_notify(SIGTRAP);
#if DEBUG_SIG
printk("SIG deliver (%s:%d): sp=%p pc=%p ra=%p\n",
diff -Nru a/arch/i386/kernel/traps.c b/arch/i386/kernel/traps.c
--- a/arch/i386/kernel/traps.c 2004-12-31 13:34:35 -08:00
+++ b/arch/i386/kernel/traps.c 2004-12-31 13:34:35 -08:00
@@ -718,23 +718,21 @@
*/
if ((regs->xcs & 3) == 0)
goto clear_TF_reenable;
- if ((tsk->ptrace & (PT_DTRACE|PT_PTRACED)) == PT_DTRACE)
- goto clear_TF;
+
+ /*
+ * Was the TF flag set by a debugger? If so, clear it now,
+ * so that register information is correct.
+ */
+ if (tsk->ptrace & PT_DTRACE) {
+ regs->eflags &= ~TF_MASK;
+ tsk->ptrace &= ~PT_DTRACE;
+ if (!tsk->ptrace & PT_DTRACE)
+ goto clear_TF;
+ }
}
/* Ok, finally something we can handle */
- tsk->thread.trap_no = 1;
- tsk->thread.error_code = error_code;
- info.si_signo = SIGTRAP;
- info.si_errno = 0;
- info.si_code = TRAP_BRKPT;
-
- /* If this is a kernel mode trap, save the user PC on entry to
- * the kernel, that's what the debugger can make sense of.
- */
- info.si_addr = ((regs->xcs & 3) == 0) ? (void __user *)tsk->thread.eip
- : (void __user *)regs->eip;
- force_sig_info(SIGTRAP, &info, tsk);
+ send_sigtrap(tsk, regs, error_code);
/* Disable additional traps. They'll be re-enabled when
* the signal is delivered.
diff -Nru a/include/asm-i386/ptrace.h b/include/asm-i386/ptrace.h
--- a/include/asm-i386/ptrace.h 2004-12-31 13:34:35 -08:00
+++ b/include/asm-i386/ptrace.h 2004-12-31 13:34:35 -08:00
@@ -55,6 +55,7 @@
#define PTRACE_SET_THREAD_AREA 26
#ifdef __KERNEL__
+extern void send_sigtrap(struct task_struct *tsk, struct pt_regs *regs, int error_code);
#define user_mode(regs) ((VM_MASK & (regs)->eflags) || (3 & (regs)->xcs))
#define instruction_pointer(regs) ((regs)->eip)
#if defined(CONFIG_SMP) && defined(CONFIG_FRAME_POINTER)
[-- Attachment #3: Type: TEXT/PLAIN, Size: 3751 bytes --]
# This is a BitKeeper generated diff -Nru style patch.
#
# ChangeSet
# 2004/12/31 13:33:39-08:00 torvalds@evo.osdl.org
# x86 TF handling: don't clear after user "popf"
#
# The "popf" will have set TF to something new,
# we should not clear it.
#
# NOTE! We should not allow the user to see TF on
# the stack after a "pushf", but that is better done
# by the controlling process in user space. This just
# makes it possible to do non-intrusive single-stepping,
# but the final part is up to the debugger.
#
# arch/i386/kernel/ptrace.c
# 2004/12/31 13:33:29-08:00 torvalds@evo.osdl.org +83 -1
# x86 TF handling: don't clear after user "popf"
#
# The "popf" will have set TF to something new,
# we should not clear it.
#
# NOTE! We should not allow the user to see TF on
# the stack after a "pushf", but that is better done
# by the controlling process in user space. This just
# makes it possible to do non-intrusive single-stepping,
# but the final part is up to the debugger.
#
diff -Nru a/arch/i386/kernel/ptrace.c b/arch/i386/kernel/ptrace.c
--- a/arch/i386/kernel/ptrace.c 2004-12-31 13:34:48 -08:00
+++ b/arch/i386/kernel/ptrace.c 2004-12-31 13:34:48 -08:00
@@ -144,6 +144,79 @@
return retval;
}
+#define LDT_SEGMENT 4
+
+static unsigned long convert_eip_to_linear(struct task_struct *child, struct pt_regs *regs)
+{
+ unsigned long addr, seg;
+
+ addr = regs->eip;
+ seg = regs->xcs & 0xffff;
+ if (regs->eflags & VM_MASK) {
+ addr = (addr & 0xffff) + (seg << 4);
+ return addr;
+ }
+
+ /*
+ * 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 inline int is_at_popf(struct task_struct *child, struct pt_regs *regs)
+{
+ int i, copied;
+ unsigned char opcode[16];
+ unsigned long addr = convert_eip_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;
+ /* 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;
+
+ /*
+ * 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);
@@ -161,8 +234,17 @@
if (regs->eflags & TRAP_FLAG)
return;
- /* Set TF on the kernel stack, and set the flag to say so */
+ /* 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.
+ */
+ if (is_at_popf(child, regs))
+ return;
+
child->ptrace |= PT_DTRACE;
}
next prev parent reply other threads:[~2004-12-31 22:01 UTC|newest]
Thread overview: 101+ messages / expand[flat|nested] mbox.gz Atom feed top
[not found] <Pine.LNX.4.58.0411151439270.2222@ppc970.osdl.org>
2004-11-15 22:53 ` ptrace single-stepping change breaks Wine Roland McGrath
2004-11-19 19:00 ` Eric Pouech
2004-11-19 19:20 ` Linus Torvalds
2004-11-19 19:33 ` Eric Pouech
2004-11-19 19:51 ` Linus Torvalds
2004-11-19 20:41 ` Eric Pouech
2004-11-19 21:22 ` Linus Torvalds
2004-11-19 21:23 ` Daniel Jacobowitz
2004-11-19 21:53 ` Linus Torvalds
2004-11-20 21:49 ` Jesse Allen
2004-11-21 4:55 ` Jesse Allen
2004-11-21 21:32 ` Davide Libenzi
2004-11-21 22:33 ` Linus Torvalds
2004-11-21 23:14 ` Davide Libenzi
2004-11-22 1:12 ` Linus Torvalds
2004-11-22 0:13 ` Andreas Schwab
2004-11-22 1:07 ` Linus Torvalds
2004-11-22 4:06 ` Davide Libenzi
2004-11-22 4:29 ` Linus Torvalds
2004-11-22 6:23 ` Linus Torvalds
2004-11-22 11:06 ` Andreas Schwab
2004-11-22 16:27 ` Linus Torvalds
2004-11-22 13:46 ` Davide Libenzi
2004-11-22 23:15 ` Jesse Allen
2004-11-22 23:48 ` Jesse Allen
2004-11-28 17:01 ` Eric Pouech
2004-11-22 20:52 ` Eric Pouech
2004-11-22 21:10 ` Linus Torvalds
2004-11-22 22:19 ` Mike Hearn
2004-11-22 22:25 ` Linus Torvalds
2004-12-29 2:14 ` Thomas Sailer
2004-12-29 15:02 ` Mike Hearn
2004-12-29 18:53 ` Linus Torvalds
2004-12-29 19:40 ` Jesse Allen
2004-12-29 20:04 ` Linus Torvalds
2004-12-29 21:43 ` Jesse Allen
2004-12-30 0:44 ` Linus Torvalds
2004-12-30 1:13 ` Davide Libenzi
2004-12-30 1:55 ` Linus Torvalds
2004-12-30 4:51 ` Linus Torvalds
2004-12-30 4:58 ` Linus Torvalds
2004-12-30 5:07 ` Davide Libenzi
2004-12-30 7:26 ` Linus Torvalds
2004-12-30 17:59 ` Davide Libenzi
2004-12-30 18:16 ` Linus Torvalds
2004-12-30 19:27 ` Jesse Allen
2004-12-30 19:34 ` Linus Torvalds
2004-12-30 22:46 ` Linus Torvalds
2004-12-30 23:00 ` Daniel Jacobowitz
2004-12-30 23:17 ` Linus Torvalds
2004-12-31 5:36 ` Daniel Jacobowitz
2004-12-31 5:47 ` Linus Torvalds
2004-12-31 7:00 ` Jesse Allen
2004-12-31 15:10 ` Daniel Jacobowitz
2004-12-31 17:19 ` Linus Torvalds
2005-01-01 23:20 ` Daniel Jacobowitz
2005-01-29 9:25 ` Kari Hurtta
2004-12-30 23:15 ` Andi Kleen
2004-12-31 0:38 ` Linus Torvalds
2004-12-31 12:35 ` Andi Kleen
2004-12-31 15:16 ` Davide Libenzi
2004-12-31 17:30 ` Linus Torvalds
2004-12-31 19:55 ` Jesse Allen
2004-12-31 17:14 ` Linus Torvalds
2004-12-31 4:55 ` Jesse Allen
2004-12-31 5:05 ` Linus Torvalds
2004-12-31 5:38 ` Daniel Jacobowitz
2004-12-30 19:19 ` Davide Libenzi
2004-12-30 5:06 ` Davide Libenzi
2004-12-30 4:28 ` Jesse Allen
2004-12-29 20:56 ` Jesse Allen
2004-12-29 19:35 ` Thomas Sailer
2004-12-29 20:13 ` Jesse Allen
2004-12-30 1:49 ` Thomas Sailer
2004-12-30 2:10 ` Linus Torvalds
2004-12-30 2:39 ` Thomas Sailer
2004-12-30 2:57 ` Thomas Sailer
2004-12-30 3:15 ` Thomas Sailer
2004-12-30 4:15 ` Andrew Morton
2004-12-30 10:09 ` Thomas Sailer
2004-12-30 13:06 ` Mike Hearn
2004-12-31 13:13 ` Thomas Sailer
2004-12-31 13:31 ` Mike Hearn
2004-12-31 15:42 ` Jesse Allen
2004-12-31 15:56 ` Davide Libenzi
2004-12-31 15:59 ` Jesse Allen
2004-12-31 22:01 ` Linus Torvalds [this message]
2005-01-01 22:04 ` Davide Libenzi
2005-01-01 22:14 ` Linus Torvalds
2005-01-02 3:46 ` Davide Libenzi
2005-01-07 4:51 ` minor nit with decoding popf instruction - was " John Kacur
2005-01-07 6:48 ` Linus Torvalds
2005-01-08 5:05 ` John Kacur
2004-12-31 15:51 ` Thomas Sailer
[not found] ` <1104873315.3557.87.camel@littlegreen>
2005-01-04 21:21 ` Andrew Morton
2005-01-05 10:43 ` Thomas Sailer
2005-01-05 11:24 ` Ingo Molnar
2005-01-05 11:40 ` Alexandre Julliard
2004-12-30 12:11 ` Mike Hearn
2004-11-20 3:40 ` Roland McGrath
2004-11-19 20:59 ` Grzegorz Kulewski
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=Pine.LNX.4.58.0412311359460.2280@ppc970.osdl.org \
--to=torvalds@osdl.org \
--cc=akpm@osdl.org \
--cc=davidel@xmailserver.org \
--cc=linux-kernel@vger.kernel.org \
--cc=the3dfxdude@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).