JDK-4151665 : loading non-existent class through HTTP can throw ClassFormatError
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.net
  • Affected Version: 1.2.0
  • Priority: P2
  • Status: Closed
  • Resolution: Fixed
  • OS: solaris_2.5
  • CPU: sparc
  • Submitted: 1998-06-23
  • Updated: 1999-01-15
  • Resolved: 1999-01-15
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
1.2.0 1.2fcsFixed
Related Reports
Relates :  
Description
With a configuration-dependent probability, a ClassFormatError will be thrown
when an attempt is made to load a class from an HTTP URL that doesn't exist,
but there is an HTTP server listening on the indicated host and port.
A checked ClassNotFoundException should be thrown instead.

This is bad not only because an unchecked Error will be thrown instead of the
checked ClassNotFoundException in situations that should fail somehow (albeit
more gracefully) anyway, but also because it will cause class loading to fail
even when it should otherwise succeed (for correct installations).

For example, 1.2's new class loader delegation mechanism depends on the checked
ClassNotFoundException to be thrown even for entirely correct class
installations.  Imagine that URLClassLoader CLA has as its parent URLClassLoader
CLB, and CLB has an "http:" codebase URL.  If we ask CLA to load a class Foo
that is only available through CLA's codebase, CLA will still try to load Foo
from CLB first, because CLB is its parent, but that is expected to fail with a
ClassNotFoundException, which CLA will catch and proceed to look for the class
in its own codebase.  But if CLB throws a ClassFormatError instead, CLA will not
catch it, and the whole class loading process will fail, even though everything
was configured properly.

The above situation can occur when, for example, an applet (loaded through HTTP)
that uses RMI needs to download the type of an unmarshalled return value; the
class for the return type would be CLA, and the applet's class loader would be
CLB.

The probability of the ClassFormatError is noticeably increased by the following
factors:

* running with the JIT (solaris).  This is not a JIT bug, as far as I can tell,
  as the ClassFormatError will still happen without the JIT running, but it does
  increase the likelihood significantly, probably by affecting relevant timing.
* loading from certain web servers.  The ClassFormatError *seems* (to me) more
  likely to occur trying to load from the Java Web Server 1.1 (on jini.east)
  than from 1.0 (on jse.east).
* loading from web servers that are nearby (network-wise).  The ClassFormatError
  is easiest to reproduce if the web server is on the same subnet, for example.

I have not yet found a configuration (login machine and web server machine) in
the ".eng" domain that reproduces this problem; perhaps someone can point me to
a good candidate.  But it's quite easy to reproduce over here in ".east";
logged in on "terrier.east" and loading from "jini.east", for example.  The
following sample program demonstrates the effect:

import java.net.*;
 
public class TestLoad {
     public static void main(String[] args) {
        try {
            URL url = new URL(args.length >= 1 ? args[0] : "http://jini.east/");
            String name = args.length >= 2 ? args[1] : "foo.bar.Baz";
            ClassLoader loader = new URLClassLoader(new URL[] { url });
            Class c = loader.loadClass(name);
            System.out.println("Loaded class \"" + c.getName() + "\".");
        } catch (Exception e) {
            e.printStackTrace(System.out);
        }
    }
}

Needless to say, the class "foo.bar.Baz" does not exist at "http://jini.east".
The following output shows running this program on "terrier.east" and getting
the failure (which happens almost all of the time in this configuration):

[terrier] 52 % java -fullversion
java full version "JDK-1.2beta4-J"
[terrier] 53 % java TestLoad
Exception in thread "main" java.lang.ClassFormatError: foo/bar/Baz (Bad magic number)
        at java.lang.ClassLoader.defineClass0(Native Method)
        at java.lang.ClassLoader.defineClass(Compiled Code)
        at java.security.SecureClassLoader.defineClass(Compiled Code)
        at java.net.URLClassLoader.defineClass(Compiled Code)
        at java.net.URLClassLoader.access$1(Compiled Code)
        at java.net.URLClassLoader$1.run(Compiled Code)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(Compiled Code)
        at java.lang.ClassLoader.loadClass(Compiled Code)
        at java.lang.ClassLoader.loadClass(Compiled Code)
        at TestLoad.main(Compiled Code)
[terrier] 54 % 

The "Comments" section of this bug report contains a more detailed description
of what I think is causing the problem (and a suggested fix).  In short, the
class loader is attempting to call ClassLoader.defineClass() with the bytes of
the HTML error message returned from the web server that says that the URL did
not exist on the server.  Obviously, this HTML message is, in all likelihood,
not a valid class file, and thus the disasterous ClassFormatError is thrown.

Comments
CONVERTED DATA BugTraq+ Release Management Values COMMIT TO FIX: 1.2fcs FIXED IN: 1.2fcs INTEGRATED IN: 1.2fcs
14-06-2004

WORK AROUND Not using the JIT seems to make the problem occur much less often, but it still can happen. This is not a JIT bug, of course.
11-06-2004

SUGGESTED FIX See the bottom of the "Comments" section.
11-06-2004

EVALUATION Throwing the FileNotFoundException earlier so that it is not caught in getHeaderField() seems like a reasonable patch. michael.mccloskey@eng 1998-06-24
24-06-1998