Issue | Fix Version | Assignee | Priority | Status | Resolution | Resolved In Build |
---|---|---|---|---|---|---|
JDK-8269809 | 8u311 | David Buck | P2 | Closed | Fixed | b02 |
FULL PRODUCT VERSION :
java version "1.8.0_25"
Java(TM) SE Runtime Environment (build 1.8.0_25-b18)
Java HotSpot(TM) 64-Bit Server VM (build 25.25-b02, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows [Version 6.1.7601]
EXTRA RELEVANT SYSTEM CONFIGURATION :
Visual Studio 2013 Update 2
A DESCRIPTION OF THE PROBLEM :
Floating point variable values are being corrupted by the call to JNI_CreateVM on Windows. Specifically, float variables on the stack are fine, but any that the compiler optimizes by storing in XMM/SSE float registers are being overwritten to 0xcafebabecafebabecafebabecafebabe by the call to CreateVM. This behaviour is observed since we upgraded to 1.8.0_25 and is a regression from 1.7.0_25.
According to MSDN (http://msdn.microsoft.com/en-us/library/9z1stfyw.aspx) the Windows ABI calling convention lists XMM6:XMM15 as non-volatile registers which must be preserved by a callee if they are to be used. Thus the compiler is allowed to optimise a value from the stack into registers as long as it uses these non-volatile registers, and the calling convention guarantees that if a callee (in this case jvm.dll) modifies these registers it restores them before returning to the caller. However we see the values of XMM7, XMM8 and XMM15 overwritten by CreateVM.
The CreateVM JNI call is therefore violating the ABI, causing potentially serious yet subtle robustness bugs in any application that uses an embedded JVM in a native application on Windows. We wonder whether there is a bug in the compiler used to build jvm.dll, or if there are non-conformant manual assembly language optimizations in its implementation.
Note that a similar (but different) bug http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6741940 was filed against Java 1.6 and fixed in 1.7, and that bug seemed to affect CallVoidMethod whereas this bug affects CreateJVM (and Java 1.8.0).
REGRESSION. Last worked in version 7u45
ADDITIONAL REGRESSION INFORMATION:
java version "1.8.0_25"
Java(TM) SE Runtime Environment (build 1.8.0_25-b18)
Java HotSpot(TM) 64-Bit Server VM (build 25.25-b02, mixed mode)
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Compile the test program using Visual Studio 2013 update 2 in a release build (compiler does not registerize float values if optimizations are disabled). Run it (no arguments required) with jvm.dll on path.
Adding -Xint and -Xcheck:jni does not affect the results. Works fine with Java 1.7.0_25
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The value of local float variables should be the same before and after the call to CreateVM
ACTUAL -
Two of the float values were overwritten by 0xcafebabecafebabe:
>jni_xmm
Value: 101.000000 4059400000000000
Value: 102.000000 4059800000000000
Value: 103.000000 4059c00000000000
Value: 104.000000 405a000000000000
Value: 105.000000 405a400000000000
Value: 106.000000 405a800000000000
Value: 107.000000 405ac00000000000
Value: 108.000000 405b000000000000
Value: 109.000000 405b400000000000
Created JVM
Value: 101.000000 4059400000000000
Value: 102.000000 4059800000000000
Value: 103.000000 4059c00000000000
Value: 104.000000 405a000000000000
Value: 105.000000 405a400000000000
Value: 106.000000 405a800000000000
Value: -183956177830910960000000000000000000000000000000000000.000000 cafebabecafebabe
Value: -183956177830910960000000000000000000000000000000000000.000000 cafebabecafebabe
Value: 109.000000 405b400000000000
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
#include <jni.h>
#include <string>
#include <stdio.h>
typedef jint(JNICALL *createJVMFunction)(JavaVM **pvm, void **penv, void *args);
int main(int, char**)
{
JavaVMInitArgs args;
args.version = JNI_VERSION_1_8;
args.nOptions = 0;
args.options = nullptr;
args.ignoreUnrecognized = JNI_FALSE;
JNIEnv *env;
createJVMFunction createJVM = JNI_CreateJavaVM;
JavaVM *jvm;
double f1 = 101;
double f2 = 102;
double f3 = 103;
double f4 = 104;
double f5 = 105;
double f6 = 106;
double f7 = 107;
double f8 = 108;
double f9 = 109;
printf("Value: %f %llx\n", f1, f1);
printf("Value: %f %llx\n", f2, f2);
printf("Value: %f %llx\n", f3, f3);
printf("Value: %f %llx\n", f4, f4);
printf("Value: %f %llx\n", f5, f5);
printf("Value: %f %llx\n", f6, f6);
printf("Value: %f %llx\n", f7, f7);
printf("Value: %f %llx\n", f8, f8);
printf("Value: %f %llx\n", f9, f9);
jint res = createJVM(&jvm, (void**)(void*)&env, &args);
printf("Created JVM\n");
printf("Value: %f %llx\n", f1, f1);
printf("Value: %f %llx\n", f2, f2);
printf("Value: %f %llx\n", f3, f3);
printf("Value: %f %llx\n", f4, f4);
printf("Value: %f %llx\n", f5, f5);
printf("Value: %f %llx\n", f6, f6);
printf("Value: %f %llx\n", f7, f7);
printf("Value: %f %llx\n", f8, f8);
printf("Value: %f %llx\n", f9, f9);
return res;
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
We have been able to work around the bug on Windows by adding calls to _fxsave/_fxrstor around the CreateJVM call to preserve and restore the register values manually.
However although we have only observed this with the CreateJVM call we are concerned that if it is caused by a compiler bug then other JNI functions might also be affected, in which case our workaround would be inadequate and there could be corruption happening anywhere in our applications.
java version "1.8.0_25"
Java(TM) SE Runtime Environment (build 1.8.0_25-b18)
Java HotSpot(TM) 64-Bit Server VM (build 25.25-b02, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows [Version 6.1.7601]
EXTRA RELEVANT SYSTEM CONFIGURATION :
Visual Studio 2013 Update 2
A DESCRIPTION OF THE PROBLEM :
Floating point variable values are being corrupted by the call to JNI_CreateVM on Windows. Specifically, float variables on the stack are fine, but any that the compiler optimizes by storing in XMM/SSE float registers are being overwritten to 0xcafebabecafebabecafebabecafebabe by the call to CreateVM. This behaviour is observed since we upgraded to 1.8.0_25 and is a regression from 1.7.0_25.
According to MSDN (http://msdn.microsoft.com/en-us/library/9z1stfyw.aspx) the Windows ABI calling convention lists XMM6:XMM15 as non-volatile registers which must be preserved by a callee if they are to be used. Thus the compiler is allowed to optimise a value from the stack into registers as long as it uses these non-volatile registers, and the calling convention guarantees that if a callee (in this case jvm.dll) modifies these registers it restores them before returning to the caller. However we see the values of XMM7, XMM8 and XMM15 overwritten by CreateVM.
The CreateVM JNI call is therefore violating the ABI, causing potentially serious yet subtle robustness bugs in any application that uses an embedded JVM in a native application on Windows. We wonder whether there is a bug in the compiler used to build jvm.dll, or if there are non-conformant manual assembly language optimizations in its implementation.
Note that a similar (but different) bug http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6741940 was filed against Java 1.6 and fixed in 1.7, and that bug seemed to affect CallVoidMethod whereas this bug affects CreateJVM (and Java 1.8.0).
REGRESSION. Last worked in version 7u45
ADDITIONAL REGRESSION INFORMATION:
java version "1.8.0_25"
Java(TM) SE Runtime Environment (build 1.8.0_25-b18)
Java HotSpot(TM) 64-Bit Server VM (build 25.25-b02, mixed mode)
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Compile the test program using Visual Studio 2013 update 2 in a release build (compiler does not registerize float values if optimizations are disabled). Run it (no arguments required) with jvm.dll on path.
Adding -Xint and -Xcheck:jni does not affect the results. Works fine with Java 1.7.0_25
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The value of local float variables should be the same before and after the call to CreateVM
ACTUAL -
Two of the float values were overwritten by 0xcafebabecafebabe:
>jni_xmm
Value: 101.000000 4059400000000000
Value: 102.000000 4059800000000000
Value: 103.000000 4059c00000000000
Value: 104.000000 405a000000000000
Value: 105.000000 405a400000000000
Value: 106.000000 405a800000000000
Value: 107.000000 405ac00000000000
Value: 108.000000 405b000000000000
Value: 109.000000 405b400000000000
Created JVM
Value: 101.000000 4059400000000000
Value: 102.000000 4059800000000000
Value: 103.000000 4059c00000000000
Value: 104.000000 405a000000000000
Value: 105.000000 405a400000000000
Value: 106.000000 405a800000000000
Value: -183956177830910960000000000000000000000000000000000000.000000 cafebabecafebabe
Value: -183956177830910960000000000000000000000000000000000000.000000 cafebabecafebabe
Value: 109.000000 405b400000000000
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
#include <jni.h>
#include <string>
#include <stdio.h>
typedef jint(JNICALL *createJVMFunction)(JavaVM **pvm, void **penv, void *args);
int main(int, char**)
{
JavaVMInitArgs args;
args.version = JNI_VERSION_1_8;
args.nOptions = 0;
args.options = nullptr;
args.ignoreUnrecognized = JNI_FALSE;
JNIEnv *env;
createJVMFunction createJVM = JNI_CreateJavaVM;
JavaVM *jvm;
double f1 = 101;
double f2 = 102;
double f3 = 103;
double f4 = 104;
double f5 = 105;
double f6 = 106;
double f7 = 107;
double f8 = 108;
double f9 = 109;
printf("Value: %f %llx\n", f1, f1);
printf("Value: %f %llx\n", f2, f2);
printf("Value: %f %llx\n", f3, f3);
printf("Value: %f %llx\n", f4, f4);
printf("Value: %f %llx\n", f5, f5);
printf("Value: %f %llx\n", f6, f6);
printf("Value: %f %llx\n", f7, f7);
printf("Value: %f %llx\n", f8, f8);
printf("Value: %f %llx\n", f9, f9);
jint res = createJVM(&jvm, (void**)(void*)&env, &args);
printf("Created JVM\n");
printf("Value: %f %llx\n", f1, f1);
printf("Value: %f %llx\n", f2, f2);
printf("Value: %f %llx\n", f3, f3);
printf("Value: %f %llx\n", f4, f4);
printf("Value: %f %llx\n", f5, f5);
printf("Value: %f %llx\n", f6, f6);
printf("Value: %f %llx\n", f7, f7);
printf("Value: %f %llx\n", f8, f8);
printf("Value: %f %llx\n", f9, f9);
return res;
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
We have been able to work around the bug on Windows by adding calls to _fxsave/_fxrstor around the CreateJVM call to preserve and restore the register values manually.
However although we have only observed this with the CreateJVM call we are concerned that if it is caused by a compiler bug then other JNI functions might also be affected, in which case our workaround would be inadequate and there could be corruption happening anywhere in our applications.
- backported by
-
JDK-8269809 XMM/SSE float register values corrupted by JNI_CreateVM call in JRE 8 (Windows)
- Closed
- relates to
-
JDK-8169261 Fix for JDK-8067744 creates build failures with some versions of gcc and/or linux
- Resolved
-
JDK-8078122 YMM registers upper 128 bits may get clobbered by a JNI call on windows
- Closed
-
JDK-6741940 Nonvolatile XMM registers not preserved across JNI calls
- Closed
-
JDK-8169656 Return of build giving extraneous find warnings
- Resolved