[9] RFR (S): 8157181: Compilers accept modification of final fields outside initializer methods

Vladimir Ivanov vladimir.x.ivanov at oracle.com
Thu Jun 2 10:47:09 UTC 2016


Zoltan,

(broadening the audience to hotspot-dev@)

I'd like to draw attention from Runtime group to this change.
> OK, so I have implemented a generic fix that adds the checks required by
> the JVMS.
>
> For bytecodes modifying final fields, the VM checks
> - that the accessing method is <clinit> (for static fields)
> - that the accessing method is <init> (for instance fields)
> and thrown an IllegalAccessError if any of the checks fails.
>
> Here is the new webrev:
> http://cr.openjdk.java.net/~zmajo/8157181/webrev.02/

Regarding tightening linkage checks, your fix doesn't take into account 
JVMS changes between 6 & 7 (expected behavior differs depending on class 
file version).

Before JVMS-7, where putfield/putstatic linkage checks were tightened, 
it was allowed to change final fields from anywhere in the same class.

putfield "Linking Exceptions":

   JVMS-6 [1]:
     "Otherwise, if the field is final, it must be declared in the 
current class. Otherwise, an IllegalAccessError is thrown."

   JVMS-7 [2]:
     "Otherwise, if the field is final, it must be declared in the 
current class, and the instruction must occur in an instance 
initialization method (<init>) of the current class. Otherwise, an 
IllegalAccessError is thrown."


src/share/vm/interpreter/linkResolver.cpp:

+         (byte == Bytecodes::_putstatic && fd.is_static() && 
method_name != vmSymbols::class_initializer_name()) ||
+         ((byte == Bytecodes::_putfield || byte == 
Bytecodes::_nofast_putfield) && !fd.is_static() && method_name != 
vmSymbols::object_initializer_name())


Also, as we found earlier, checking method name is not enough:

bool Method::is_static_initializer() const {
   // For classfiles version 51 or greater, ensure that the clinit method is
   // static.  Non-static methods with the name "<clinit>" are not static
   // initializers. (older classfiles exempted for backward compatibility)
   return name() == vmSymbols::class_initializer_name() &&
          has_valid_initializer_flags();
}


-  LinkInfo link_info(defc, name, type, KlassHandle(), 
/*check_access=*/false);
+  LinkInfo link_info(defc, name, type, KlassHandle(), NULL, 
/*check_access=*/false);

Use Handle() instead of NULL.

Also, I'd prefer to see LinkInfo::_current_method and new constructor added:

   LinkInfo(KlassHandle resolved_klass, Symbol* name, Symbol* signature,
            Handle current_method, bool check_access = true) :

_current_klass can be derived from _current_method, since:

//   current_klass  = sending method holder (i.e., class containing the 
method
//                    containing the call being resolved)

> I did the following testing:
> - JPRT
> - pre-PIT RBT run (in progress)
> - the reproducer.
>
> The reproducer now behaves as expected (an IllegalAccessError is
> thrown). Also, the pre-PIT RBT run has shown no new failures so far.
>
> But there is a problem with JPRT: A JCK test, putstatic01801m, fails.
>
> The reason for the failure is that the the test generates a method that
> contains a putstatic to a static final field (i.e., the bytecodes
> generated by the test do not conform to the JVMS). (Even the test
> mentions that the Java source equivalent to the generated bytecodes is
> compilable only if the "final" keyword is removed from the declaration
> of the static field.)
>
> So (if we decide to push the current fix) the issue with the test needs
> to be fixed first. Do you know how we could proceed with that?
File a bug against JCK.

Best regards,
Vladimir Ivanov

[1] 
https://docs.oracle.com/javase/specs/jvms/se6/html/Instructions2.doc11.html

[2] 
https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.putfield

[3] 
https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-5.html#jvms-5.4.3.2

>
> Thank you and best regards,
>
>
> Zoltan
>
>>
>> Best regards,
>> Vladimir Ivanov
>>
>>>
>>> I agree with you, alternatively we can propose a more generic fix (fix
>>> the interpreter and the compilers as well). The fix would most likely
>>> affect field resolution in LinkResolver::resolve_field() around here:
>>> http://hg.openjdk.java.net/jdk9/hs-comp/hotspot/file/07a5eceac654/src/share/vm/interpreter/linkResolver.cpp#l910
>>>
>>>
>>>
>>> Changing resolve_field() to throw an IllegalAccessError for
>>> inappropriate field access will propagate the exception to the
>>> interpreter. Changing ciField::will_link() will most likely kill (some)
>>> compilations (if, e.g., fields are linked through ciField::will_link()).
>>>
>>> I'd prefer to take the second option (changing field resolution), but
>>> there might be some disadvantages related to option I might be
>>> overseeing.
>>
>>
>>
>>
>>>
>>> Thank you!
>>>
>>> Best regards,
>>>
>>>
>>> Zoltan
>>>
>>>
>>>>
>>>>> More details about the failure are here [3].
>>>>>
>>>>> With the patch applied, the program always completes as it is expected
>>>>> by Jython, no matter if we use -Xint, -Xcomp, or -Xmixed (because we
>>>>> always interpret methods non-conforming with the VM spec).
>>>>>
>>>>> Here is the updated webrev:
>>>>> http://cr.openjdk.java.net/~zmajo/8157181/webrev.01/
>>>> Bailing out the whole compilation in C2 is even less appropriate. It
>>>> should generate an uncommon trap for such accesses instead (see
>>>> Parse::do_field_access).
>>>>
>>>> Best regards,
>>>> Vladimir Ivanov
>>>>
>>>> [1]
>>>> http://hg.openjdk.java.net/jdk9/hs-comp/hotspot/file/07a5eceac654/src/share/vm/c1/c1_GraphBuilder.cpp#l1595
>>>>
>>>>
>>>
>


More information about the hotspot-dev mailing list