≡ Menu

Bypassing subclass method overrides

Someone in ##java recently asked a question that may come up with beginners to OOP: How to invoke a method of a superclass from the outside. For example, if ChildClass extends BaseClass and overrides BaseClass.method(), how can one invoke the method of the superclass, bypassing the override in ChildClass?

This is not a very good idea from an OOP standpoint, because the child class might not expect the parent’s behavior to be invoked, but it’s still a very interesting question. It turns out there actually is a way to do this, too.

We will work with the following class setup:

public class Test {
  public static class BaseClass {
    public String method() {
      return "BaseClass.method()";
    }
  }

  public static class ChildClass extends BaseClass {
    @Override
    public String method() {
      return "ChildClass.method()";
    }
  }
}

In Java bytecode, normal (not static, not an interface) method calls are made via the invokevirtual instruction. (As an example, invokevirtual is used when doing a simple obj.method() call.) However, this obviously will not work for super.method() instructions in code – the overridden method, not the superclass’ method, would be called. For this purpose, the JVM has another invoke instruction called invokespecial: it is used to invoke an instance method of an exact class, without respecting overridden method definitions.

Sadly, the verifier complains when we try to do load a class that does this; it throws a VerifyError with the message Illegal use of nonvirtual function call. The invokespecial instruction on a method can only be used from a direct subclass or the class itself, in the places where you would expect super.method() to work (inner classes use a bridge method). It’s probably better this way, too – if this was possible without some security checks, this could probably be easily exploited.

Method handles to the rescue! With the introduction of the MethodHandles API in Java 7, we have all sorts of nifty ways to bypass such measures through a bit of reflection. This API is also protected by access checks – here throwing IllegalAccessExceptions when we try to create our invokespecial handle.

Editor’s note: Java 7 has been end-of-lifed as of this writing – you should be using Java 8 by now, unless you have specific requirements holding you back to an older version of Java.

This is fairly easy to bypass by using normal reflection to create an instance of MethodHandles.Lookup that has a “Lookup Class”, meaning the permissions of a class, that is in fact allowed to invokespecial our target method BaseClass.method(). There are two candidates for this: the direct subclass of BaseClass, in our example ChildClass (for those super.method() calls mentioned above), and BaseClass itself (for some constructor business). For convenience we will use BaseClass as getting the direct child class requires a few more lines of code:

Constructor<Methodhandles.Lookup> methodHandlesLookupConstructor =
  MethodHandles.Lookup.class.getDeclaredConstructor(Class.class);
methodHandlesLookupConstructor.setAccessible(true);
MethodHandles.Lookup lookup = methodHandlesLookupConstructor.newInstance(BaseClass.class);

Now the fun begins! We can use MethodHandles.Lookup.findSpecial() to create a MethodHandle that points towards our target method. We don’t need to worry about access checks here due to the constructor code above:

MethodHandle handle = lookup.findSpecial(
  BaseClass.class, "method", MethodType.methodType(String.class), BaseClass.class);

Done! Working example:

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Constructor;

public class Test {
  public static void main(String[] args) throws Throwable {
    ChildClass obj = new ChildClass();
    System.out.println(obj.method()); // prints ChildClass.method()
    System.out.println(invokeSpecial(obj)); // prints BaseClass.method()
  }

  static String invokeSpecial(BaseClass obj) throws Throwable {
    // create the lookup
    Constructor<MethodHandles.Lookup> methodHandlesLookupConstructor =
      MethodHandles.Lookup.class.getDeclaredConstructor(Class.class);
    methodHandlesLookupConstructor.setAccessible(true);
    MethodHandles.Lookup lookup = methodHandlesLookupConstructor.newInstance(BaseClass.class);
    // create the method handle
    MethodHandle handle = lookup.findSpecial(
      BaseClass.class, "method", MethodType.methodType(String.class), BaseClass.class);
    return (String) handle.invokeWithArguments(obj);
  }

  public static class BaseClass {
    public String method() {
      return "BaseClass.method()";
    }
  }

  public static class ChildClass extends BaseClass {
    @Override
    public String method() {
      return "ChildClass.method()";
    }
  }
}

Comments on this entry are closed.