Date: Thu, 6 Mar 2008 09:13:23 GMT From: Aurelien Jarno <aurelien@aurel32.net> To: freebsd-gnats-submit@FreeBSD.org Subject: kern/121422: FreeBSD doesn't follow x86/x86-64 ABI wrt direction flag Message-ID: <200803060913.m269DNxv058566@www.freebsd.org> Resent-Message-ID: <200803060920.m269K17h068487@freefall.freebsd.org>
next in thread | raw e-mail | index | archive | help
>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:
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200803060913.m269DNxv058566>