fast static lookup can be used instead of dynamic dispatch for field access. (without ... denial-of-service attacks on field access is prevented, and. â access ...
Java Access Protection through Typing? Eva Rose1 and Kristoffer Høgsbro Rose2 1 INRIA-Rocquencourt (GIE Dyade); Domaine de Voluceau, Rocquencourt B.P. 105; F–78153 Le Chesnay (France) 2 ENS-Lyon (LIP); 46, All´ee d’Italie; F–69364 Lyon 7 (France)
Abstract. We propose integrating field access in general, and dedicated readonly field access in particular, into the Java type system. The principal gain is that “getter” methods can be eliminated such that – fast static lookup can be used instead of dynamic dispatch for field access (without requiring a sophisticated inlining analyses), – the (noticeable) space required by getter methods is avoided, – denial-of-service attacks on field access is prevented, and – access protection violations can be discovered by the bytecode verifier thus further simplifying the required run-time support. We obtain this by extending a formalization of the Java bytecode verifier with access control so we can prove that the change is safe and backwards compatible.
1
Introduction
Object-oriented programming languages in general, and Java in particular, do not distinguish between read- and write-access to fields. Instead the recommended method to only permit read access to a field is to make the field private and write a “getter” method that accesses the field and returns the stored value. For Java, the semantics of field access states that the actual field location accessed in an object can be determined statically (at compile-time), whereas the actual getter method invocation is determined dynamically (at run-time) [1, §15.10.1]. This has the following consequences: – Using a getter method is significantly slower (at run-time) than using a direct field access. (The traditional remedy for this is to declare getter methods final which permits the compiler to inline its body, i.e., insert the field access instruction directly at the invocation place. In Java this is frequently not feasible because Java employs dynamic class loading which means that often a class to inline from is not available when installing a class using a getter method.) ? Extended abstract, presented at the Formal Techniques for Java Programs work-
shop at ECOOP 2000 . workshops/ecoop2000.html
– It is possible to access the field belonging to a particular (super)class of an object by simply casting the object of the field access to the appropriate class. One cannot obtain a similar effect with a getter method. (One may see this as a feature rather than an inconvenience.) – “Denial-of-service” attacks are possible in that a getter method can be overriden by a subclass. (This can also be avoided by declaring the method final.) – Finally, getter methods may add a significant space overhead to class files since they must be declared and their code given. For example, getter methods account for about one fourth of the total number of methods in the standard Java “java.*” package source classes.1 Furthermore the Java virtual machine (JVM) specifies that field access control is performed through (dynamic) load and run time checks. This seems a shame since everything else about fields is static. Here is a traditional example with a getter method: an object that simply contains an integer value that should be publicly readable. class CrCardRd1 { int it; public int getIt() {return it;} } Access to the it field value of an object cc of type CrCardRd1 requires the method invocation cc.getIt() with the problems discussed above. In this paper we propose a simple modification in two steps that eliminates the problem altogether: 1. add a special get-specific access modifier that permits making the reading of a field “more public” than the modification of it, and 2. integrate field access checks into the type system. In effect we propose replacing the above code with class CrCardRd2 { read public int it; } which explicitly permits everyone to read off the field value with the usual field access syntax cc.it (but not to assign to it). 1
This measure obtained for Sun’s JDK 1.1 [4] with the unix commands “find jdk1.1 -name ’*.java’ -exec grep ’ +public .*(’ ’{}’ ’;’ | wc -l” to get the total number of public methods (4317), and “find jdk1.1 -name ’*.java’ -exec egrep ’ +public .* get.*(’ ’{}’ ’;’ | wc -l” to get the number of getter methods (999).
Modifier private package protected public
Accessibility from same class other class, same package subclass outside package other class outside package Table 1. Java Access Modifiers.
XXXX XXX XX X
Plan. In section 2 we propose a mininimal extension of the Java language [1] with the desired semantics, and since the Java runtime environment is centered around the JVM [2], we exlain how the modification could be specified for the JVM. In section 3 we then explain how we can integrate field access into the type system of the JVM to be performed by the JVM bytecode verifier. Finally, we conclude in section 4 with some remarks on future work.
2
Read-only Field Access in Java
Recall that the Java access “modifiers” change the access rights as shown in table 1. (The “package” modifier is the default assumed when no modifier keywhen access is permitted and when it word is present and the table has is not.) Notice that the permissions are strictly included in each other and, in fact, statically checkable, since both the class hosting the field and the method attempting to access it are statically known. We propose to extend the Java language with syntax for specifying an access modifier specific to reading a field value. This can be done with the following syntax extension to the Java Language Specification [1, §8.3.1]:
X
FieldDeclaration: FieldModifiersop ReadModifierop Type VariableDeclarators ; :::
ReadModifier: read AccessModifierop The semantics of the new construction is that we must separate field access from field assignment: an access that is not an assignment, i.e., is not a Java LeftHandSide [1, §15.25], is permitted if either of the (original) field access modifier or the specific read access modifier (if any) permits it.
By using “either” we ensure that our extension is conservative in that old systems ignoring the read modifier will always be strictly less permissive than new ones. This change permeates through to the JVM [2] where it could be realized directly by extending the access flags item of the field info structure and modifying the getfield semantics correspondingly, however, we will prefer to integrate it into the type system as described in the next section.
3
Field Access Types
At present field access rights are checked for the getfield instruction both at Java verification time, for the declared “static” type [2, §4.8.2, p.138], and again at run-time, using the real “dynamic” type [2, §6.4, p.248]. Our idea is the following: if access information is integrated into the type system then 1. the bytecode verifier can check for access violations assuming that the provided access information (in the type) is correct, and 2. resolution requires equality of the used and actual type. Thus once resolution has happened the system has checked that no access violation can happen. Formalizing this is based on the ordering private < package < protected < public (where larger access rights define more accessible fields). Assume a field is declared like class 1 { w read r }
t f
;
with 1 the class hosting the field, w the “write” access modifiers, r the “read” access modifiers, t the field (value) type, and f the field name. Consider an access in a class from within some method with code like
2 :::
x; x.f
:::
(with 2 a subclass of 1 ). When we include the access modifiers in the type information this means that the field access generates the JVM instruction getfield( 1 ;
w
read r t ; f)
where we note that the bytecode (as usual) contains
– the class where the field is declared: 1 , – a copy of the (complete) type of the field extracted from the original definition: w read r t, and – the field name: f. We will express the static check for access rights of the above situation by extending the JVM type checking (verification) rules with a judgment like
`
getfield( 1 ;
w
read r t ; f)
defined by
`
getfield( 1 ; r
r
`
read public t ; f)
protected getfield( 1 ;
w
:
1
read r t ; f)
package same-package( ` getfield( 1 read
; 1 )
w
`
; w
getfield( ;
w
r t ; f)
read private t ; f)
Notice that it is the fact that the checks in table 1 are static that makes this possible since the verifier merely needs to be able to determine whether two classes belong to the same package and whether the current class is a subclass of the class owning the field; both can be checked with information readily available. There are similar rules for putfield checking the w component of the type, of course. All that remains is to encode the access modifiers into the JVM FieldDescriptor encoding [2, §4.3.2]: the existing type equality test at resolution time will ensure that the verifier has not made false assumptions. (The encoding is not difficult but outside the scope of this paper.) In the full paper we integrate the above rules into our formalization of a Java bytecode verifier [3], and show that “well-access-typed” programs cannot violate field permissions.
4
Conclusion
We have outlined how access rights to fields, and specifically read-only access rights, can be encoded in the Java type system as implemented by a (slightly
modified) Java bytecode verifier, thus eliminating all access right checks at runtime. One very interesting further venue of research is that using “access types” could be used to implement “sticky” access rights such as “private objects” where the value cannot be passed out of the current method, for example. One may comment that “static is bad because everything should be run-time configurable.” This possibility remains (using setter and getter methods) but we believe it is important to give the programmer of a class the choice of permitting (efficient) build-in static field access even for read-only fields, specifically for the variants of Java targeted at devices with limited resources [5]. Another question that one could ask is “why not for ‘setter’ methods?” This can be done but is complicated by the fact that “setter” methods usually also check the value to be stored for validity to ensure that the object is (internally) consistent. One could introduce special “validity checks” such that our getter example could be extended, for example, with a class CrCardRd raises IllegalCreCaIt { protected read public int it {if (it