Search code examples
javabytecodeinstrumentationjavaagentsbyte-buddy

Using Java agent (Byte Buddy) to transform existing field from private to public


I'm trying to efficiently access the cpath field from FilePermission inside my application code. Mind you this should be able to be done with a security manager in place, so if possible I don't want to resort to calling setAccessible.

I'm already using a Byte Buddy powered agent using AgentBuilder. Here's what the AgentBuilder does:

public static void premain(String arg, Instrumentation inst) {
    install(arg, inst);
}

public static void agentmain(String arg, Instrumentation inst) {
    install(arg, inst);
}

private static void install(String arg, Instrumentation inst) {
    Transformer filePermissionTransformer = (builder, typeDescription, classLoader, module) ->
        builder.field(named("cpath")).transform(ForField.withModifiers(Visibility.PUBLIC));

    new AgentBuilder.Default()
        .with(new ByteBuddy().with(Implementation.Context.Disabled.Factory.INSTANCE))
        .with(Listener.StreamWriting.toSystemOut())
        .with(InitializationStrategy.NoOp.INSTANCE)
        .with(RedefinitionStrategy.REDEFINITION)
        .with(TypeStrategy.Default.REDEFINE)
        .ignore(none())
        .type(named("java.io.FilePermission"))
        .transform(filePermissionTransformer)
        .installOn(inst);
}

I can see with the sysout listener that it is indeed being transformed:

[Byte Buddy] DISCOVERY java.io.FilePermission [null, null, loaded=true]
[Byte Buddy] TRANSFORM java.io.FilePermission [null, null, loaded=true]
[Byte Buddy] COMPLETE java.io.FilePermission [null, null, loaded=true]

Then I attempt to get the field in the application:

if (perm instanceof FilePermission) {
        Field cpathField = perm.getClass().getDeclaredField("cpath");
        String cpath = (String) cpathField.get(perm);
}

But that results in an IllegalAccessException, with a cause that tells me it's still "private transient".

Just for kicks, I tried .annotateField instead of .transform with a Deprecated annotation. That did indeed work, and at runtime I can retrieve the annotation from the declared field. So that at least proves that the path to field transformation is working... just not this particular transformation for some reason.

Just for background, no this isn't the only reason I'm using Byte Buddy... I'm also using it to redefine some other things. I could calculate cpath myself referencing the OpenJDK code, but I'd like this to be as efficient as possible... and since FilePermission is already doing the work internally I'd rather grab the value than do the work twice. Since I am already using instrumentation for other things, this seemed like a more elegant solution.

Cheers!


Solution

  • If you run the program with the property -Dnet.bytebuddy.dump set to some folder, Byte Buddy will extract the produced class files and write them to the specified folder. If you use javap to investigate the created file for FilePermission, you get:

    Compiled from "FilePermission.java"
    public final class java.io.FilePermission extends java.security.Permission implements java.io.Serializable {
      public transient java.lang.String cpath;
      public java.io.FilePermission(java.lang.String, java.lang.String);
      java.io.FilePermission(java.lang.String, int);
      public boolean implies(java.security.Permission);
      boolean impliesIgnoreMask(java.io.FilePermission);
      public boolean equals(java.lang.Object);
      public int hashCode();
      int getMask();
      public java.lang.String getActions();
      public java.security.PermissionCollection newPermissionCollection();
      static java.lang.String access$000(java.io.FilePermission);
    }
    

    As you can see, the cpath field was rendered public but the JVM does not seem to care about this. If you define a class with the same cpath field and run the transformation above, this will work.

    I can only suspect that the JVM hardcodes this property somewhere. You can ask on the JVM mailing list why this is the case, I can only guess.