RFR: JDK-8242888: Convert dynamic proxy to hidden classes
Johannes Kuhn
jkuhn at openjdk.java.net
Tue Apr 19 17:08:28 UTC 2022
On Sun, 17 Apr 2022 16:17:30 GMT, liach <duke at openjdk.java.net> wrote:
> Convert dynamic proxies to hidden classes. Modifies the serialization of proxies (requires change in "Java Object Serialization Specification"). Makes the proxies hidden in stack traces. Removes duplicate logic in proxy building.
>
> The main compatibility changes and their rationales are:
> 1. Modification to the serialization specification: In the "An instance of the class is allocated... The contents restored appropriately" section, I propose explicitly state that handling of proxies are unspecified as to allow implementation freedom, though I've seen deliberate attempts for proxies to implement interfaces with `readResolve` in order to control their serialization behavior.
> - This is for the existing generated constructor accessor is bytecode-based, which cannot refer to hidden classes.
> - An alternative is to preserve the behavior, where the serialization constructor calls `invokespecial` on the closest serializable superclass' no-arg constructor, like in #1830 by @DasBrain.
> - My rationale against preservation is such super calls are unsafe and should be discouraged in the long term. Calling the existing constructor with a dummy argument, as in my implementation, would be more safe.
> 2. The disappearance of proxies in stack traces.
> - Same behavior exists in lambda expressions: For instance, in `((Runnable) () -> { throw new Error(); }).run();`, the `run` method, implemented by the lambda, will not appear in the stack trace, and isn't too problematic.
>
> A summary of the rest of the changes:
> 1. Merged the two passes of determining module and package of the proxy into one. This reduced a lot of code and allowed anchor class (for hidden class creation) selection be done together as well.
> 2. Exposed internal API for obtaining a full-privileged lookup to the rest of `java.base`. This API is intended for implementation of legacy (pre `MethodHandles.Lookup`) caller sensitive public APIs so they don't need more complex tricks to obtain proper permissions as lookups.
> 3. Implements [8229959](https://bugs.openjdk.java.net/browse/JDK-8229959): passes methods computed by proxy generator as class data to the hidden proxy class to reduce generated proxy class size and improve performance.
>
> In addition, since this change is somewhat large, should we keep the old proxy generator as well and have it toggled through a command-line flag (like the old v49 proxy generator or the old reflection implementation)?
>
> Please feel free to comment or review. This change definitely requires a CSR, but I have yet to determine what specifications should be changed.
About deserializing Proxies, this is currently done in 3 steps:
1. Finding the class
2. Allocating a new instance and invoke the constructor of the **first non-serializable superclass**
3. Setting the fields of the instance
Only step 2 is problematic here:
1. For a proxy, the class is serialized using a TC_PROXYCLASSDESC. when deserialized it invokes `Proxy.getProxyClass` (https://github.com/openjdk/jdk/blob/13fb1eed52f1a9152242119969a9d4a0c0627513/src/java.base/share/classes/java/io/ObjectInputStream.java#L891).
For this step, it doesn't matter if `Proxy.getProxyClass` returns a normal class or a hidden class.
2. Allocating and calling the constructor:
This part is currently implemented by spinning a class. The generated bytecode looks more or less like this:
anew ProxyClass
invokespecial Object.<init>()
The big lie here is that methods and constructors are different - but constructors are just methods, and the verifier makes sure that a constructor is called only once, exactly once, and that uninitialized instances don't escape.
This doesn't work for hidden classes - as the hidden class can not be named in an other class.
But MethodHandles can do this. Here is one of my old prototypes, based on a prototype for implementing reflection using MethodHandles from Mandy Chung: https://github.com/dasbrain/jdk/compare/ae45c5de7fc635df4dc86b764405158c245d2765...fbb0a63436f696a85e7039c0e109c379dfa1edce
3. Setting the fields uses deep reflection.
But the hidden class themself doesn't have any fields - just it's superclass `java.lang.reflect.Proxy.h`.
This is not a problem if the class can be instantiated at all (see step 2).
-------------
PR: https://git.openjdk.java.net/jdk/pull/8278
More information about the security-dev
mailing list