130

According to the JLS, an int array should be filled by zeros just after initialization. However, I am faced with a situation where it is not. Such a behavior occurs first in JDK 7u4 and also occurs in all later updates (I use 64-bit implementation). The following code throws exception:

public static void main(String[] args) {
        int[] a;
        int n = 0;
        for (int i = 0; i < 100000000; ++i) {
            a = new int[10];
            for (int f : a)
                if (f != 0)
                  throw new RuntimeException("Array just after allocation: "+ Arrays.toString(a));
            Arrays.fill(a, 0);
            for (int j = 0; j < a.length; ++j)
                a[j] = (n - j)*i;
            for (int f : a)
                n += f;
        }
        System.out.println(n);
    }

The exception occurs after the JVM performs compilation of the code block and does not arise with -Xint flag. Additionally, the Arrays.fill(...) statement (as all other statements in this code) is necessary, and the exception does not occurs if it is absent. It is clear that this possible bug is bounded with some JVM optimization. Any ideas for the reason of such a behavior?

Update:
I see this behavior on HotSpot 64-bit server VM, Java version from 1.7.0_04 to 1.7.0_10 on Gentoo Linux, Debian Linux (both kernel 3.0 version) and MacOS Lion. This error can always be reproduced with the code above. I did not test this problem with a 32-bit JDK or on Windows. I already sent a bug report to the Oracle (bug id 7196857) and it will appear in public Oracle bug database in few days.

Update:
Oracle published this bug at their public bug database: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7196857

