Date: Wed, 4 Jun 1997 13:02:48 -0700 (PDT)
From: Sean McDirmid <mcdirmid@cs.washington.edu>
To: Marianne Mueller <mrm@Eng>
Subject: Re: New(?) bytecode verification weakness
In-Reply-To: <199706032126.OAA22589@puffin.eng.sun.com>
Hi Marianne,
I am trying to understand how this is allowed in the JVM specification.
The only information I can find in the book is on Page 102 in table 4.3,
under the listing of ACC_FINAL, "Is final; no further overriding or
assignment after initialization."
So our bytecode verifier does the following when dealing with final
fields:
Given a final field and a putfield or putstatic instruction:
If the field is not static (that is a non static field and a putfield
instruction):
(1) The field reference must be of the current class being verified.
(2) The current method must be <init>
(3) The reference (or target of the putfield) must be "this" (that is the
reference that is thrown into register 0 and the target of initialization
in the <init> function.)
If the field is static (that is a static field and a putstatic
instruction):
(1) The field reference must be of the current class being verified.
(2) The current method must be <clinit>
Are we being too conservative that we might actually reject valid class
files that assign to final fields out of order?
Also besides introducing some security questions, this also brings out
some interesting optimization problems. If for example I have a singleton
class, a class which is garunteed to only have one instance, I may want to
optimize compiled code for that instance alone (since only one instance
can exist). After <init> is called, I will compile the class to optimize
according to the types of the final instance variables. Let's say that
this an instance of a compiler class which can take an instruction set
architecture (say X86 or Sparc) as an argument to the constructer so that
the final instance variable ISA can be assigned to at <init>.
(1) If I assume final variables will not change after initialization, I
can go ahead and inline in compiled code any method on the ISA instance
variable. This might lead to really good performance gains.
(2) If I assume final variables can change after initialization, I can
make no such optimizations and I have to treat each final variable to a
reference as an ordinary reference.
Thank you for your time,
Sean
On Tue, 3 Jun 1997, Marianne Mueller
wrote:
>
> Hi Bill,
>
> (Actually since you're about two blocks away, we should meet for
> coffee at Starbuck's!)
>
> Anyway, this is a known feature or a known bug, depending on many
> interesting debates that people have about how the Java keyword
> "final" is defined and enforced.
>
> Your example shows that a class is able to assign to its own final
> variable more than once. (at the bytecode level)
>
> The compiler performs checks to make sure that a final field is
> assigned to at most once, but, the VM doesn't perform such checks.
>
> I don't feel this is a security hole, since if a malicious class can
> launch an attack by assigning to one of its own final fields, what's
> preventing the same class file from declaring the field as non-final
> in the first place?
>
> We do not allow a class to modify final fields in other classes.
>
> This does lead to the interesting discussion that the Princeton people
> have raised, about how best to define the distribution layer for Java.
>
> Marianne
>
> p.s. Here's my version of your test, and also the jasm version of the
> bytecode. We have jasm and jdis, a couple interesting tools. jdis
> takes as input a .class file and produces a .jasm file. jasm takes a
> .jasm file and produces a .class file.
>
> --
>
> import examples.FinalTest;
>
> public class Test {
> public static void main(String[] args) {
>
> System.out.println("foo = " + FinalTest.foo);
> FinalTest.putFoo(2);
> System.out.println("foo = " + FinalTest.foo);
> }
> }
>
>
>
>
>
>
> package examples;
>
> public super class FinalTest {
> // Compiled from FinalTest.java
> // Compiler version 3.45;
>
> final public static Field foo:"I";
>
> public static Method putFoo:"(I)V"
> stack 1 locals 1
> {
> iload_0;
> putstatic Field foo:"I";
> return;
> }
>
> public Method <init>:"()V"
> stack 1 locals 1
> {
> aload_0;
> invokespecial Method java/lang/Object.<init>:"()V";
> return;
> }
>
> static Method <clinit>:"()V"
> stack 1 locals 0
> {
> iconst_1;
> putstatic Field foo:"I";
> return;
> }
>
> } // end Class FinalTest
>