From nobody@FreeBSD.org  Thu Mar  6 09:16:26 2008
Return-Path: <nobody@FreeBSD.org>
Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34])
	by hub.freebsd.org (Postfix) with ESMTP id 89D44106566C
	for <freebsd-gnats-submit@FreeBSD.org>; Thu,  6 Mar 2008 09:16:26 +0000 (UTC)
	(envelope-from nobody@FreeBSD.org)
Received: from www.freebsd.org (www.freebsd.org [IPv6:2001:4f8:fff6::21])
	by mx1.freebsd.org (Postfix) with ESMTP id 787118FC19
	for <freebsd-gnats-submit@FreeBSD.org>; Thu,  6 Mar 2008 09:16:26 +0000 (UTC)
	(envelope-from nobody@FreeBSD.org)
Received: from www.freebsd.org (localhost [127.0.0.1])
	by www.freebsd.org (8.14.2/8.14.2) with ESMTP id m269DNUV058567
	for <freebsd-gnats-submit@FreeBSD.org>; Thu, 6 Mar 2008 09:13:23 GMT
	(envelope-from nobody@www.freebsd.org)
Received: (from nobody@localhost)
	by www.freebsd.org (8.14.2/8.14.1/Submit) id m269DNxv058566;
	Thu, 6 Mar 2008 09:13:23 GMT
	(envelope-from nobody)
Message-Id: <200803060913.m269DNxv058566@www.freebsd.org>
Date: Thu, 6 Mar 2008 09:13:23 GMT
From: Aurelien Jarno <aurelien@aurel32.net>
To: freebsd-gnats-submit@FreeBSD.org
Subject: FreeBSD doesn't follow x86/x86-64 ABI wrt direction flag
X-Send-Pr-Version: www-3.1
X-GNATS-Notify:

>Number:         121422
>Category:       kern
>Synopsis:       [kernel] [patch] FreeBSD doesn't follow x86/x86-64 ABI wrt direction flag
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    kib
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Thu Mar 06 09:20:01 UTC 2008
>Closed-Date:    Thu Mar 27 13:56:31 UTC 2008
>Last-Modified:  Thu Mar 27 13:56:31 UTC 2008
>Originator:     Aurelien Jarno
>Release:        all versions affected
>Organization:
>Environment:
>Description:
Since version 4.3, gcc changed its behaviour concerning the x86/x86-64 
ABI and the direction flag, that is it now assumes that the direction 
flag is cleared at the entry of a function and it doesn't clear once 
more if needed.

This causes some problems with the FreeBSD kernel which does not clear
the direction flag when entering a signal handler. 

If the signal handler is using code that need the direction flag cleared
(for example bzero() or memset()), the code is incorrectly executed.

This has been first reported on the Linux kernel, but *BSD kernels have
the same problem. See http://gcc.gnu.org/ml/gcc/2008-03/msg00267.html for
more details
>How-To-Repeat:
Testcase for x86-64

#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>

void handler(int signal) {
        uint64_t rflags;
        
        asm volatile("pushfq ; popq %0" : "=g" (rflags));

        if (rflags & (1 << 10))
                printf("DF = 1\n");
        else
                printf("DF = 0\n");
}

int main() {
        signal(SIGUSR1, handler);

        while(1)
        {
                asm volatile("std\r\n");
        }

        return 0;
}
>Fix:
Patch attached

Patch attached with submission follows:

diff -Nurd sys/amd64/amd64/machdep.c sys/amd64/amd64/machdep.c
--- sys/amd64/amd64/machdep.c	2008-01-19 19:15:01.000000000 +0100
+++ sys/amd64/amd64/machdep.c	2008-03-06 01:29:07.000000000 +0100
@@ -357,7 +357,7 @@
 
 	regs->tf_rsp = (long)sfp;
 	regs->tf_rip = PS_STRINGS - *(p->p_sysent->sv_szsigcode);
