Review Request JDK-8200559: Java agents doing instrumentation need a means to define auxiliary classes

mandy chung mandy.chung at oracle.com
Wed Apr 18 23:59:47 UTC 2018


https://bugs.openjdk.java.net/browse/JDK-8201784

This is the correct JBS issue (Sorry I cut-n-paste the wrong link).

Mandy

On 4/18/18 3:46 PM, mandy chung wrote:
> Hi Rafael,
>
> I think it's best to separate the testing requirement from java agents 
> doing instrumentation that may run in production environment.
>
> I have created a JBS issue to track the testing mode idea that would 
> require more discussion and investigation:
> https://bugs.openjdk.java.net/browse/JDK-8201562
>
> I understand it's not as efficient to inject a class in a package 
> different than the package of the transformed class. With the 
> principle of least privilege, I prefer not to provide an API to inject 
> any class in any package and you can achieve by calling 
> retransformClasses.
>
> What do you think?
>
> Mandy
>
> On 4/18/18 5:23 AM, Rafael Winterhalter wrote:
>> Hei Mandy,
>> Lookup::defineClass would always be an alternative but it would 
>> require me to open the class first. If the instrumented type can read 
>> the module with the callback but its module was not opened, this 
>> would not help me much, unfortunately. Also, I could not resolve this 
>> lookup as the class in question is not necessarily loaded at this point.
>> Best regards, Rafael
>>
>> 2018-04-17 9:28 GMT+02:00 mandy chung <mandy.chung at oracle.com 
>> <mailto:mandy.chung at oracle.com>>:
>>
>>     Hi Rafael,
>>
>>     I see that mocking/proxying/testing framework should be looked at
>>     separately since its requirements and approaches can be different
>>     than tool agents.
>>
>>     On 4/17/18 5:06 AM, Rafael Winterhalter wrote:
>>>     Hei Mandy,
>>>
>>>     I have looked into several Java agents that I have worked on and
>>>     for many of them, this API does unfortunately not supply
>>>     sufficient access. I would therefore still prefer a method
>>>     Instrumentation::defineClass.
>>>
>>>     The problem is that some agents need to define classes in other
>>>     packages then in that of the instrumented class. For example, I
>>>     might need to enhance a library that defines a set of callback
>>>     classes in package A. All these classes share a common super
>>>     class with a package-private constructor. I want to instrument
>>>     some class in package B to use a callback that the library does
>>>     not supply and need to add a new callback class to A. This is
>>>     not possible using the current API.
>>>
>>
>>     Are these callback classes made available statically?  or just
>>     dynamically defining additional class as needed?  Is
>>     Lookup::defineClass an alternative if you get a hold of common
>>     super class in A?
>>
>>>     I could however achieve do so by calling
>>>     Instrumentation::retransform on one of the classes in A after
>>>     registering a class file transformer. Once the retransformation
>>>     is triggered, I can now define a class in A. Of course this is
>>>     inefficient and I would rather open the jdk.internal.misc module
>>>     and use the "old" API instead.
>>>
>>>     For this reason, I argue that this rather restrained API is not
>>>     convenient while it does not add anything to security. Also, for
>>>     the use case of Mockito, this would neither be sufficient as
>>>     Mockito sometimes redefines classes and sometimes adds a
>>>     subclass without retransforming. We would rather have direct
>>>     access to class definition once we are already running with the
>>>     privileges of a Java agent.
>>>
>>>     I would therefore suggest to add a method:
>>>
>>>     interface Instrumentation {
>>>       Class<?> defineClass(byte[] bytes, ProtectionDomain pd);
>>>     }
>>>
>>>     which can be implemented simply by delegating to
>>>     jdk.internal.misc.Unsafe.
>>>
>>>     On a side note. Does JavaLangAccess::defineClass work with the
>>>     bootstrap class loader? I have not tried it but I always thought
>>>     it was just an access layer for the class loader API that cannot
>>>     access the null value.
>>>
>>
>>     The JVM entry point does allow null loader.
>>
>>     Mandy
>>
>>
>>>     Thanks for considering this use case!
>>>     Best regards, Rafael
>>>
>>>     2018-04-15 8:23 GMT+02:00 mandy chung <mandy.chung at oracle.com
>>>     <mailto:mandy.chung at oracle.com>>:
>>>
>>>         Background:
>>>
>>>         Java agents support both load time and dynamic
>>>         instrumentation.   At load time,
>>>         the agent's ClassFileTransformer is invoked to transform
>>>         class bytes.  There is
>>>         no Class objects at this time.  Dynamic instrumentation is
>>>         when redefineClasses
>>>         or retransformClasses is used to redefine an existing loaded
>>>         class.  The
>>>         ClassFileTransformer is invoked with class bytes where the
>>>         Class object is present.
>>>
>>>         Java agent doing instrumentation needs a means to define
>>>         auxiliary classes
>>>         that are visible and accessible to the instrumented class. 
>>>         Existing agents
>>>         have been using sun.misc.Unsafe::defineClass to define aux
>>>         classes directly
>>>         or accessing protected ClassLoader::defineClass method with
>>>         setAccessible to
>>>         suppress the language access check (see [1] where this issue
>>>         was brought up).
>>>
>>>         Instrumentation::appendToBootstrapClassLoaderSearch and
>>>         appendToSystemClassLoaderSearch
>>>         APIs are existing means to supply additional classes.  It's
>>>         too limited
>>>         for example it can't inject a class in the same runtime
>>>         package as the class
>>>         being transformed.
>>>
>>>         Proposal:
>>>
>>>         This proposes to add a new ClassFileTransformer.transform
>>>         method taking additional ClassDefiner parameter.  A
>>>         transformer can define additional
>>>         classes during the transformation process, i.e.
>>>         when ClassFileTransformer::transform is invoked. Some details:
>>>
>>>         1. ClassDefiner::defineClass defines a class in the same
>>>         runtime package
>>>            as the class being transformed.
>>>         2. The class is defined in the same thread as the
>>>         transformers are being
>>>            invoked.   ClassDefiner::defineClass returns Class object
>>>         directly
>>>            before the transformed class is defined.
>>>         3. No transformation is applied to classes defined by
>>>         ClassDefiner::defineClass.
>>>
>>>         The first prototype we did is to collect the auxiliary
>>>         classes and define
>>>         them  until all transformers are invoked and have these aux
>>>         classes to go
>>>         through the transformation pipeline. Several complicated
>>>         issues would
>>>         need to be resolved for example timing whether the auxiliary
>>>         classes should
>>>         be defined before the transformed class (otherwise a
>>>         potential race where
>>>         some other thread references the transformed class and cause
>>>         the code to
>>>         execute that in turn reference the auxiliary classes.  The
>>>         current
>>>         implementation has a native reentrancy check that ensure one
>>>         class is being
>>>         transformed to avoid potential circularity issues.  This may
>>>         need JVM TI
>>>         support to be reliable.
>>>
>>>         This proposal would allow java agents to migrate from
>>>         internal API and ClassDefiner to be enhanced in the future.
>>>
>>>         Webrev:
>>>         http://cr.openjdk.java.net/~mchung/jdk11/webrevs/8200559/webrev.00/
>>>         <http://cr.openjdk.java.net/%7Emchung/jdk11/webrevs/8200559/webrev.00/>
>>>
>>>         Mandy
>>>         [1]
>>>         http://mail.openjdk.java.net/pipermail/jdk-dev/2018-January/000405.html
>>>         <http://mail.openjdk.java.net/pipermail/jdk-dev/2018-January/000405.html>
>>>
>>>
>>
>>
>

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.openjdk.java.net/pipermail/serviceability-dev/attachments/20180419/1d4455e0/attachment-0001.html>


More information about the serviceability-dev mailing list