Review Request JDK-8202113: Reflection API is causing caller classes to leak
mandy chung
mandy.chung at oracle.com
Sun Apr 29 08:02:47 UTC 2018
On 4/28/18 6:52 PM, Peter Levart wrote:
> Hi Mandy,
>
> On 04/28/18 11:44, mandy chung wrote:
>> Webrev:
>> http://cr.openjdk.java.net/~mchung/jdk11/webrevs/8202113/webrev.00/
>>
>> The reflection machinery stores the caller class in each
>> AccessibleObject
>> such that it can skip the access check if access to a member has been
>> verified for a given caller. At the first time accessing the
>> AccessibleObject,
>> it creates an Accessor object and then cache for subsequent use.
>> This cached
>> Accessor object keeps a reference to the AccessibleObject object that
>> will keep the caller class alive.
>
> ...because the same instance of Accessor object is also set on the
> root AccessibleObject in order to be shared among other child
> AccessibleObject instances created from it and because this root
> AccessibleObject is retained by the cache of AccessibleObject(s) in
> the declaring j.l.Class...
>
Yes
>>
>> The implementation has a root object for each AccessibleObject and
>> the API returns a child object for users to access (that may suppress
>> access check via setAccessible). The caller class is set in the cache
>> of the child object. This patch proposes to change ReflectionFactory
>> newXXXAccessor methods to ensure to pass the root object rather than
>> the child object. The cache of the root object is always null.
>
> ...since root AccessibleObject(s) are never used for accessing the
> member(s), they will never cache the caller Class and so they can be
> retained by the Class-level cache together with cached Accessors that
> now just point back to them and never to AccessibleObject(s) handed
> out to users that do cache caller Class.
>
Exactly.
> Right, this patch fixes the issue of retaining (leaking) the caller
> class when using AccessibleObject(s) returned from Reflection API and
> then throwing them away.
>
> There might be a separate issue when user-handed AccessibleObjects are
> cached by user code and such cache is located in a different class
> loader than classes that make invocations. This could only be solved
> with a WeakReference in the access-check cache I suppose...
>
User code caching the AccessibleObject should be for its own use and I
would think the cached caller would be itself.
> But this patch is good anyway, because it ensures Accessor(s) only
> retain root AccessibleObjects which by itself is more memory-friendly.
>
> About the tests:
>
> - There seems to be some strange ununiform formatting in AccessTest.
>
> - For public and protected members, the test looks good, but for
> private members, I don't think it is doing anything useful, because if
> access-check fails, the caller class is not cached (from
> AccessibleObject):
>
Right.
> private boolean slowVerifyAccess(Class<?> caller, Class<?> memberClass,
> Class<?> targetClass, int modifiers)
> {
> if (!Reflection.verifyMemberAccess(caller, memberClass,
> targetClass, modifiers)) {
> // access denied
> return false;
> }
> ...
> ...
> securityCheckCache = cache; // write volatile
> return true;
> }
>
>
> But for private members, there's no issue of leaking caller class(es)
> because the only successfully caller is the declaring class itself.
including the private method/field is solely for completeness (still
partial since it does not cover the constructor). I can add a comment
to mention that it won't cache caller for such access failing case.
Mandy
More information about the core-libs-dev
mailing list