-	regs->tf_rflags &= ~PSL_T;
+	regs->tf_rflags &= ~(PSL_T | PSL_D);
 	regs->tf_cs = _ucodesel;
 	PROC_LOCK(p);
 	mtx_lock(&psp->ps_mtx);
diff -Nurd sys/amd64/ia32/ia32_signal.c sys/amd64/ia32/ia32_signal.c
--- sys/amd64/ia32/ia32_signal.c	2006-10-05 03:56:10.000000000 +0200
+++ sys/amd64/ia32/ia32_signal.c	2008-03-06 01:29:07.000000000 +0100
@@ -391,7 +391,7 @@
 
 	regs->tf_rsp = (uintptr_t)sfp;
 	regs->tf_rip = FREEBSD32_PS_STRINGS - sz_freebsd4_ia32_sigcode;
-	regs->tf_rflags &= ~PSL_T;
+	regs->tf_rflags &= ~(PSL_T | PSL_D);
 	regs->tf_cs = _ucode32sel;
 	regs->tf_ss = _udatasel;
 	load_ds(_udatasel);
@@ -511,7 +511,7 @@
 
 	regs->tf_rsp = (uintptr_t)sfp;
 	regs->tf_rip = FREEBSD32_PS_STRINGS - *(p->p_sysent->sv_szsigcode);
-	regs->tf_rflags &= ~PSL_T;
+	regs->tf_rflags &= ~(PSL_T | PSL_D);
 	regs->tf_cs = _ucode32sel;
 	regs->tf_ss = _udatasel;
 	load_ds(_udatasel);
diff -Nurd sys/amd64/linux32/linux32_sysvec.c sys/amd64/linux32/linux32_sysvec.c
--- sys/amd64/linux32/linux32_sysvec.c	2007-09-20 15:46:26.000000000 +0200
+++ sys/amd64/linux32/linux32_sysvec.c	2008-03-06 01:29:07.000000000 +0100
@@ -402,7 +402,7 @@
 	regs->tf_rsp = PTROUT(fp);
 	regs->tf_rip = LINUX32_PS_STRINGS - *(p->p_sysent->sv_szsigcode) +
 	    linux_sznonrtsigcode;
-	regs->tf_rflags &= ~PSL_T;
+	regs->tf_rflags &= ~(PSL_T | PSL_D);
 	regs->tf_cs = _ucode32sel;
 	regs->tf_ss = _udatasel;
 	load_ds(_udatasel);
@@ -524,7 +524,7 @@
 	 */
 	regs->tf_rsp = PTROUT(fp);
 	regs->tf_rip = LINUX32_PS_STRINGS - *(p->p_sysent->sv_szsigcode);
-	regs->tf_rflags &= ~PSL_T;
+	regs->tf_rflags &= ~(PSL_T | PSL_D);
 	regs->tf_cs = _ucode32sel;
 	regs->tf_ss = _udatasel;
 	load_ds(_udatasel);
diff -Nurd sys/i386/i386/machdep.c sys/i386/i386/machdep.c
--- sys/i386/i386/machdep.c	2008-01-19 19:15:03.000000000 +0100
+++ sys/i386/i386/machdep.c	2008-03-06 01:29:07.000000000 +0100
@@ -416,7 +416,7 @@
 
 	regs->tf_esp = (int)fp;
 	regs->tf_eip = PS_STRINGS - szosigcode;
-	regs->tf_eflags &= ~PSL_T;
+	regs->tf_eflags &= ~(PSL_T | PSL_D);
 	regs->tf_cs = _ucodesel;
 	regs->tf_ds = _udatasel;
 	regs->tf_es = _udatasel;
@@ -537,7 +537,7 @@
 
 	regs->tf_esp = (int)sfp;
 	regs->tf_eip = PS_STRINGS - szfreebsd4_sigcode;
