# Thursday, 17 October 2013
« Type Confusion PoC for CVE-2013-3131 (MS... | Main | Java Method Overriding Is FUBAR Part 8 o... »
Java Method Overriding Is FUBAR Part 7 of ∞

My friends at Oracle seem determined to make me finish my infinite series of blog posts of Java method overriding.

Before the 7u45 security update the following (pseudo) code ran fine:

class A {
  final void m() { }
}

class B extends A {
  private void m() { }
}

Now with 7u45, loading class B throws an exception:

java.lang.VerifyError: class B overrides final method m.()V

This makes no sense at all and is a misguided attempt to fix the issue I reported here. Ironically, it doesn't even completely fix the issue, because a static finalize method still prevents the final finalizer from running:

class A {
  protected void finalize() {
    System.out.println("A.finalize");
  }
}

class B extends A {
  public static void main(String[] args) {
    new B();
    System.gc();
    System.runFinalization();
  }

  private static void finalize() { }
}

Pre-emptive comment about comments: Feel free to leave comments, but I'm not going to respond to people that clearly don't have a clue.

Update: I misread the spec. The change is actually in line with the spec. Unfortunately the spec is wrong.

Thursday, 17 October 2013 09:33:48 (W. Europe Daylight Time, UTC+02:00)  #    Comments [8]
Thursday, 17 October 2013 23:33:24 (W. Europe Daylight Time, UTC+02:00)
Well, your method is not final in the second example?
Rafael
Friday, 18 October 2013 09:39:28 (W. Europe Daylight Time, UTC+02:00)
I assume that the pseudo-code represents a patched class file, as this code definitely wouldn't compile (JLS 8.4.3.3, 8.4.8.3).

As for the VerifyError: The link you provided (JVMS-5.4.5) clearifies, that the method B.m() indeed overwrites A.m(). And in chapter 4.10 of the JVMS (Verification of class Files), it is defined that the check 'Ensuring that final classes are not subclassed and that final methods are not overridden (§5.4.5).' must be performed.

Therefore I argue that the current behaviour (as of 7u45) is correct.
Michael
Wednesday, 23 October 2013 14:59:36 (W. Europe Daylight Time, UTC+02:00)
@Michael, B.m is not trying overriding it, but merely trying to create a private method local to B. Right?
Joe
Wednesday, 23 October 2013 17:33:43 (W. Europe Daylight Time, UTC+02:00)
@Joe Correct. Private methods don't participate in method overriding in any way. That has always been the case. I don't know how to argue with people who read JVMS-5.4.5 as saying otherwise, they seem to live in an alternate reality where words mean different things.
Monday, 28 October 2013 15:24:51 (W. Europe Standard Time, UTC+01:00)
@Jeroen: Can you provide any information as where in the JVMS it defines, that private methods do not participate in the method overriding check? I clearly cannot see why JVMS-5.4.5 would say so:

* B is a subclass of A -> TRUE
* m2 (which refers to A.m) has the same name and descriptor as m1 (B.m) -> TRUE
* m2 (which refers to A.m) is marked ACC_PUBLIC; or is marked ACC_PROTECTED; or is marked neither ACC_PUBLIC nor ACC_PROTECTED nor ACC_PRIVATE and belongs to the same run-time package as B -> TRUE, its package protected

Michael
Monday, 28 October 2013 15:54:45 (W. Europe Standard Time, UTC+01:00)
The last bullet point you quoted says that B.m2 must be ACC_PUBLIC or ACC_PROTECTED or package accessible and in the same package to override A.m.

Note that this text is very new in the JVM spec, before Java 7 there was nothing the JVMS about method overriding, so for many years people have implemented JVMs by copying the behavior of the reference implementation, which has *never* considered private methods in its overriding algorithm.

The reason the wrong finalize method is called has nothing to do with overriding, it's just the consequence of the incorrect usage of reflection in the native code.
Monday, 28 October 2013 16:30:00 (W. Europe Standard Time, UTC+01:00)
@Jeroen: I think I see our misunderstanding. The JVMS defines m2 as the method in the super-class which will be overridden and m1 as the method in the sub-class that will override it. So m1 will override m2 and not the other way around.

The last bullet point that I quoted says that A.m must be ACC_PUBLIC or ACC_PROTECTED or package accessible and in the same package to allow B.m to override it. It does not imply any access modifiers for B.m.

The finalize call seems to be a different problem and was not what I was referring to. I did _not_ try to argue that a private static method will override a protected instance method.
Michael
Monday, 28 October 2013 17:21:04 (W. Europe Standard Time, UTC+01:00)
Yes, you are correct. I misread the spec. This however changes nothing in my opinion, just that the spec is incorrect.

I do however think I now have a better understanding of how this went wrong. With Java 7 the spec finally got the 5.4.5 section that specifies method overriding, but there was a mistake (it considered private methods as candidates for overriding). Then someone noticed the behavior was out of line with the spec and the behavior was "corrected", instead of the spec.

I'd be willing to entertain the idea that I'm wrong, if it weren't for the fact that the JVM implementation is such a mess (and the spec is unclear too, not to mention that it has always lacked any proper description of how method overriding works).
Name
E-mail
Home page

I apologize for the lameness of this, but the comment spam was driving me nuts. In order to be able to post a comment, you need to answer a simple question. Hopefully this question is easy enough not to annoy serious commenters, but hard enough to keep the spammers away.

Anti-Spam Question: What method on java.lang.System returns an object's original hashcode (i.e. the one that would be returned by java.lang.Object.hashCode() if it wasn't overridden)? (case is significant)

Answer:  
Comment (HTML not allowed)  

Live Comment Preview