Section 14.4. LESSONS LEARNED


14.4. LESSONS LEARNED

The introduction of transformation as a new program development tool raises important issues, including interface design, safety and correctness, the existence of multiple namespaces, interactions between base code and inserted code, and difficulties with debugging. Many of these issues arise from the lack of maturity of tools and programming techniques associated with transformation.

JOIE has been available for free to academic researchers since 1997; since then, many research projects have employed it as a transformation environment. These projects include security architectures [22] [6], a framework for automated testing of object-oriented programs [20], a meta-object protocol for Java [23], groupware [14], program termination [16], and a voice-enabling toolkit [7]. The feedback from these researchers, as well as our own experience developing applications, has led to continued refinements in the implementation and interface of JOIE. This section discusses some of the lessons learned from experience.

14.4.1. Interface Design

First, it is important to provide transformer developers with a platform that includes common sequences of operations on bytecode, especially for difficult or dangerous operations. For example, early versions of JOIE did not provide an interface that abstracted individual bytecode details. Not only were the resulting transformers prone to errors, but also each of them repeated similar sequences of code for safety checks and updates. Providing an interface that hid the majority of details of type and local addressing mode simplified the task of assembling transformations and improved their safety and correctness (also see [3] and [18] for alternate interface approaches).

Second, it is easier to construct and reuse transformers that maintain a strong separation between the transformation logic and the content of the inserted code. For example, early implementations of transformers constructed code sequences by manually creating each instruction. This was difficult and tedious and was equivalent to programming in assembly. The resulting transformer was also difficult to reuse, as it hardwired the instructions. Transformers written for later versions of JOIE could simply refer to a separate class containing the splice written in Java. The current interface extends this principle by providing a mechanism to separate different aspects of the transformation logictraversal, target selection, and new code generationinto separate modules.

Third, the concerns best suited to implementation through transformation are those that are orthogonal to the functionality of the original application. These sorts of concerns tend to supply system functionality such as security, monitoring, or language features. The common element of these features is that each assists the base application in providing the same service, but with some additional independent functionality.

14.4.2. Safety, Security, and Correctness

We want transformers to be correct, secure, and safe. A transformer must produce a transformed class that does not violate the security or safety guarantees of Java; it must not interfere with the correct functioning of the target class, except where explicitly intended by the author; and it must correctly perform whatever change on the class the author intended. However, the use of third-party transformers, especially if executed at load time and potentially loaded over the network, raises concerns over whether the resulting transformed code will still be safe, that is, whether it can gain unauthorized access to resources or memory.

Java security is supported by two pillars: the verifier and the Security-Manager. The Java verifier guarantees, among other things, that code is type-safe and particularly that pointers cannot be created or manipulated. This prevents a malicious or buggy program from walking through memory and reading or writing in a way that violates type safety. The SecurityManager is a user-extensible class that is queried by library code at execution time about whether specific classes have permission to access specific resources. For example, a program loaded over the Internet might not be permitted to read from the file system, but it could spawn a new thread. Since load-time transformations occur before the verifier runs, transformed code must still comply with the same type-safety restrictions as non-transformed code.

With respect to security, transformers are no more powerful than any imported code. Put another way, transformed code is no less secure than any code produced by an untrusted compiler. The SecurityManager has the same ability to restrict transformed code's access to system resources as before.

There are also important security policy issues. For example, is it safe for classes to instantiate and register new transformations during the execution of the program? Is it safe to load transformers over the network and apply them? How does a system determine the access permissions of a transformed class? Can classes ever gain or lose permissions as a result of the transformation?

While the verifier prevents outright abuses of the type system (such as pointer arithmetic), a transformer could translate one type-safe program to a subtly different type-safe program but have violated certain aspects of the language model. For example, a class with private data members could be translated into a similar class but with the members redefined to be public. Accesses to those newly public members are perfectly valid and legal, but they violate the original intent of the programmer and thus the source-code language specification. The addition of transformation to program development suggests that the verifier could be extended to include formal analysis of transformations as well [1].

