byte-buddy icon indicating copy to clipboard operation
byte-buddy copied to clipboard

[Question] AgentBuilder.InitializationStrategy.SelfInjection's behaviour in detail

Open ganschen opened this issue 1 year ago • 2 comments

Background

I wrote a ThreadSubclassInterceptor to transform Thread's Subclass.

class ThreadSubclassInterceptor {
    public AgentBuilder install(AgentBuilder agentBuilder) {
        return agentBuilder
                .type(hasSuperType(named("java.lang.Thread"))
                        .and(not(named("java.lang.Thread").or(nameStartsWith("java.lang.ref")))))
                .transform((builder, typeDescription, classLoader, module) -> {
                    ...
                    return builder;
                });
    }

I ran the JDK test and exception thrown.

java.lang.ExceptionInInitializerError
    at java.base/java.lang.Class.forName0(Native Method)
    at java.base/java.lang.Class.forName(Class.java:467)
    at Security.setupProxy(Security.java:172)
    at Security.main(Security.java:447)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at java.base/java.lang.Thread.run(Thread.java:840)
Caused by: java.security.AccessControlException: access denied ("java.lang.RuntimePermission" "getClassLoader")
    at java.base/java.security.AccessControlContext.checkPermission(AccessControlContext.java:485)
    at java.base/java.security.AccessController.checkPermission(AccessController.java:1068)
    at java.base/java.lang.SecurityManager.checkPermission(SecurityManager.java:416)
    at java.base/java.lang.ClassLoader.checkClassLoaderPermission(ClassLoader.java:2060)
    at java.base/java.lang.ClassLoader.getSystemClassLoader(ClassLoader.java:1948)
    at ProxyServer.<clinit>(ProxyServer.java:56)
    ... 11 more

I found that it's because ProxyServer extends thread class so it will be instrumented, and because of AgentBuilder's default strategy, code below will be added.

    static {
  ClassLoader.getSystemClassLoader().loadClass("net.bytebuddy.dynamic.Nexus").getMethod("initialize", Class.class, Integer.TYPE).invoke((Object)null, ProxyServer.class, -429884501);
        ...
    }

While in the policy of the test, there is no getClassLoader permission, so the exception will be thrown.

I tried to change the strategy to InitializationStrategy.NoOp and it worked but still not sure if it's safe to do so.

Question

May i know In which case it's safe to change the InitializationStrategy to NoOp or Minimal? I tried to understand InitializationStrategy.SelfInjection's behaviour by reading the source code, looks like what it's trying to do is to call the LoadedTypeInitializer registered in the instrumented type and it's auxiliary types, but it's still not very clear to me what does those LoadedTypeInitializers do, can i get some explanation is detail or some examples? Thank you so much.

ganschen avatar Mar 22 '24 22:03 ganschen

If any of your instrumentations requires dispatch to an object, it is required. But normally it is not. I will see if I can change it to detect this automatically. But unless you do something like MethodDelegation.to(new MyDispatcher()), it should be safe.

raphw avatar Mar 25 '24 19:03 raphw

Thank you for your explanation!

ganschen avatar Mar 25 '24 21:03 ganschen