kern/121422: FreeBSD doesn't follow x86/x86-64 ABI wrt direction flag

Aurelien Jarno aurelien at aurel32.net
Thu Mar 6 09:20:01 UTC 2008


>Number:         121422
>Category:       kern
>Synopsis:       FreeBSD doesn't follow x86/x86-64 ABI wrt direction flag
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Thu Mar 06 09:20:01 UTC 2008
>Closed-Date:
>Last-Modified:
>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:
>Unformatted:


More information about the freebsd-bugs mailing list