JEP 396: Strongly Encapsulate JDK Internals by Default (original) (raw)

Summary

Strongly encapsulate all internal elements of the JDK by default, except for critical internal APIs such as sun.misc.Unsafe. Allow end users to choose the relaxed strong encapsulation that has been the default since JDK 9.

Goals

Non-Goals

Motivation

Over the years the developers of various libraries, frameworks, tools, and applications have used internal elements of the JDK in ways that compromise both security and maintainability. In particular:

In Java 9, we improved both the security and the maintainability of the JDK by leveraging modules to limit access to its internal elements. Modules provide strong encapsulation, which means that

Strong encapsulation applies at both compile time and run time, including when compiled code attempts to access elements via reflection at run time. The non-public elements of exported packages, and all elements of unexported packages, are said to be strongly encapsulated.

In JDK 9 and later releases we strongly encapsulated all new internal elements, thereby limiting access to them. As an aid to migration, however, we deliberately chose not to strongly encapsulate, at run time, the content of packages that existed in JDK 8. Library and application code on the class path could thus continue to use reflection to access the non-public elements of java.* packages, and all elements of sun.* and other internal packages, for packages that existed in JDK 8. This arrangement is called relaxed strong encapsulation.

We released JDK 9 in September 2017. Most of the commonly-used internal elements of the JDK now have standard replacements. Developers have had over three years in which to migrate away from internal elements of the JDK to standard APIs such asjava.lang.invoke.MethodHandles.Lookup::defineClass, java.util.Base64, and java.lang.ref.Cleaner. Many library, framework, and tool maintainers have completed that migration and released updated versions of their components. We are now ready to take the next step toward the strong encapsulation of all internal elements of the JDK — except for critical internal APIs such as sun.misc.Unsafe — as originally planned in Project Jigsaw.

Description

Relaxed strong encapsulation is controlled by the launcher option--illegal-access. This option, introduced by JEP 261, is provocatively named in order to discourage its use. It presently works as follows:

As the next step toward strongly encapsulating all internal elements of the JDK, we propose to change the default mode of the --illegal-accessoption from permit to deny. With this change, packages that existed in JDK 8 and do not contain critical internal APIs will no longer be open by default; a complete list is availablehere. The sun.misc package will still be exported by the jdk.unsupported module, and will still be accessible via reflection.

We will also revise the related text in the Java Platform Specification to disallow the opening of any package by default in any Java Platform Implementation, unless that package is explicitly declared to be open in the declaration of its containing module.

The permit, warn, and debug modes of the --illegal-access option will continue to work. These modes allow end users to choose relaxed strong encapsulation if they wish.

We expect a future JEP to remove the --illegal-access option entirely. At that point it will not be possible to open all of the JDK 8 packages via a single command-line option. It will still be possible to use the --add-opens command-line option, or theAdd-Opens JAR-file attribute, to open specific packages.

To prepare for the eventual removal of the --illegal-access option we will deprecate it for removal as part of this JEP. As a consequence, specifying that option to the java launcher will cause a deprecation warning to be issued.

Risks and Assumptions

The primary risk of this proposal is that existing Java code will fail to run. The kinds of code that will fail include, but are not limited, to:

We encourage all developers to:

Secondary risks

Examples of the impact of this change

System.out.println(sun.security.util.SecurityConstants.ALL_PERMISSION);  

will fail with an exception of the form

Exception in thread "main" java.lang.IllegalAccessError: class Test  
  (in unnamed module @0x5e481248) cannot access class  
  sun.security.util.SecurityConstants (in module java.base) because  
  module java.base does not export sun.security.util to unnamed  
  module @0x5e481248  
var ks = java.security.KeyStore.getInstance("jceks");  
var f = ks.getClass().getDeclaredField("keyStoreSpi");  
f.setAccessible(true);  

will fail with an exception of the form

Exception in thread "main" java.lang.reflect.InaccessibleObjectException:  
  Unable to make field private java.security.KeyStoreSpi  
  java.security.KeyStore.keyStoreSpi accessible: module java.base does  
  not "opens java.security" to unnamed module @6e2c634b  
var dc = ClassLoader.class.getDeclaredMethod("defineClass",  
                                             String.class,  
                                             byte[].class,  
                                             int.class,  
                                             int.class);  
dc.setAccessible(true);  

will fail with an exception of the form

Exception in thread "main" java.lang.reflect.InaccessibleObjectException:  
  Unable to make protected final java.lang.Class  
  java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int)  
  throws java.lang.ClassFormatError accessible: module java.base does  
  not "opens java.lang" to unnamed module @5e481248