git: 55872d471d05 - stable/13 - Arch64: Clear VFP state on execve()

Alex Richardson arichardson at FreeBSD.org
Wed Mar 17 10:27:27 UTC 2021


The branch stable/13 has been updated by arichardson:

URL: https://cgit.FreeBSD.org/src/commit/?id=55872d471d05c14207c4c4042975f602c2112956

commit 55872d471d05c14207c4c4042975f602c2112956
Author:     Alex Richardson <arichardson at FreeBSD.org>
AuthorDate: 2021-03-09 19:11:40 +0000
Commit:     Alex Richardson <arichardson at FreeBSD.org>
CommitDate: 2021-03-17 09:44:17 +0000

    Arch64: Clear VFP state on execve()
    
    I noticed that many of the math-related tests were failing on AArch64.
    After a lot of debugging, I noticed that the floating point exception flags
    were not being reset when starting a new process. This change resets the
    VFP inside exec_setregs() to ensure no VFP register state is leaked from
    parent processes to children.
    
    This commit also moves the clearing of fpcr that was added in 65618fdda0f27
    from fork() to execve() since that makes more sense: fork() can retain
    current register values, but execve() should result in a well-defined
    clean state.
    
    Reviewed By:    andrew
    MFC after:      1 week
    Differential Revision: https://reviews.freebsd.org/D29060
    
    (cherry picked from commit 953a7d7c61f3b2f5351dfe668510ec782ae282e8)
---
 sys/arm64/arm64/elf32_machdep.c |  7 +++++++
 sys/arm64/arm64/machdep.c       |  7 +++++++
 sys/arm64/arm64/vfp.c           | 21 +++++++++++++++++++++
 sys/arm64/arm64/vm_machdep.c    |  1 -
 sys/arm64/include/vfp.h         |  1 +
 sys/arm64/linux/linux_sysvec.c  |  8 ++++++++
 6 files changed, 44 insertions(+), 1 deletion(-)

diff --git a/sys/arm64/arm64/elf32_machdep.c b/sys/arm64/arm64/elf32_machdep.c
index 916633650d69..84b62caf8590 100644
--- a/sys/arm64/arm64/elf32_machdep.c
+++ b/sys/arm64/arm64/elf32_machdep.c
@@ -51,6 +51,9 @@ __FBSDID("$FreeBSD$");
 #include <sys/vnode.h>
 
 #include <machine/elf.h>
+#ifdef VFP
+#include <machine/vfp.h>
+#endif
 
 #include <compat/freebsd32/freebsd32_util.h>
 
@@ -251,6 +254,10 @@ freebsd32_setregs(struct thread *td, struct image_params *imgp,
 	tf->tf_x[14] = imgp->entry_addr;
 	tf->tf_elr = imgp->entry_addr;
 	tf->tf_spsr = PSR_M_32;
+
+#ifdef VFP
+	vfp_reset_state(td, td->td_pcb);
+#endif
 }
 
 void
diff --git a/sys/arm64/arm64/machdep.c b/sys/arm64/arm64/machdep.c
index 73b06beeba7e..91f0a31ebe36 100644
--- a/sys/arm64/arm64/machdep.c
+++ b/sys/arm64/arm64/machdep.c
@@ -552,6 +552,7 @@ void
 exec_setregs(struct thread *td, struct image_params *imgp, uintptr_t stack)
 {
 	struct trapframe *tf = td->td_frame;
+	struct pcb *pcb = td->td_pcb;
 
 	memset(tf, 0, sizeof(struct trapframe));
 
@@ -559,6 +560,12 @@ exec_setregs(struct thread *td, struct image_params *imgp, uintptr_t stack)
 	tf->tf_sp = STACKALIGN(stack);
 	tf->tf_lr = imgp->entry_addr;
 	tf->tf_elr = imgp->entry_addr;
+
+#ifdef VFP
+	vfp_reset_state(td, pcb);
+#endif
+
+	/* TODO: Shouldn't we also reset pcb_dbg_regs? */
 }
 
 /* Sanity check these are the same size, they will be memcpy'd to and fro */
