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 07:46:37 UTC 2018


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/20180418/1537bffc/attachment-0001.html>


More information about the serviceability-dev mailing list