JDK-4841786 : (cl) flaw in ClassLoader getPackage/definePackage API
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.lang:class_loading
  • Affected Version: 1.2.1
  • Priority: P3
  • Status: Closed
  • Resolution: Other
  • OS: solaris_8
  • CPU: sparc
  • Submitted: 2003-04-02
  • Updated: 2017-12-20
  • Resolved: 2016-03-23
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 9
9Resolved
Related Reports
Relates :  
Relates :  
Relates :  
Relates :  
Description
CL.definePackage specifies that it throws IllegalArgumentException if the specified package name duplicates the name of a package already defined in this class loader or one of its ancestor loaders (in other words, if an invocation of getPackage with the same name would return a non-null Package object).  But there is no way for a caller (a subclass of ClassLoader) to guarantee that invoking CL.definePackage will not throw an IllegalArgumentException (unless the caller has complete operational control over all of the loaders in its chain of ancestors, which is not generally practical).

A pratical example is that the private java.net.URLClassLoader.defineClass method contains a code sequence roughly like this:

	    Package pkg = getPackage(pkgname);
	    if (pkg != null) {
		// ...
	    } else {
		definePackage(...);
	    }

That code is not safe because it is possible that an ancestor loader will define the named package after getPackage returns null but before definePackage is invoked.  [The synchronization of ClassLoader.loadClass prevents the same loader from defining the named package as part of a concurrent loading operation, but nothing prevents an ancestor loader from doing so (and the lock that guards ClassLoader's package definitions is private).]

Therefore, if a given URLClassLoader (L1) and its parent loader (L2) can both load classes in package P, if L1 can load a class in P that L2 cannot, and if both L1 and L2 can be concurrently asked to load classes in P, then it is possible that L2's loading, which should succeed, will fail with an unchecked IllegalArgumentException-- which can then turn into other unexpected exceptions (such as Errors), depending on the context.

Attached is an example that demonstrates the above situation.  A subclass of URLClassLoader is used, which overrides definePackage to add a Thread.sleep in order to magnify the time window in between the getPackage invocation and the definePackage invocation and thus force the bug to occur.  To run the example:

- unzip packagerace.zip somewhere
- cd into the created "packagerace" directory
- execute "java -jar packagerace.jar"
- observe the IllegalArgumentException trace

In this example, the path of the system class loader is packagerace.jar and thus contains the class definitions for PackageRace (and its nested classes) and foo.Bar.  The path of the created URLClassLoader subclass is baz.jar (in the current directory), which contains the class definitions for foo.Bar and foo.Baz (which is not loadable by the system class loader).  [Note that using a JAR file with a manifest for the class path is only important to reproduce this bug with J2SE 1.2.x (because of 4244970); with J2SE 1.3 and beyond (and thus 4244970 fixed), the class path can also be just the current directory to reproduce this bug.]

This bug has been turning up in practice, occasionally in Jini test suite runs.

A bandage to URLClassLoader (see "Suggested Fix") might be a sufficient fix for most practical occurrences of this bug.

[One might wonder that with the current CL.getPackage and CL.definePackage specifications, there can be a race with regard to whether or not a class loader or its parent gets to "define" a given Package-- yes, that's a known problem; see RFEs 4302423 and 4302406.]

Comments
Package API has been updated in jdk-9+111 and this issue has been resolved. See the summary of the API change described at: https://bugs.openjdk.java.net/browse/JDK-8061804?focusedCommentId=13916978&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-13916978
23-03-2016

SUGGESTED FIX In the long term, we still think that the ClassLoader getPackage/definePackage API should be repaired along the lines described in RFE 4302423 (independent of the "preferred classes" feature that that RFE was originally filed for). But in the short term, much of the practical effect of this bug could probably be ameliorated by making URLClassLoader.defineClass tolerant of ClassLoader.definePackage throwing an IllegalArgumentException; for example, URLClassLoader.defineClass could swallow the exception, re-invoke ClassLoader.getPackage, and just proceed as if the original invocation of getPackage had returned a non-null Package in the first place (since that would have been what would have happened had the timing been slightly different anyway). ###@###.### 2003-04-02
02-04-2003

WORK AROUND If you are in control of the ClassLoader implementation (perhaps not the typical case), then override ClassLoader.definePackage to delegate to the superclass definePackage method but catch IllegalArgumentException and in the catch block, return the result of invoking getPackage with the specified name (perhaps a little odd, but I can't think of anything better to return-- at any rate, URLClassLoader doesn't actually care what definePackage returns). ###@###.### 2003-04-02
02-04-2003

EVALUATION The submitter is correct. We will attempt to resolve this problem in the Tiger timeframe. -- iag@sfbay 2003-04-2
02-04-2003