Oracle bug 7708133
FP corruption running a JRockit test program
Chuck Anderson
chuck.anderson@oracle.com
02/17/2011

Oracle patch linux-2.6-x86_64-7708133-fix-fp-corruption.patch was mostly
replaced by Red Hat's:
    linux-2.6.9-x86_64-floating-point-state-corruption-after-handlin.patch
They left out an important part of our patch.  This patch adds it back in.
The header in linux-2.6-x86_64-7708133-fix-fp-corruption.patch describes
the original problem.  The excerpt below describes why we must add back
in the part that Red Hat left out.  See the last paragraph below:

If we needed to save FP state prior to calling a signal handler (used_math()
was true) then we saved it to the address pointed to by
struct sigcontext.fpstate.  If !used_math() then we set fpstate = NULL.
restore_sigcontext() is called to restore that context after returning from
the signal handler.  If fpstate != NULL then restore_i387() is called to
restore FP state.

The problem is that TS_USEDFPU may not be set.  When set, TS_USEDFPU indicates
that the FP state for the task is in the FP registers.  We just restored
state to the FP registers so TS_USEDFPU must also be set.  Otherwise, if
we are preempted after restoring FP state then context switching code will
assume that there is no context in the FP registers that needs to be saved.
It will assume that the FP context for this task has been saved to
struct task_struct.thread.i387.fxsave, which has a stale context.  It could
be from the signal handler causing us to inherit the signal handler's FP
values.  The correct state is in the FP registers.

The fix is to check if TS_USEDFPU is set.  If not, set it and make sure
CR0.ts is not set (clts()) to indicate that the FPU has this task's
context and is ready for use.  We must also clear TS_USEDFPU when we save our
FP context in save_i387() and set CR0.ts (stts()) to indicate that the
FPU must be initialized on first use by the signal handler.

--- linux-2.6.9/arch/x86_64/kernel/i387.c.orig	2011-02-17 11:52:51.000000000 -0800
+++ linux-2.6.9/arch/x86_64/kernel/i387.c	2011-02-17 12:04:36.000000000 -0800
@@ -96,6 +96,7 @@
 	if (tsk->thread_info->status & TS_USEDFPU) {
 		err = save_i387_checking((struct i387_fxsave_struct __user *)buf);
 		if (err) return err;
+		tsk->thread_info->status &= ~TS_USEDFPU;
 		stts();
 	} else {
 		if (__copy_to_user(buf, &tsk->thread.i387.fxsave, 