-	regs->tf_eflags &= ~PSL_T;
+	regs->tf_eflags &= ~(PSL_T | PSL_D);
 	regs->tf_cs = _ucodesel;
 	regs->tf_ds = _udatasel;
 	regs->tf_es = _udatasel;
@@ -673,7 +673,7 @@
 
 	regs->tf_esp = (int)sfp;
 	regs->tf_eip = PS_STRINGS - *(p->p_sysent->sv_szsigcode);
-	regs->tf_eflags &= ~PSL_T;
+	regs->tf_eflags &= ~(PSL_T | PSL_D);
 	regs->tf_cs = _ucodesel;
 	regs->tf_ds = _udatasel;
 	regs->tf_es = _udatasel;
diff -Nurd sys/i386/linux/linux_sysvec.c sys/i386/linux/linux_sysvec.c
--- sys/i386/linux/linux_sysvec.c	2007-09-20 15:46:26.000000000 +0200
+++ sys/i386/linux/linux_sysvec.c	2008-03-06 01:29:07.000000000 +0100
@@ -389,7 +389,7 @@
 	regs->tf_esp = (int)fp;
 	regs->tf_eip = PS_STRINGS - *(p->p_sysent->sv_szsigcode) +
 	    linux_sznonrtsigcode;
-	regs->tf_eflags &= ~(PSL_T | PSL_VM);
+	regs->tf_eflags &= ~(PSL_T | PSL_VM | PSL_D);
 	regs->tf_cs = _ucodesel;
 	regs->tf_ds = _udatasel;
 	regs->tf_es = _udatasel;
@@ -508,7 +508,7 @@
 	 */
 	regs->tf_esp = (int)fp;
 	regs->tf_eip = PS_STRINGS - *(p->p_sysent->sv_szsigcode);
-	regs->tf_eflags &= ~(PSL_T | PSL_VM);
+	regs->tf_eflags &= ~(PSL_T | PSL_VM | PSL_D);
 	regs->tf_cs = _ucodesel;
 	regs->tf_ds = _udatasel;
 	regs->tf_es = _udatasel;
diff -Nurd sys/i386/svr4/svr4_machdep.c sys/i386/svr4/svr4_machdep.c
--- sys/i386/svr4/svr4_machdep.c	2005-10-19 16:59:54.000000000 +0200
+++ sys/i386/svr4/svr4_machdep.c	2008-03-06 01:29:07.000000000 +0100
@@ -497,13 +497,13 @@
 	     svr4_szsigcode);
 	tf->tf_cs = GSEL(GUSERLDT_SEL, SEL_UPL);
 
-	tf->tf_eflags &= ~(PSL_T|PSL_VM|PSL_AC);
+	tf->tf_eflags &= ~(PSL_T|PSL_VM|PSL_AC|PSL_D);
 	tf->tf_esp = (int)fp;
 	tf->tf_ss = GSEL(GUSERLDT_SEL, SEL_UPL);
 #else
 	tf->tf_esp = (int)fp;
 	tf->tf_eip = (int)(((char *)PS_STRINGS) - *(p->p_sysent->sv_szsigcode));
-	tf->tf_eflags &= ~PSL_T;
+	tf->tf_eflags &= ~(PSL_T | PSL_D);
 	tf->tf_cs = _ucodesel;
 	tf->tf_ds = _udatasel;
 	tf->tf_es = _udatasel;
diff -Nurd sys/pc98/pc98/machdep.c sys/pc98/pc98/machdep.c
--- sys/pc98/pc98/machdep.c	2008-01-19 19:15:05.000000000 +0100
+++ sys/pc98/pc98/machdep.c	2008-03-06 01:29:07.000000000 +0100
@@ -388,7 +388,7 @@
 
 	regs->tf_esp = (int)fp;
 	regs->tf_eip = PS_STRINGS - szosigcode;
-	regs->tf_eflags &= ~PSL_T;
+	regs->tf_eflags &= ~(PSL_T | PSL_D);
 	regs->tf_cs = _ucodesel;
 	regs->tf_ds = _udatasel;
 	regs->tf_es = _udatasel;
