RFR: 8159746: (proxy) Support for default methods

Mandy Chung mchung at openjdk.java.net
Fri Oct 16 18:18:12 UTC 2020


On Fri, 16 Oct 2020 14:46:59 GMT, Peter Levart <plevart at openjdk.org> wrote:

>> Hi Peter,
>> 
>> The current `newProxyInstance` API does not perform access check against the proxy interfaces.    I want to avoid
>> adding new caller-sensitive method where possible.   I did consider any alternative to make it non-static method which
>> only allows a proxy's invocation handler to call `invokeDefaultMethod` if `proxy.getInvocationHandler() == this`.  If
>> access check done at invocation time, `invokeDefaultMethod` still needs to be caller-sensitive which must be a final
>> method (can't be on a default method)  In addition, an invocation handler to call `invokeDefaultMethod` can't be
>> implemented in a lambda because it can't self-reference itself.  So the invocation handler that wants to invoke default
>> methods must be a concrete or anonymous class which is a limitation.    Alan and I also discussed other alternative
>> earlier.   I agree to prototype a slightly different alternative that will perform access check at proxy instance
>> creation time: 1. define an abstract `DelegatingInvocationHandler` class that defines a protected final `invokeDefault`
>> method (shortened method name). 2. `Proxy::newProxyClass` will perform access check if the given `InvocationHandler` is
>> an instance of `DelegatingInvocationHandler` 3. `Proxy(InvocationHandler h)` constructor should throw an exception if
>> the given `h` is an instance of `DelegatingInvocationHandler`.  `Proxy::getProxyClass` is deprecated and it's
>> recommended to call `newProxyInstance` instead.  It's strongly discouraged to invoke the Proxy constructor and so I
>> think not supporting `DelegatingInvocationHandler` may be reasonable. 4. `Proxy::getInvocationHandler` needs to check
>> if the caller class has access to the proxy interfaces if it's a `DelegatingInvocationHandler`.  So one can't get a
>> proxy instance whose invocation handler can invoke default methods if the caller has no access to the proxy interfaces;
>> similarly one can't get a `DelegatingInvocationHandler` from a proxy instance if the caller has no access.  This
>> alternative avoids `invokeDefault` being caller-sensitive but the limitation of `DelegatingInvocationHandler`
>> implementation in a concrete/anonymous class.  Thought/feedback?
>
> Hi Mandy,
> 
> I read your concerns and have an alternative API proposition here. Instread of an abstract class with protected final
> `invokeDefaultMethod` method, we can keep the public static `invokeDefaultMethod` and add a `Lookup` parameter to it.
> The Lookup instance to be passed to the method should be able to `findSpecial` the default method - it has to be the
> full privilege lookup with proxy class as lookup class. Such instance is provided in a call to the alternative
> invocation handler: `InvocationHandlerWithLookup` which is a super-interface of plain `InvocationHandler` that just
> forwards the invocation with the additional Lookup parameter to the abstract method without parameter (in order to keep
> backwards-compatibility). The generated proxy class invokes this new method in new interface and passes its own lookup
> to it. There's also an overloaded Proxy::newProxyInstance method that takes this new InvocationHandlerWithLookup
> parameter instead of plain InvocationHandler. This allows specifying lambdas for both overloaded methods and the right
> one is selected according to the number of lambda parameters. invoking the constructor of the proxy class taking plain
> InvocationHandler is still allowed but only if the handler class does not use the Lookup parameter. The overloaded
> Proxy.newProxyInstance throws IllegalAccessException in case the passed-in handler uses the provided lookup but some
> interfaces of the proxy are not accessible.  Performance with this implementation is restored and is not worse when
> dealing with inaccessible interfaces:  Mandy's original:  Benchmark             Mode  Cnt    Score    Error  Units
> ProxyBench.implClass  avgt    5    3.709 ±  0.026  ns/op ProxyBench.implProxy  avgt    5  926.215 ± 11.835  ns/op
> Peter's performance patch:  Benchmark             Mode  Cnt   Score   Error  Units ProxyBench.implClass  avgt    5
> 3.777 ± 0.005  ns/op ProxyBench.implProxy  avgt    5  27.579 ± 0.250  ns/op
> 
> Moved to InvocationHandler + added access check
> 
> Benchmark               Mode  Cnt    Score   Error  Units
> ProxyBench.implClass    avgt    5    3.740 ± 0.004  ns/op
> ProxyBench.implProxy    avgt    5   34.226 ± 0.125  ns/op
> ProxyBench.ppImplClass  avgt    5    3.780 ± 0.004  ns/op
> ProxyBench.ppImplProxy  avgt    5  147.318 ± 1.422  ns/op
> 
> 
> Alternative API with access check in newProxyInstance
> 
> Benchmark               Mode  Cnt   Score   Error  Units
> ProxyBench.implClass    avgt    5   3.782 ± 0.013  ns/op
> ProxyBench.implProxy    avgt    5  32.493 ± 0.192  ns/op
> ProxyBench.ppImplClass  avgt    5   3.749 ± 0.002  ns/op
> ProxyBench.ppImplProxy  avgt    5  30.565 ± 0.190  ns/op
> 
> The code is in this pull request against your last commit in your branch: https://github.com/mlchung/jdk/pull/2

Hi Peter,

This seems an attracting idea to keep the possibility of using lambdas.  However, the full privileged lookup of the
proxy class will be leaked to `InvocationHandlerWithLookup` implementation which imposes security concerns.   This
full-privileged lookup has access to other proxy classes in that module including private members that we don't want to
hand out to user code.

Other than this security concern, we will also need to consider the inconsistency having `java.lang.reflect` API to
reference `Lookup`.

-------------

PR: https://git.openjdk.java.net/jdk/pull/313


More information about the core-libs-dev mailing list