Stanislav Poslavsky
  • 2,398
  • 2
  • 24
  • 36
  • 15
    I'd say bug in the implementation if it's not following the spec – Anya Shenanigans Sep 07 '12 at 12:04
  • Works fine in `OpenJDK Server VM (build 20.0-b12, mixed mode)` – m0skit0 Sep 07 '12 at 12:06
  • I can't confirm this with Java 7u2 (x64) or Java 7u7 (x86) on Windows. What is the exact version, architecture and OS you're running on? Any flags? – Joachim Sauer Sep 07 '12 at 12:06
  • This code reproduces the exception always. – Stanislav Poslavsky Sep 07 '12 at 12:09
  • 2
    I just ran it on a Mac with 1.7.0_05 and it reproduces the issue consistently with random content in the array after the new int[10]. – Anya Shenanigans Sep 07 '12 at 12:11
  • 12
    Since you have a well-defined example that reliably reproduces the problem (at least on some platforms), have you considered [filing a bug](http://bugs.sun.com/)? – Joachim Sauer Sep 07 '12 at 12:23
  • 4
    Yes, you should most definitely file a bug report. This is a very serious bug! – Hot Licks Sep 07 '12 at 12:29
  • 7
    Yes, I have already sent a bug report to the Oracle (bug id 7196857) and it will appear in public Oracle bug database in few days. – Stanislav Poslavsky Sep 07 '12 at 12:34
  • 1
    I've sent a report to hotspot-dev Digest to speed things along a bit. – Hot Licks Sep 07 '12 at 12:39
  • 6
    I tried it with Java 7 update 7 64-bit on Windows and it didn't have a problem. – Peter Lawrey Sep 07 '12 at 13:12
  • 2
    Based on the chatter in my inbox the Java development crew is all over it and just about has the problem figured out. – Hot Licks Sep 07 '12 at 15:59
  • I run this program on the following plateform :java version "1.7.0-ea" Java(TM) SE Runtime Environment (build 1.7.0-ea-b145) Java HotSpot(TM) Client VM (build 21.0-b15, mixed mode, sharing) its didn't throw any exception got print the value of n – kTiwari Sep 07 '12 at 16:13
  • This bug occurs using Java 1.7.0_04 or later – Stanislav Poslavsky Sep 07 '12 at 16:18
  • 1
    java version "1.7.0_04" Java(TM) SE Runtime Environment (build 1.7.0_04-b22) Java HotSpot(TM) Client VM (build 23.0-b21, mixed mode) compiled with JDK 1.7.0 32 bit on Windows problem does not happen for me... just prints `n` – vane Sep 07 '12 at 22:43
  • 1
    By the way, Poslavsky, kudos for isolating this bug and producing a test case that reliably reproduces it! It's the sort of effort one rarely sees but which is very valuable! – Hot Licks Sep 08 '12 at 00:51
  • 2
    (And it's very rare indeed for someone to get a 30 rating on their very first post!) – Hot Licks Sep 08 '12 at 00:54
  • 2
    (Thought I'd point out that, based both on Poslavsky's statements above and the chatter I got in my inbox, the bug is in the JITCed version of the code. Some JVMs will JITC the body of a loop (in a method that's only called once) and others won't, so that's one reason for different results (in addition to differences in different versions of the JITC itself). Apparently it has to do with optimizations that attempt to avoid clearing objects that will be completely overwritten, and the logic is not detecting that the array here won't be overwritten before reference. – Hot Licks Sep 08 '12 at 02:48
  • in my setup it always happens at n = 57701. so at that point it causes an integer overflow.. i think that when this happens it gets the negative max value. the code is: a[j] = (n - j)*i; i always see my array as [0, 0, 0, 0, 0, -283115258, 0, 0, 14, 0] so on the 5th j , this is : (1159641735- 5)* 57701 = 66912487462730 (at cases with lower free mem this may happen on 57769 and it gives a negative array cause on the overflow it turned negative). – ramrunner Sep 08 '12 at 02:49
  • 1
    Can someone try running this with `-XX:CompileThreshold=0`? – obataku Sep 08 '12 at 03:04
  • @HotLicks Thank you for your words! It was really difficult to reduce this bug into a test case! – Stanislav Poslavsky Sep 08 '12 at 06:07
  • 3
    @PeterLawrey: I just tried it with jdk1.7.0_07 64-bit on Windows 7, and I could reproduce the problem. Proof: `Exception in thread "main" java.lang.RuntimeException: Array just after allocation: [0, 0, 0, 0, 0, -1558181744, 0, 0, 14, 0] at So.main(So.java:12)` – Roland Illig Sep 08 '12 at 11:01
  • 1
    @oldrinb: It works with that runtime parameter, it just takes an inordinate amount of time to do so. – Makoto Sep 08 '12 at 22:39
  • @Makoto it reproduces the bug? Can you tell me what `n` is when it failed? – obataku Sep 08 '12 at 22:41
  • No, it works as in it doesn't throw an exception. – Makoto Sep 08 '12 at 22:42
  • The explanation is given in the bug tracker: "So compiler correctly matches a complete array fill, but the check for uses between the allocation and the fill is faulty." – Kirill Rakhman Sep 09 '12 at 19:44
  • In the wild such code might not occur that often, from the description it requires that the array be used just before full array fill (not partial), might have been there for loong time. – Antti Haapala -- Слава Україні Sep 10 '12 at 07:09
  • 1
    @Stanislav Since your bug has been accepted why don't you go ahead and answer your own question and mark it as the accepted answer. This will remove this as the top unanswered question on the site. – Phil Patterson Sep 10 '12 at 17:38
  • @PhilPatterson There is an ambiguity in the OS versions, on which the problem exist. For example, I've tested on my Windows 7 64-bit with jdk1.7.0_07 and have not problem, but some argue that the problems arise. I'll wait the clarification from Oracle until tomorrow and paste the answer in any case. – Stanislav Poslavsky Sep 10 '12 at 18:31
  • 2
    @Stanislav I see what you are saying. I would agree that Oracle would be in a better position to elaborate on the scope of the issue (as far as the bug being in the JVM, OS specific, the JIT compiler, etc.). Either way this was an excellent bug catch on your part! Color me impressed. – Phil Patterson Sep 10 '12 at 19:35

2 Answers2

42

Here we are faced with a bug in the JIT-compiler. Compiler determines that the allocated array is filled after allocation in Arrays.fill(...), but the check for uses between the allocation and the fill is faulty. So, compiler performs an illegal optimization - it skips zeroing of allocated array.

This bug is placed in Oracle bug tracker (bug id 7196857). Unfortunately, I did not wait for any clarifications from Oracle about the following points. As I see, this bug is OS-specific: it absolutely reproducible on 64-bit Linux and Mac, but, as I see from comments, it reproduces not regularly on Windows (for similar versions of JDK). Additionally it would be nice to know when this bug will be fixed.

There is only advice at the moment: do not use JDK1.7.0_04 or later if you depend on JLS for newly declared arrays.

Update at October 5:

In the new Build 10 of the JDK 7u10 (early access) released at October 04, 2012, this bug was fixed at least for Linux OS (I did not test for other). Thanks to @Makoto, who found that this bug is no longer available for public access in Oracle bug database. Unfortunately, I do not know for the reasons Oracle removed it from public access, but it is available in Google cache. Also, this bug has caught the attention of Redhat: the CVE identifiers CVE-2012-4420 (bugzilla) and CVE-2012-4416 (bugzilla) were assigned to this flaw.

Stanislav Poslavsky
  • 2,398
  • 2
  • 24
  • 36
  • 2
    The bug ID is now invalid - could you look into this? – Makoto Sep 21 '12 at 15:52
  • 1
    @Makoto I am confused, since this bug was in bug database yesterday. I do not know for the reason Oracle removed this bug from the public access. But Google remember http://webcache.googleusercontent.com/search?q=cache:Q0V_rKQNw5sJ:bugs.sun.com/bugdatabase/view_bug.do%3Fbug_id%3D7196857+&cd=5&hl=en&ct=clnk Additionally this bug was also placed in RedHat bug database, since it can lead to a CVE https://bugzilla.redhat.com/show_bug.cgi?id=856124 – Stanislav Poslavsky Sep 21 '12 at 21:08
0

I made some change in your code. It's not a problem of Integer overflow. See the code, it throws an exception at runtime

    int[] a;
    int n = 0;
    for (int i = 0; i < 100000000; ++i) {
        a = new int[10];
        for (int f : a) {
            if (f != 0) {
                throw new RuntimeException("Array just after allocation: " + Arrays.toString(a));
            }
        }
        for (int ii = 0, len = a.length; ii < len; ii++)
            a[ii] = 0;
        for (int j = 0; j < a.length; ++j)
            a[j] = Integer.MAX_VALUE - 1;
        for (int j = 0; j < a.length; ++j)
            n++;
    }