JDK-5012949 : Reflect.Instances.add of a null causes a Sig 11 in 1.4.2
  • Type: Bug
  • Component: hotspot
  • Sub-Component: runtime
  • Affected Version: 1.4.2,5.0
  • Priority: P4
  • Status: Closed
  • Resolution: Fixed
  • OS: linux,windows_xp
  • CPU: x86
  • Submitted: 2004-03-12
  • Updated: 2012-10-08
  • Resolved: 2004-04-26
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.
Other
5.0 b49Fixed
Related Reports
Duplicate :  
Duplicate :  
Relates :  
Relates :  
Relates :  
Relates :  
Description
 Set s1 = (Set)((Map.Entry)o1).getValue();
                        Set s2 = (Set)((Map.Entry)o2).getValue();
                        return s2.size() - s1.size();
                    }
                });
            return new Itr(v);
        default:
            throw new IllegalArgumentException();
        }
    }

    // Iterates over the given collection of map entries
    static private class Itr implements Iterator {
        Iterator i;
        Itr(Collection c) {
            i = c.iterator();
        }
        public boolean hasNext () {
            return i.hasNext();
        }
        public Object next () {
            Map.Entry entry = (Map.Entry)i.next();
            return entry.getKey()+": "+((Set)entry.getValue()).size();
        }
        public void remove () {
            i.remove();
        }
    }

    /** Merges the given graph into this graph and returns the
     * combined graph.  Any type that appeared in one of the two
     * graphs now appears in this graph.  If the same instance appears
     * in both graphs, it counts only as one instance in the new
     * graph.  In particular, adding a graph to itself leaves this
     * graph unchanged.
     *
     * @param inst a graph of instances, along with their type names
     * @return this graph after the merge
     */
    public Instances add (Instances inst) {
        Iterator i = inst.allInstances.entrySet().iterator();
        while (i.hasNext()) {
            Map.Entry e = (Map.Entry)i.next();
            String key = (String)e.getKey();
            Set more = (Set)e.getValue();
            Set s = (Set)allInstances.get(key);
            if (s == null)
                allInstances.put(key, s = new HashSet());
            s.addAll(more);
        }
        return this;
    }

    /*
    // for debugging purposes
    public String toString () {
        StringBuffer b = new StringBuffer();            
        Iterator i = allInstances.keySet().iterator();
        while (i.hasNext()) {
            Object key = i.next();
            b.append("("+key+","+allInstances.get(key)+")\n");
        }
        return b.toString();
    }
    */

    public static void main (String[] args) {
        Instances inst = new Instances(new Thread() {public void run(){}});
    }
}

REPRODUCIBILITY :
This bug can be reproduced always.
(Incident Review ID: 240076) 
======================================================================


Name: rmT116609			Date: 03/12/2004


FULL PRODUCT VERSION :
java version "1.4.2"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.2-b28)
Java HotSpot(TM) Client VM (build 1.4.2-b28, mixed mode)

java version "1.5.0-beta"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0-beta-b32c)
Java HotSpot(TM) Client VM (build 1.5.0-beta-b32c, mixed mode)

FULL OS VERSION :
Linux berlioz 2.4.20-13.9custom #4 Wed Sep 24 08:56:20 EDT 2003 i686 i686 i386 GNU/Linux


A DESCRIPTION OF THE PROBLEM :
The VM crashes while running a recursive method.  The method uses reflection to list all the instances referenced by a Thread object.


ERROR MESSAGES/STACK TRACES THAT OCCUR :

Unexpected Signal : 11 occurred at PC=0x404216B8
Function=(null)+0x404216B8
Library=/usr/java/j2sdk1.4.2/jre/lib/i386/client/libjvm.so

NOTE: We are unable to locate the function name symbol for the error
      just occurred. Please refer to release documentation for possible
      reason and solutions.