Here, we describe three different security paradigms in which transformers can exist. The first and most secure is to consider the transformers to be a trusted subsystem and only allow transformations supplied by the system provider or installer. For example, a transformer might transform incoming code to comply with a different local security system. User code would have no access to the transformation process.

The second is to allow class providers to supply their own transformers designed to accompany their code. This still allows them to decompose the program into a base functionality and attributes obtainable through transformation, similar, for example, to the programming model for AspectJ [13]. However, this would prevent transformations from one source being applied to another.

The final and most general policy is to allow transformations full access to other classes. While this seems fundamentally unsafe, recall that transformed code is still verified and runs subject to the SecurityManager. And while it is true that the loader might unknowingly attempt to load malicious code, this risk is no higher for transformers than it is for base code.

If the transformer comes from a trusted source, then it should be as trusted as original code. A refinement of this policy is to provide a set of permissions in the SecurityManager that restrict the kinds of transformations the platform currently allows. For example, a particular policy might allow changing the type of classes but not changing private members to public.

14.4.3. Multiple Namespaces

The use of multiple classloaders can complicate an application. Each instance of a classloader in a JVM defines a new namespace for types. That is, an instance of a class loaded by one classloader is not the same type as an instance of that class loaded by a different classloader. This means, for example, that an attempt to assign one to the other results in a type mismatch error. This feature is intended for security, as it provides a measure of isolation between different applications that might share the same run-time context, as is the case with applets or servlets. However, it can make programming with classloaders tricky, as classes that compile correctly can throw run-time type mismatch errors when components are loaded by different classloaders.

This collision points out that two fairly different program mechanismsnamespaces and class loadingare tied together in the JVM specification. Implementing load-time transformation would be cleaner and more convenient if combined with a mechanism that redefined loading semantics but did not create a new namespace. The Load-Time Adaptation project [5] simulated this by interposing class editing between the JVM and the file system, avoiding the classloader altogether.

14.4.4. Reflection

Java offers a capability at runtime to discover the fields and methods of classes, to invoke methods and get and set fields, and to instantiate objects. While this is a useful and powerful technique, it can interfere with some of the functionality of transformation, especially in security-oriented transformation.

Since many security-oriented transformations seek to prevent an application from invoking sensitive methods without going through accounting calls, this ability presents a threat. More seriously, applications could use reflection to access or change the security runtime's state, call its methods, or instantiate objects associated with protected resources. There are a number of possible solutions, including disabling reflection, disabling only reflective method invocation and allocation, or wrapping reflection with checks that prevent access to unwrapped methods. Disabling reflection or some portion of it is the easiest solution but may also prevent legitimate usage.

Similar concerns and responses are associated with the ability to explicitly and dynamically load new classes by name.

14.4.5. Addressing Transformed Functionality

When the original program is written, the transformation is still in the future. Thus, references to classes or methods that will be introduced during transformation are invalid and will not compile. This can have advantages for security-oriented transformations; if methods dealing with the security state are inserted, the runtime can be assured that they are legitimate (since they would not have compiled in the original).

However, this inability to address transformed code can create difficulties when source code needs to access the transformed functionality, such as subscribing to an Observable object. In this case, the fact that the target class has not yet been transformed prevents code from invoking subscribe; that method is at that point not a part of the class. This can be solved with a simple but inelegant pass to a method that receives types Object, typecasts arguments to the appropriate type, and invokes a particular method. (A substantially less strict compiler would solve the problem as well.)

14.4.6. Debugging Transformed Code

When a bug in a transformed application arises, it is often unclear if it is a bug in the original code, a bug in the transformation code, or a bug that arose from the interaction of the two. Currently, debugging transformers demands an understanding of both the original code and the transformer. The problem is particularly severe if multiple transformations are active.

Some specific debugging support is essential to facilitate load-time transformation as a useful programming tool. At the very least, the environment must be able to determine if the failed code was transformed, if transformed code appears in the call chain, and which transformers were responsible for any transformed code.



Aspect-Oriented Software Development
Aspect-Oriented Software Development with Use Cases
ISBN: 0321268881
EAN: 2147483647
Year: 2003
Pages: 307

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net