@@ -509,7 +509,7 @@
 
 	regs->tf_esp = (int)sfp;
 	regs->tf_eip = PS_STRINGS - szfreebsd4_sigcode;
-	regs->tf_eflags &= ~PSL_T;
+	regs->tf_eflags &= ~(PSL_T | PSL_D);
 	regs->tf_cs = _ucodesel;
 	regs->tf_ds = _udatasel;
 	regs->tf_es = _udatasel;
@@ -645,7 +645,7 @@
 
 	regs->tf_esp = (int)sfp;
 	regs->tf_eip = PS_STRINGS - *(p->p_sysent->sv_szsigcode);
-	regs->tf_eflags &= ~PSL_T;
+	regs->tf_eflags &= ~(PSL_T | PSL_D);
 	regs->tf_cs = _ucodesel;
 	regs->tf_ds = _udatasel;
 	regs->tf_es = _udatasel;


>Release-Note:
>Audit-Trail:
Responsible-Changed-From-To: freebsd-bugs->kib 
Responsible-Changed-By: kib 
Responsible-Changed-When: Wed Mar 12 13:35:31 UTC 2008 
Responsible-Changed-Why:  
Take. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=121422 

From: dfilter@FreeBSD.ORG (dfilter service)
To: bug-followup@FreeBSD.org
Cc:  
Subject: Re: kern/121422: commit references a PR
Date: Thu, 13 Mar 2008 10:54:44 +0000 (UTC)

 kib         2008-03-13 10:54:38 UTC
 
   FreeBSD src repository
 
   Modified files:
     sys/amd64/amd64      machdep.c 
     sys/amd64/ia32       ia32_signal.c 
     sys/amd64/linux32    linux32_sysvec.c 
     sys/i386/i386        machdep.c 
     sys/i386/linux       linux_sysvec.c 
     sys/i386/svr4        svr4_machdep.c 
     sys/pc98/pc98        machdep.c 
   Log:
   Since version 4.3, gcc changed its behaviour concerning the i386/amd64
   ABI and the direction flag, that is it now assumes that the direction
   flag is cleared at the entry of a function and it doesn't clear once
   more if needed. This new behaviour conforms to the i386/amd64 ABI.
   
   Modify the signal handler frame setup code to clear the DF {e,r}flags
   bit on the amd64/i386 for the signal handlers.
   
   jhb@ noted that it might break old apps if they assumed DF == 1 would be
   preserved in the signal handlers, but that such apps should be rare and
   that older versions of gcc would not generate such apps.
   
   Submitted by:   Aurelien Jarno <aurelien aurel32 net>
   PR:     121422
   Reviewed by:    jhb
   MFC after:      2 weeks
   
   Revision  Changes    Path
   1.682     +1 -1      src/sys/amd64/amd64/machdep.c
   1.16      +2 -2      src/sys/amd64/ia32/ia32_signal.c
   1.33      +2 -2      src/sys/amd64/linux32/linux32_sysvec.c
   1.666     +3 -3      src/sys/i386/i386/machdep.c
   1.152     +2 -2      src/sys/i386/linux/linux_sysvec.c
   1.39      +2 -2      src/sys/i386/svr4/svr4_machdep.c
   1.402     +3 -3      src/sys/pc98/pc98/machdep.c
 _______________________________________________
 cvs-all@freebsd.org mailing list
 http://lists.freebsd.org/mailman/listinfo/cvs-all
 To unsubscribe, send any mail to "cvs-all-unsubscribe@freebsd.org"
 
State-Changed-From-To: open->closed 
State-Changed-By: kib 
State-Changed-When: Thu Mar 27 13:56:02 UTC 2008 
State-Changed-Why:  
Fix committed to the RELENG_6 and RELENG_7, thanks. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=121422 
>Unformatted:
