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

mandy chung mandy.chung at oracle.com
Mon Apr 16 07:17:26 UTC 2018



On 4/16/18 2:13 PM, David Holmes wrote:
> Hi Mandy,
>
> How do you handle dependencies across a set of auxiliary types, i.e if 
> you are defining a new class A, and it depends on B (which you also 
> intend to define), how will the classloader resolve references from A 
> to B when it can't load B itself?

It'll be the java agent's responsibility to arrange that.  The 
classloader does not find any class for the dynamic generated classes.  
For example, if A is a subclass or subinterface of B (direct or 
indirect), B has to be defined before A or ensure that B is injected in 
the class path and visible to the class loader. This API is specific for 
instrumentation similar to the compiler that sometimes needs to define 
anonymous or bridge classes and they are typically quite straight 
forward.   The existing java agent has been using 
sun.misc.Unsafe::defineClass and so this defineClass form should be 
adequate.

I mention a possible future enhancement to allow the auxiliary classes 
to be defined together with the transformed classes and allowing them to 
go through the transformation pipeline.   It could look into relaxing 
this constraint.

>
> Is the key capability here the ability to inject a class into a 
> package that would otherwise not be open to it?

The public API defining a class to a given runtime package is via 
protected ClassLoader::defineClass method or Lookup::defineClass but 
these methods don't satisfy the java agent use cases to aid an 
instrumentation.  Rafael summarized it quite well in [1] and a brief 
note: ClassLoader::defineClass can be used from a custom class loader 
but it won't be able to define a class in the same runtime package as 
the instrumented class.  Lookup::defineClass requires a Class object as 
the lookup class while Class object is not available when doing 
transformation at load time.

> What are the security implications here?

The java agent has to be enabled explicitly.  To use this API, the java 
agent must obtain the Instrumentation object and add the 
ClassFileTransformer.   The java agent can be started by -javaagent 
option at startup or be put on the classpath such that the java agent 
can be dynamically loaded via the attach mechansim.

Mandy

>
> Thanks,
> David
>
> On 15/04/2018 4:23 PM, mandy chung wrote:
>> 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/
>>
>> Mandy
>> [1] 
>> 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/20180416/3dad80ef/attachment.html>


More information about the serviceability-dev mailing list