Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-4841786

(cl) flaw in ClassLoader getPackage/definePackage API

    XMLWordPrintable

Details

    • Bug
    • Resolution: Other
    • P3
    • 9
    • 1.2.1
    • core-libs
    • None

    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.]

      Attachments

        Issue Links

          Activity

            People

              mchung Mandy Chung
              peterjones Peter Jones
              Votes:
              0 Vote for this issue
              Watchers:
              3 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved:
                Imported:
                Indexed: