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

Proxy package membership to respect referenced package-private types

XMLWordPrintable

    • Icon: CSR CSR
    • Resolution: Unresolved
    • Icon: P4 P4
    • tbd
    • core-libs
    • None
    • behavioral
    • medium
    • Hide
      This change introduces new restrictions to the factory of Proxy, as now public interfaces with different package-private types in signatures (which can happen to one interface through inheritance, or by supplying different interfaces) are rejected.

      The behavior changes with the package membership may also affect users who wish to obtain a Proxy class with an accessible constructor but is not interested in calling the problematic methods (or returning non-null for such methods), as now some proxy classes previously in unconditionally exported packages are now in user packages.
      Show
      This change introduces new restrictions to the factory of Proxy, as now public interfaces with different package-private types in signatures (which can happen to one interface through inheritance, or by supplying different interfaces) are rejected. The behavior changes with the package membership may also affect users who wish to obtain a Proxy class with an accessible constructor but is not interested in calling the problematic methods (or returning non-null for such methods), as now some proxy classes previously in unconditionally exported packages are now in user packages.
    • Java API
    • SE

      Summary

      The package and module membership of java.lang.reflect.Proxy implementations should consider package-private types in all referenced types, in addition to the proxied interfaces.

      Problem

      The Java programming language allows an accessible method to refer to inaccessible types (package-private or module unexported); in practice, when such an inaccessible type is required, users can pass an instance of a public subtype or the null literal, and when such a type is returned, users can store it in a variable of a public supertype, such as Object or a public superinterface. For example, if a public method of type (AbstractStringBuilder) -> AbstractStringBuilder is exposed, users can use it as if it has a (StringBuffer) -> Appendable type.

      For java.lang.reflect.Proxy, the longstanding behavior (possibly since the inception of Proxy) of package membership of the implementation only consider the package-private proxied interfaces. This allows creation of valid proxy instances; however, if an interface defines a method that returns a package-private type, the implementation of Proxy could not return such a type unless it is null due to the restrictions of the checkcast instruction - it performs an access check against the target type if and only if the incoming value is non-null.

      It is a little bit surprising that such an issue is only lately reported - since the implementation of annotations in core reflection uses Proxy, an annotation interface that has a package-private enum or annotation (or an array of them) element can easily reproduce this issue once those elements are accessed. Such usages even exist in the tests from OpenJDK, except that these elements were not accessed via annotation interfaces elements in those tests.

      Solution

      Tighten the restrictions on package and module membership of Proxy: the existence of package-private classes and interfaces is now checked against all referenced classes and interfaces in public non-static methods, in addition to the proxy interfaces.

      1. If two package-private classes and interfaces from this extended set come from different run-time packages, Proxy generation fails.
      2. If this extended set is not empty, Proxy implementation now uses the package-private package and module membership, where previously an implementation may live in a dynamic module, either in an unconditionally exported or non-exported package.

      Specification

      In the Proxy properties section:

      @@ -104,7 +104,7 @@
        * on its {@code Class} object will return an array containing the same
        * list of interfaces (in the order specified at its creation), invoking
        * {@link Class#getMethods getMethods} on its {@code Class} object will return
      - * an array of {@code Method} objects that include all of the
      + * an array of {@code Method} objects that include all of the public non-static
        * methods in those interfaces, and invoking {@code getMethod} will
        * find methods in the proxy interfaces as would be expected.
        *

      For package/module membership:

      @@ -165,40 +165,36 @@
        *
        * The package and module to which a proxy class belongs are chosen such that
        * the accessibility of the proxy class is in line with the accessibility of
      - * the proxy interfaces. Specifically, the package and the module membership
      - * of a proxy class defined via the
      + * the proxy interfaces and all classes and interfaces referenced by signatures
      + * of all public non-static methods from the proxy interfaces. Specifically, the
      + * package and the module membership of a proxy class defined via the
        * {@link Proxy#getProxyClass(ClassLoader, Class[])} or
        * {@link Proxy#newProxyInstance(ClassLoader, Class[], InvocationHandler)}
        * methods is specified as follows:
        *
        * <ol>
      - * <li>If all the proxy interfaces are in <em>exported</em> or <em>open</em>
      - *     packages:
      + * <li>If some of these classes and interfaces are <em>non-public</em>, all
      + *     non-public classes and interfaces must be in the same package and module,
      + *     and that module must be able to access all public classes, some of which
      + *     may be in <em>non-exported</em> and <em>non-open</em> packages.
      + *     Otherwise, proxying is {@linkplain ##restrictions not possible}.
      + *     The proxy class is <em>non-public</em> in the same package and module as
      + *     the non-public classes and interfaces.
      + *     </li>
      + * <li>Otherwise, all these classes and interfaces are <em>public</em>, and all
      + *     proxy interfaces are public.
        * <ol type="a">
      - * <li>if all the proxy interfaces are <em>public</em>, then the proxy class is
      - *     <em>public</em> in an unconditionally exported but non-open package.
      - *     The name of the package and the module are unspecified.</li>
      - *
      - * <li>if at least one of all the proxy interfaces is <em>non-public</em>, then
      - *     the proxy class is <em>non-public</em> in the package and module of the
      - *     non-public interfaces. All the non-public interfaces must be in the same
      - *     package and module; otherwise, proxying them is
      - *     <a href="#restrictions">not possible</a>.</li>
      - * </ol>
      - * </li>
      - * <li>If at least one proxy interface is in a package that is
      - *     <em>non-exported</em> and <em>non-open</em>:
      - * <ol type="a">
      - * <li>if all the proxy interfaces are <em>public</em>, then the proxy class is
      + * <li>If all proxy interfaces are in <em>exported</em> or <em>open</em>
      + *     packages, the proxy class is <em>public</em> in an unconditionally
      + *     exported but non-open package in a {@linkplain ##dynamicmodule
      + *     <em>dynamic module</em>}. The names of the package and the module are
      + *     unspecified. (In this case, any of the referenced class or interface that
      + *     is not a proxied interface may be non-exported and non-open.)</li>
      + * <li>Otherwise, at least one of the proxy interfaces is in a package that is
      + *     <em>non-exported</em> and <em>non-open</em>, and the proxy class is
        *     <em>public</em> in a <em>non-exported</em>, <em>non-open</em> package of
      - *     <a href="#dynamicmodule"><em>dynamic module</em>.</a>
      - *     The names of the package and the module are unspecified.</li>
      - *
      - * <li>if at least one of all the proxy interfaces is <em>non-public</em>, then
      - *     the proxy class is <em>non-public</em> in the package and module of the
      - *     non-public interfaces. All the non-public interfaces must be in the same
      - *     package and module; otherwise, proxying them is
      - *     <a href="#restrictions">not possible</a>.</li>
      + *     a {@linkplain ##dynamicmodule <em>dynamic module</em>}. The names of the
      + *     package and the module are unspecified.</li>
        * </ol>
        * </li>
        * </ol>

      For the restrictions section:

      @@ -867,12 +867,12 @@ private static Module getDynamicModule(ClassLoader loader) {
            * and those inherited by their superinterfaces
            * must be visible by name through the specified class loader.
            *
      -     * <li>All non-public interfaces must be in the same package
      -     * and module, defined by the specified class loader and
      -     * the module of the non-public interfaces can access all of
      -     * the interface types; otherwise, it would not be possible for
      -     * the proxy class to implement all of the interfaces,
      -     * regardless of what package it is defined in.
      +     * <li>All non-public interfaces and all non-public classes and interfaces
      +     * referenced by all public non-static method signatures must be in the same
      +     * package and module, defined by the specified class loader and can access
      +     * all of the interfaces and the referenced classes and interfaces; otherwise,
      +     * it would not be possible for the proxy class to implement all of the
      +     * interfaces, regardless of what package it is defined in.
            *
            * <li>For any set of member methods of the specified interfaces
            * that have the same signature:

            liach Chen Liang
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

              Created:
              Updated: