Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > 89877e42827f16fa5f86b1df0c2860b1 > files > 2426

kernel-2.6.18-128.1.10.el5.src.rpm

From: Jason Baron <jbaron@redhat.com>
Date: Mon, 10 Mar 2008 15:42:19 -0400
Subject: [x86] clear df flag for signal handlers
Message-id: 20080310194215.GB2077@redhat.com
O-Subject: [rhel5.2 patch] clear df flag for signal handlers
Bugzilla: 436131

The 'df' or directional flags is used to determine the direction in which
string operations should proceed. If its set to 1 then string operations move
from higher to lower addresses in memory, otherwise the operate from lower
to higher ones. the 'std' instruction sets the df flag to one, 'cld' clears it.

The bug is that if the df flag is set 1, in a user process, which then receives
a signal, this flag state is 'leaked' to the signal handler. This is in
violation of the x86 ABI, even though BSD apparently works this way as well.
GCC < 4.3 currently inserts the 'std' instruction when appropriate, so this bug
is masked. However, with the introduction of GCC 4.3, these 'std' instructions
are no longer being inserted, I suspect as an optimization.

The proposal is to correct the kernel behavior and make it obey the spec. This
change has already been accepted by Linus. This is a userspace visible change,
however any reliance on the old behavior is buggy. How do we know when a signal
is going to be delivered? Therefore, to support new userspace, this change is
being requested.

I've verified that the patch below resolves this issue for 64-bit and 32-bit
code paths on x86_64...and i've also verified the patch on i386. Patch resolves
bz #436131.

Thanks to Jakub for helping me with this bug.

-Jason

Acked-by: Alan Cox <alan@redhat.com>
Acked-by: Pete Zaitcev <zaitcev@redhat.com>
Acked-by: Jon Masters <jcm@redhat.com>

diff --git a/arch/i386/kernel/signal.c b/arch/i386/kernel/signal.c
index 0f187c9..7cee063 100644
--- a/arch/i386/kernel/signal.c
+++ b/arch/i386/kernel/signal.c
@@ -540,7 +540,7 @@ handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka,
 		 * The tracer may want to single-step inside the
 		 * handler too.
 		 */
-		regs->eflags &= ~TF_MASK;
+		regs->eflags &= ~(TF_MASK | X86_EFLAGS_DF);
 		tracehook_report_handle_signal(sig, ka, oldset, regs);
 	}
 
diff --git a/arch/x86_64/kernel/signal.c b/arch/x86_64/kernel/signal.c
index e92087a..54573ca 100644
--- a/arch/x86_64/kernel/signal.c
+++ b/arch/x86_64/kernel/signal.c
@@ -384,7 +384,7 @@ handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka,
 		 * The tracer may want to single-step inside the
 		 * handler too.
 		 */
-		regs->eflags &= ~TF_MASK;
+		regs->eflags &= ~(TF_MASK | X86_EFLAGS_DF);
 		tracehook_report_handle_signal(sig, ka, oldset, regs);
 	}