diff --git a/sys/arm64/arm64/vfp.c b/sys/arm64/arm64/vfp.c
index 62244ddc80e8..6382547c8966 100644
--- a/sys/arm64/arm64/vfp.c
+++ b/sys/arm64/arm64/vfp.c
@@ -33,6 +33,7 @@ __FBSDID("$FreeBSD$");
 #ifdef VFP
 #include <sys/param.h>
 #include <sys/systm.h>
+#include <sys/limits.h>
 #include <sys/kernel.h>
 #include <sys/malloc.h>
 #include <sys/pcpu.h>
@@ -199,6 +200,26 @@ vfp_save_state(struct thread *td, struct pcb *pcb)
 	critical_exit();
 }
 
+/*
+ * Reset the FP state to avoid leaking state from the parent process across
+ * execve() (and to ensure that we get a consistent floating point environment
+ * in every new process).
+ */
+void
+vfp_reset_state(struct thread *td, struct pcb *pcb)
+{
+	critical_enter();
+	bzero(&pcb->pcb_fpustate.vfp_regs, sizeof(pcb->pcb_fpustate.vfp_regs));
+	KASSERT(pcb->pcb_fpusaved == &pcb->pcb_fpustate,
+	    ("pcb_fpusaved should point to pcb_fpustate."));
+	pcb->pcb_fpustate.vfp_fpcr = initial_fpcr;
+	pcb->pcb_fpustate.vfp_fpsr = 0;
+	pcb->pcb_vfpcpu = UINT_MAX;
+	pcb->pcb_fpflags = 0;
+	vfp_discard(td);
+	critical_exit();
+}
+
 void
 vfp_restore_state(void)
 {
diff --git a/sys/arm64/arm64/vm_machdep.c b/sys/arm64/arm64/vm_machdep.c
index 9e9b588c7db1..8a48f0243abc 100644
--- a/sys/arm64/arm64/vm_machdep.c
+++ b/sys/arm64/arm64/vm_machdep.c
@@ -108,7 +108,6 @@ cpu_fork(struct thread *td1, struct proc *p2, struct thread *td2, int flags)
 	td2->td_pcb->pcb_sp = (uintptr_t)td2->td_frame;
 	td2->td_pcb->pcb_fpusaved = &td2->td_pcb->pcb_fpustate;
 	td2->td_pcb->pcb_vfpcpu = UINT_MAX;
-	td2->td_pcb->pcb_fpusaved->vfp_fpcr = initial_fpcr;
 
 	/* Setup to release spin count in fork_exit(). */
 	td2->td_md.md_spinlock_count = 1;
diff --git a/sys/arm64/include/vfp.h b/sys/arm64/include/vfp.h
index b0ba01a2a319..3632e5eaa396 100644
--- a/sys/arm64/include/vfp.h
+++ b/sys/arm64/include/vfp.h
@@ -68,6 +68,7 @@ struct thread;
 
 void	vfp_init(void);
 void	vfp_discard(struct thread *);
+void	vfp_reset_state(struct thread *, struct pcb *);
 void	vfp_restore_state(void);
 void	vfp_save_state(struct thread *, struct pcb *);
 
diff --git a/sys/arm64/linux/linux_sysvec.c b/sys/arm64/linux/linux_sysvec.c
index 1d628ffe6ecb..67feacfa876b 100644
--- a/sys/arm64/linux/linux_sysvec.c
+++ b/sys/arm64/linux/linux_sysvec.c
@@ -57,6 +57,10 @@ __FBSDID("$FreeBSD$");
 #include <compat/linux/linux_util.h>
 #include <compat/linux/linux_vdso.h>
 
+#ifdef VFP
+#include <machine/vfp.h>
+#endif
+
 MODULE_VERSION(linux64elf, 1);
 
 const char *linux_kplatform;
@@ -360,6 +364,10 @@ linux_exec_setregs(struct thread *td, struct image_params *imgp,
         regs->tf_lr = 0xffffffffffffffff;
 #endif
         regs->tf_elr = imgp->entry_addr;
+
+#ifdef VFP
+	vfp_reset_state(td, td->td_pcb);
+#endif
 }
 
 int


More information about the dev-commits-src-all mailing list