matteo rulli
matteo rulli

Reputation: 1493

ByteBuddy rebasing, synthetic types and OSGi

I have the following interceptor developed for byte-buddy:

public class SecurityInterceptor() {

    @RuntimeType
    public static Object intercept(
        @SuperCall Callable<Object> supercall, 
        @This Object target, 
        @Origin Method method, 
        @AllArguments Object[] args) {  

        // Check args and annotations ...       

        Object obj = supercall.call();

        // Post-process obj content ...
    }
}

The interceptor is registered as follows:

Unloaded<Object> unloaded = new ByteBuddy()
    .rebase(type, classFileLocator)
    .method(ElementMatchers.isAnnotatedWith(Secured.class))
    .intercept(MethodDelegation.to(SecurityInterceptor.class))
    .make();
wovenClass.setBytes(unloaded.getBytes());

and this happens inside a WeavingHook in OSGi. The problem is that rebasing with @SuperCall alters the original code as such

public User getUser(final String s) throws Exception {
    return SecurityInterceptor.intercept((Callable)new UsersServiceImpl$auxiliary$xhbBRSr4(this, s), 
        (Object)this, UsersServiceImpl.cachedValue$nlgHrwy3$sn5qca3, new Object[] { s });
}

where UsersServiceImpl$auxiliary$xhbBRSr4 is a synthetic class that is generated by byte-buddy:

class UsersServiceImpl$auxiliary$xhbBRSr4 implements Runnable, Callable
{
    private UsersServiceImpl argument0;
    private String argument1;

    @Override
    public Object call() throws Exception {
        return this.argument0.getUser$original$6ve6X5gN$accessor$nlgHrwy3(this.argument1);
    }

    @Override
    public void run() {
        this.argument0.getUser$original$6ve6X5gN$accessor$nlgHrwy3(this.argument1);
    }

    UsersServiceImpl$auxiliary$xhbBRSr4(final UsersServiceImpl argument0, final String argument2) {
        this.argument0 = argument0;
        this.argument1 = argument2;
    }
}

where UsersServiceImpl is the class being weaved.

So what I need is to add all these synthetic classes in the class space of the UsersServiceImpl's bundle (or in general make synthetic classes "accessible" from that bundle). Is this possible?

Upvotes: 3

Views: 418

Answers (2)

matteo rulli
matteo rulli

Reputation: 1493

In the end I used a different approach:

Unloaded<Object> unloaded = new ByteBuddy()
    .redefine(type, classFileLocator)
    .visit(Advice.to(SecurityAdvice.class)
            .on(ElementMatchers.isAnnotatedWith(Secured.class)))
    .make();

with

public class SecurityAdvice {
    @Advice.OnMethodEnter
    private static void enter(@Advice.AllArguments Object[] args) {
        //...
    }

    @Advice.OnMethodExit
    private static void exit(@Advice.Return(typing = Typing.DYNAMIC) Object value) {
       //...
    }
}

this only alters the original class's bytecode without introducing additional synthetic types.

Upvotes: 1

Rafael Winterhalter
Rafael Winterhalter

Reputation: 44077

One trick you can apply is to not use rebasing but to create your own class loaders during the loading of a proxy where the new class (and all of its synthetic classes) are loaded in a class loader with multiple parents:

dynamicType
  .make()
  .load(new MultipleParentClassLoader.Builder(false)
    .append(type.getClassLoader(), SecurityInterceptor.class.getClassLoader())
    .build());

The proxy class will now be loaded in a new class loader that has multiple parents; the OSGi loader that shields any other types not normally known to a bundle and the security interceptor's class loader that is yours. The multiple parent class loader will however request any type from both class loaders, thus making both visible for the proxy class.

Notice the false argument to the builder's constructor. This unseals the class loader making it potentially vulnerable for injection of additional classes. I don't assume that your proxy classes are sensitive to reflection attacks but it is something to consider. You can remove the flag and pay slightly higher costs for class generation.

Upvotes: 0

Related Questions