Current Java thread:
	at java.lang.Class.getName(Native Method)
	at Reflect.Instances.add(Instances.java:36)
	at Reflect.Instances.add(Instances.java:57)
	at Reflect.Instances.add(Instances.java:57)
	at Reflect.Instances.add(Instances.java:46)
	at Reflect.Instances.add(Instances.java:57)
	at Reflect.Instances.add(Instances.java:57)
	at Reflect.Instances.add(Instances.java:57)
	at Reflect.Instances.add(Instances.java:57)
	at Reflect.Instances.add(Instances.java:57)
	at Reflect.Instances.add(Instances.java:46)
	at Reflect.Instances.add(Instances.java:57)
	at Reflect.Instances.add(Instances.java:57)
	at Reflect.Instances.add(Instances.java:57)
	at Reflect.Instances.add(Instances.java:57)
	at Reflect.Instances.add(Instances.java:57)
	at Reflect.Instances.add(Instances.java:46)
	at Reflect.Instances.add(Instances.java:57)
	at Reflect.Instances.add(Instances.java:57)
	at Reflect.Instances.add(Instances.java:57)
	at Reflect.Instances.add(Instances.java:57)
	at Reflect.Instances.add(Instances.java:46)
	at Reflect.Instances.add(Instances.java:57)
	at Reflect.Instances.add(Instances.java:57)
	at Reflect.Instances.add(Instances.java:57)
	at Reflect.Instances.main(Instances.java:121)

Dynamic libraries:
08048000-0804e000 r-xp 00000000 03:07 115900     /usr/java/j2sdk1.4.2/bin/java
0804e000-0804f000 rw-p 00005000 03:07 115900     /usr/java/j2sdk1.4.2/bin/java
40000000-40012000 r-xp 00000000 03:03 48178      /lib/ld-2.3.2.so
40012000-40013000 rw-p 00011000 03:03 48178      /lib/ld-2.3.2.so
40014000-4001c000 r-xp 00000000 03:07 588040     /usr/java/j2sdk1.4.2/jre/lib/i386/native_threads/libhpi.so
4001c000-4001d000 rw-p 00007000 03:07 588040     /usr/java/j2sdk1.4.2/jre/lib/i386/native_threads/libhpi.so
4001d000-40021000 rw-s 00000000 03:03 144855     /tmp/hsperfdata_charpov/7966
40022000-40030000 r-xp 00000000 03:03 48127      /lib/libpthread-0.10.so
40030000-40033000 rw-p 0000e000 03:03 48127      /lib/libpthread-0.10.so
40073000-40075000 r-xp 00000000 03:03 48109      /lib/libdl-2.3.2.so
40075000-40076000 rw-p 00002000 03:03 48109      /lib/libdl-2.3.2.so
40076000-40196000 r-xp 00000000 03:03 48105      /lib/libc-2.3.2.so
40196000-40199000 rw-p 00120000 03:03 48105      /lib/libc-2.3.2.so
4019c000-40593000 r-xp 00000000 03:07 490517     /usr/java/j2sdk1.4.2/jre/lib/i386/client/libjvm.so
40593000-405ae000 rw-p 003f6000 03:07 490517     /usr/java/j2sdk1.4.2/jre/lib/i386/client/libjvm.so
405c1000-405d1000 r-xp 00000000 03:03 48113      /lib/libnsl-2.3.2.so
405d1000-405d2000 rw-p 00010000 03:03 48113      /lib/libnsl-2.3.2.so
405d4000-405f4000 r-xp 00000000 03:03 48111      /lib/libm-2.3.2.so
405f4000-405f5000 rw-p 00020000 03:03 48111      /lib/libm-2.3.2.so
405f5000-405f8000 r--s 00000000 03:07 881428     /usr/java/j2sdk1.4.2/jre/lib/ext/dnsns.jar
405f8000-405fe000 r--s 00000000 03:07 407433     /usr/lib/gconv/gconv-modules.cache
40603000-4060d000 r-xp 00000000 03:03 48192      /lib/libnss_compat-2.3.2.so
4060d000-4060e000 rw-p 00009000 03:03 48192      /lib/libnss_compat-2.3.2.so
4060e000-4061e000 r-xp 00000000 03:07 588047     /usr/java/j2sdk1.4.2/jre/lib/i386/libverify.so
4061e000-40620000 rw-p 0000f000 03:07 588047     /usr/java/j2sdk1.4.2/jre/lib/i386/libverify.so
40620000-40640000 r-xp 00000000 03:07 588048     /usr/java/j2sdk1.4.2/jre/lib/i386/libjava.so
40640000-40642000 rw-p 0001f000 03:07 588048     /usr/java/j2sdk1.4.2/jre/lib/i386/libjava.so
40642000-40656000 r-xp 00000000 03:07 588050     /usr/java/j2sdk1.4.2/jre/lib/i386/libzip.so
40656000-40659000 rw-p 00013000 03:07 588050     /usr/java/j2sdk1.4.2/jre/lib/i386/libzip.so
40659000-41fe4000 r--s 00000000 03:07 588306     /usr/java/j2sdk1.4.2/jre/lib/rt.jar
4202e000-42044000 r--s 00000000 03:07 588277     /usr/java/j2sdk1.4.2/jre/lib/sunrsasign.jar
42044000-4211f000 r--s 00000000 03:07 588303     /usr/java/j2sdk1.4.2/jre/lib/jsse.jar
4211f000-42130000 r--s 00000000 03:07 588278     /usr/java/j2sdk1.4.2/jre/lib/jce.jar
42130000-42689000 r--s 00000000 03:07 588304     /usr/java/j2sdk1.4.2/jre/lib/charsets.jar
44731000-4473e000 r--s 00000000 03:07 881430     /usr/java/j2sdk1.4.2/jre/lib/ext/ldapsec.jar
4c7c0000-4c9c0000 r--p 00000000 03:07 196947     /usr/lib/locale/locale-archive
4c9c0000-4c9dc000 r--s 00000000 03:07 881427     /usr/java/j2sdk1.4.2/jre/lib/ext/sunjce_provider.jar
4c9dc000-4ca98000 r--s 00000000 03:07 881462     /usr/java/j2sdk1.4.2/jre/lib/ext/localedata.jar

