JDK-8066900 : Array Out Of Bounds Exception causes variable corruption
  • Type: Bug
  • Component: hotspot
  • Sub-Component: compiler
  • Affected Version: 8u40,9
  • Priority: P1
  • Status: Closed
  • Resolution: Fixed
  • OS: other
  • CPU: x86
  • Submitted: 2014-12-01
  • Updated: 2017-08-10
  • Resolved: 2014-12-10
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 8 JDK 9
8u40Fixed 9 b44Fixed
Related Reports
Duplicate :  
Relates :  
Description
FULL PRODUCT VERSION :
java.version=1.8.0_25 (or any Java 8) tested 1.8.0_05 and 1.8.0_40

ADDITIONAL OS VERSION INFORMATION :
Apple OS X 10.9.4
Microsoft Windows [Version 6.1.7600]

A DESCRIPTION OF THE PROBLEM :
Accessing array out of bounds, resulting in an IndexOutOfBoundsException being thrown can cause corruption of stack based variable between where the `IndexOutOfBoundsException` is thrown and where the exception is caught.

This seems to occur on ALL versions of Java 8 and has been reproduced on Apple OS-X 10.9.4 and Windows 7 64bit

The exact time that the corruption occurs varies from run to run and from OS to OS

REGRESSION.  Last worked in version 7u71

ADDITIONAL REGRESSION INFORMATION: 
Appears to work with all Java 7 versions

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the JUnit text program supplied with a Java 8 JVM

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
java.version=1.8.0_40-ea
expected 1.00000, sum.getSum() = 1.00000
expected 2.00000, sum.getSum() = 2.00000
.
.
.
expected 100000.00000, sum.getSum() = 100000.00000

ACTUAL -
java.version=1.8.0_40-ea
expected 1.00000, sum.getSum() = 1.00000
expected 2.00000, sum.getSum() = 2.00000
.
.
.
expected 645.000, sum.getSum() = 645.000
expected 646.000, sum.getSum() = 645.000 <<<< Looky here!!!!!!!

ERROR MESSAGES/STACK TRACES THAT OCCUR :
testAdd Failed: expected: <646.0> but was: <645.0>

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import org.junit.Assert;
import org.junit.Test;

/**
 *
 * @author michaelellis
 */
public class SumTest {

    @Test
    public void testAdd() {
        System.out.printf("java.version=%s%n", System.getProperty("java.version"));
        final Sum sum = new Sum();
        for (int i = 1; i <= 100000; ++i) {
            sum.add(1);
            System.out.printf("expected %g, sum.getSum() = %g%n", (double)i, sum.getSum());
            Assert.assertEquals((double) i, sum.getSum(), 0.0001);
        }
    }

}

class Sum {

    protected double[] sums;

    /**
     * Construct empty Sum
     */
    public Sum() {
        sums = new double[0];
    }

    /**
     * Return the sum of all numbers added to this Sum
     *
     * @return the sum
     */
    final public double getSum() {
        double sum = 0;
        for (final double s : sums) {
            sum += s;
        }

        return sum;
    }

    /**
     * Add a new number to this Sum
     *
     * @param a number to be added.
     */
    final public void add(final double a) {
        try {
            sums[sums.length] = -1; // Cause IndexOutOfBoundsException
        } catch (final IndexOutOfBoundsException e) {
            /**
             * SHOW STOPPER
             * 
             * By now the variable a can be corrupted!!!!
             */
            final double[] oldSums = sums;
            sums = new double[oldSums.length + 1]; // Extend sums
            System.arraycopy(oldSums, 0, sums, 0, oldSums.length);
            sums[oldSums.length] = a; // Append a
        }
    }
}

---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Do not rely on being able to use stack variables after an IndexOutOfBoundException.

This is a real Java8 Show Stopper. Any code that relies of IndexOutOfBoundsException rather than explicitly testing array lengths is susceptible to failure under Java 8. For example the ImgLib 2 project fails its JUnit Tests under Java 8 (This test program was devised after finding the bug with ImgLib class RealSum.

SUPPORT :
YES


Comments
ILW = HMH = P1 I = H: incorrect compiled code L = M: exception handling in C1 (Client VM & Tiered are affected) W = H: none
09-12-2014

I poked around this problem a little bit and it's funny how it worked before... We seem to be not saving/restoring xmm registers precisely in the cases when we should and vice versa (that's a C1 thing only, of course but is obviously true for tiered as well). I think the fix is something like: diff --git a/src/cpu/x86/vm/c1_Runtime1_x86.cpp b/src/cpu/x86/vm/c1_Runtime1_x86.cpp --- a/src/cpu/x86/vm/c1_Runtime1_x86.cpp +++ b/src/cpu/x86/vm/c1_Runtime1_x86.cpp @@ -675,7 +675,7 @@ case handle_exception_nofpu_id: case handle_exception_id: // At this point all registers MAY be live. - oop_map = save_live_registers(sasm, 1 /*thread*/, id == handle_exception_nofpu_id); + oop_map = save_live_registers(sasm, 1 /*thread*/, id != handle_exception_nofpu_id); break; case handle_exception_from_callee_id: { // At this point all registers except exception oop (RAX) and @@ -748,7 +748,7 @@ case handle_exception_nofpu_id: case handle_exception_id: // Restore the registers that were saved at the beginning. - restore_live_registers(sasm, id == handle_exception_nofpu_id); + restore_live_registers(sasm, id != handle_exception_nofpu_id); break; case handle_exception_from_callee_id: // WIN64_ONLY: No need to add frame::arg_reg_save_area_bytes to SP
09-12-2014

Looks like a C1 issue: doesn't reproduce with -Xint nor with -server with TieredCompilation disabled
09-12-2014

A little smaller testcase.
08-12-2014

Reproduced on JDK 8u20b26 32-bit, Win7 x64 More reliably reproduced if run the test in a cycle (probably the method is JITed at some point) If change final public void add(final double a) to final public void add(double a) the error may occur earlier
08-12-2014