Heap at VM Abort:
Heap
 def new generation   total 576K, used 497K [0x44740000, 0x447e0000, 0x44c20000)
  eden space 512K,  97% used [0x44740000, 0x447bc498, 0x447c0000)
  from space 64K,   0% used [0x447c0000, 0x447c0000, 0x447d0000)
  to   space 64K,   0% used [0x447d0000, 0x447d0000, 0x447e0000)
 tenured generation   total 1408K, used 0K [0x44c20000, 0x44d80000, 0x48740000)
   the space 1408K,   0% used [0x44c20000, 0x44c20000, 0x44c20200, 0x44d80000)
 compacting perm gen  total 4096K, used 1154K [0x48740000, 0x48b40000, 0x4c740000)
   the space 4096K,  28% used [0x48740000, 0x48860be8, 0x48860c00, 0x48b40000)

Local Time = Mon Feb 23 17:51:34 2004
Elapsed Time = 2
#
# HotSpot Virtual Machine Error : 11
# Error ID : 4F530E43505002EF
# Please report this error at
# http://java.sun.com/cgi-bin/bugreport.cgi
#
# Java VM: Java HotSpot(TM) Client VM (1.4.2-b28 mixed mode)
#


With 1.5.0 Beta 1:

 of type class java.lang.Class
 of type class java.lang.String
 of type class [C
 of type class [Ljava.io.ObjectStreamField;
 of type class java.lang.String$CaseInsensitiveComparator
 of type class java.lang.Class
 of type class [Ljava.io.ObjectStreamField;
 of type class sun.reflect.ReflectionFactory
 of type class [Ljava.lang.annotation.Annotation;
 of type class sun.reflect.UnsafeStaticObjectFieldAccessorImpl
#
# An unexpected error has been detected by HotSpot Virtual Machine:
#
#  SIGSEGV (0xb) at pc=0x403473f8, pid=2783, tid=8192
#
# Java VM: Java HotSpot(TM) Client VM (1.5.0-beta-b32c mixed mode)
# Problematic frame:
# V  [libjvm.so+0x2e43f8]
#
# An error report file with more information is saved as hs_err_pid2783.log
#
# If you would like to submit a bug report, please visit:
#   http://java.sun.com/webapps/bugreport/crash.jsp



Source Code:

package Reflect;

// as is, it crashes
// uncomment line 111 to get a bizarre exception 
// (object o seems to "become" null inside String.ValueOf)
// Replace the call to recAdd by iterAdd on line 81 to get the same kind of
// strange thing: ArrayStoreException in Vector.addElement!


import java.util.*;
import java.lang.reflect.*;

/** Explores a graph of instances.  Given an object as a starting
 * point, all instances reachable from this object are explored.  This
 * implementation can then provide a list of all the types encountered
 * along with the number of occurrences in each type.  Different
 * graphs can be combined using the <code>add</code> method.
 *
 * @author  Michel Charpentier
 * @version 1.0, 02/24/04
 * @see Class
 * @see <a href="Tests.java">Tests.java</a>
 */
public class Instances {

    /** Types are returned in an unspecified order.
     *
     * @see #iterator
     */
    public static final int UNSORTED              = 0;

    /** Types are returned in alphabetical order of their fully qualified names.
     *
     * @see #iterator
     */
    public static final int SORTED_BY_NAME        = 1;

    /** Types are returned in decreasing order of the number of
     * occurrences in each type.
     *
     * @see #iterator
     */
    public static final int SORTED_BY_OCCURRENCES = 2;

    // Keys are type names, values are sets of instances
    private final Map allInstances = new TreeMap();

    // Wrapper around instances to make java.util stuff rely on == 
    // instead of equals()
    static private class Instance {
        Object instance;
        Instance (Object o) {
            instance = o;
        }
        public boolean equals (Object o) {
            return ((Instance)o).instance == this.instance;
        }
        public int hashCode () {
            return instance.hashCode();
        }
        public String toString () { // for debugging purposes
            return instance.toString();
        }
    }

    /** Construct a new graph by adding instance <code>o</code> and
     * all instances reachable from <code>o</code> to an empty graph.  This
     * constructor attempts to follow all data members regardless of their
     * visibility.
     * 
     * @param o initial instance
     * @throws SecurityException if a security manager prevents
     * the method from accessing some data member
     * @see SecurityManager
     */
    public Instances (Object o) throws SecurityException {
        if (o == null)
            throw new NullPointerException();
        recAdd(o, -1);
    }

    /** Construct a new graph by adding instance <code>o</code> and
     * all instances reachable from <code>o</code> within the given
     * <code>depth</code> to an empty graph.  A depth of 0 only adds object
     * <code>o</code>; a depth of 1 adds all the fields of
     * <code>o</code>.  This constructor attempts to follow all data
     * members regardless of their visibility.
     * 
     * @param o initial instance
     * @param depth depth of the exploration (maximum distance from
     * origin <code>o</code>
     * @throws IllegalArgumentException if <code>depth</code> is negative
     * @throws SecurityException if a security manager prevents
     * the method from accessing some data member
     * @see SecurityManager
     */
    public Instances (Object o, int depth) throws SecurityException {
        if (o == null)
            throw new NullPointerException();
        if (depth < 0)
            throw new IllegalArgumentException("depth cannot be negative.");
        recAdd(o, depth+1);
    }

    // recursive solution
    private void recAdd (Object o, int depth) {
        if (o == null || depth == 0) return;
        Class c = o.getClass();
        //System.out.print("Found "+o);
        System.out.println(" of type "+c);
        String name = c.getName();
        Set s = (Set)allInstances.get(name);
        if (s == null) // first occurrence of this type
            allInstances.put(name, s = new HashSet());
        if (s.add(new Instance(o))) {
            if (c.isArray() && !c.getComponentType().isPrimitive()) {
                int l = Array.getLength(o);
                for (int i=0; i<l; i++)
                    recAdd(Array.get(o, i), depth-1);
                return; // is this safe?
            }
            do {
                Field[] fields = c.getDeclaredFields();
                AccessibleObject.setAccessible(fields, true);
                for (int i=0; i<fields.length; i++) {
                    Field f = fields[i];
                    if (!f.getType().isPrimitive())
                        try {
                            recAdd(f.get(o), depth-1);
                        } catch (IllegalAccessException e) {
                            System.err.println
                                ("Could not follow field "+f+" in "+c);
                        }
                }
                c = c.getSuperclass();
            } while (c != null);
        }
    }

    // iterative solution
    private void iterAdd (Object start, int depth) {
        Stack stack1 = new Stack();
        stack1.push(start);
        while (depth != 0) {
            Stack stack2 = new Stack();
            while (!stack1.isEmpty()) {
                Object o = stack1.pop();
                if (o == null) continue;
                Class c = o.getClass();
                String name = c.getName();
                Set s = (Set)allInstances.get(name);
                if (s == null) // first occurrence of this type
                    allInstances.put(name, s = new HashSet());
                if (s.add(new Instance(o))) {
                    if (c.isArray() && !c.getComponentType().isPrimitive()) {
                        int l = Array.getLength(o);
                        for (int i=0; i<l; i++)
                            stack2.push(Array.get(o, i));
                        continue; // is it safe?
                    }
                    do {
                        Field[] fields = c.getDeclaredFields();
                        AccessibleObject.setAccessible(fields, true);
                        for (int i=0; i<fields.length; i++) {
                            Field f = fields[i];
                            if (!f.getType().isPrimitive())
                                try {
                                    stack2.push(f.get(o));
                                } catch (IllegalAccessException e) {
                                    System.err.println
                                        ("Could not follow field "+f+" in "+c);
                                }
                        }
                        c = c.getSuperclass();
                    } while (c != null);
                }
            }
            if (stack2.isEmpty()) break;
            stack1 = stack2;
            depth--;
        }
    }

    /** An iterator over all the class names found in the graph.  Each call
     * to <code>next()</code> returns a <code>String</code>.  These
     * strings are in no particular order, in alphabetical order, or
     * in decreasing order of the number of occurrences of this type,
     * depending on the value of <code>type</code>.
     *
     * @param type specifies the order of iteration
     * @return an iterator of class names (as strings), in the specified order
     * @see #UNSORTED
     * @see #SORTED_BY_NAME
     * @see #SORTED_BY_OCCURRENCES
     */
    public Iterator iterator (int type) {
        switch (type) {
        case UNSORTED:
        case SORTED_BY_NAME:
            return new Itr(allInstances.entrySet());
        case SORTED_BY_OCCURRENCES:
            Vector v = new Vector(allInstances.entrySet());
            Collections.sort(v, new Comparator() {
                    public int compare (Object o1, Object o2) {
                       

Comments
CONVERTED DATA BugTraq+ Release Management Values COMMIT TO FIX: tiger-beta2 FIXED IN: tiger-beta2 INTEGRATED IN: tiger-b49 tiger-beta2
14-06-2004

EVALUATION I do see the crash with 1.4.2_04 release. Cannot reproduce with 1.5.0-b44 build. Has the customer tried 1.5.0-b32 from the web.. I tested with it ,no crash occurs, just NullPointerExceptions. ###@###.### 2004-03-25 These crashes and NullPointerExceptions are occurring because the HotSpot JVM and J2SE libraries expose some JVM-internal objects which are not Java objects (i.e., java.lang.Object) at the library level and in the bytecodes. Attempting to perform Object-level operations like hashCode() or getName() on these objects, or to store them into an Object[], will fail in unpredictable ways. Some of these non-Java objects are stored in fields of other objects. Others are return values from native methods implemented by the JVM. Hiding these fields from the reflection implementation is probably the easiest and most feasible thing to do, although it won't solve the problem completely. One way of doing this would be to simply filter these known fields out from the return values from the field operations of class Class. Another would be to have the JVM insert the fields manually so they don't show up at the bytecode level at all. I'll plan on implementing the first of these for 1.5.1 though we have to be careful not to impact the performance of Class.getFields() in doing so. The reason this doesn't solve the problem completely is that there are still some bytecodes which hold on to these objects and operate on them, at minimum passing them down to other native methods. If a debugger is attached to the JVM then inspection of these objects in the debugger will crash the system. There isn't a good way to solve this problem aside from making the inheritance hierarchy truly bottom out at java.lang.Object rather than some JVM-internal classes. This is a longer-term project. As a workaround, the reflective iteration can be truncated at a couple of well- known classes. ###@###.### 2004-03-25 It turns out the NullPointerException reported in the comments section is real, not the VM misinterpreting a crash. The problem is the sentinel element used in a LinkedHashMap, which is used to implement HashSet, used in the test program. If the hashCode() method is called on this sentinel LinkedHashMap$Entry, it throws NullPointerException. The test case for 5012260 uses an IdentityHashMap for this reason. I've verified the proposed fix fixes the test cases for 5012260 and 5018055. ###@###.### 2004-04-01 Fixed in 1.5.0 beta 2, build b49. ###@###.### 2004-04-19
01-04-2004

WORK AROUND Truncate the test program's introspection if the following classes are encountered: java.lang.Throwable sun.reflect.UnsafeStaticFieldAccessorImpl sun.reflect.ConstantPool ###@###.### 2004-03-25
25-03-2004