Naccio Department of Computer Science
University of Virginia

Generating Policies

This page is based on:
David Evans and Andrew Twyman. Policy-Directed Code Safety. 1999 IEEE Symposium on Security and Privacy, Oakland, California, May 9-12, 1999.
.

The policy generator analyzes a safety policy and produces a policy-specific version of the platform library. The library produced depends on the platform interface - for JavaVM, we produce a policy-specific version of the Java API; for Win32, we produce policy-specific versions of the Win32 system DLLs. Most of the work done by the policy generator is substantially the same across all platforms. The differences is the format of the input and output platform library and the format and content of the platform interface.

The work of the policy generator can be divided into two main parts: generating resource implementations that perform the safety policy checks and creating a policy-specific library that uses the resource implementations.

Resource Implementations. Generating resource implementations involves analyzing the safety policy to determine which resource operations do meaningful checking and generating code that implements those resource operations. Code from safety properties and state maintainers is combined and translated to create a resource operation. A dependency analysis determines which resource operations are necessary. A resource operation is necessary if it could produce a safety violation or if it modifies some state that is used by another resource operation that could produce a safety violation. The necessary resource operations are then translated to produce a platform specific implementation.

public class RFileSystem {
   static public int bytes_written = 0;
   static public void write (RFile file, int n) {
       bytes_written += n; 
       if (bytes_written > 1000000) 
          Naccio.library.Check.violation 
              ("LimitWrite", "LimitBytesWritten", 
                "Attempt to write more than " ...);
    }
}
Generated Resource Implementation Excerpt

Naccio/JavaVM generates resource implementations as Java code and compiles them using a standard Java compiler. The figure above shows an excerpt of the resource class generated for the RFileSystem resource shown to enforce the LimitWrite safety policy. Because RFileSystem was declared as a global resource, all instance variables and methods are declared static. The write method incorporates code from the TrackTotalBytesWritten maintainstate block and the LimitBytesWritten safety property. The precode action declared in the maintainstate block is performed before the check action in the safety property. The parameter to the LimitBytesWritten safety property used in the safety policy has been bound in the code. The violation command in the safety property is replaced with a call to a Naccio library method, and an additional arguments giving the name of the safety policy and property are passed so that an informative error message can be produced.

To be secure, Naccio must implement the generated platform wrapper and resource classes in a way such that the transformed program cannot manipulate state associated with safety checking. For Naccio/JavaVM, we can do this simply by using new class instance variables and restricting use of the Java reflection classes so that the transformed program cannot access this state. We can express the necessary constraints on the reflection classes by using platform interface wrappers.

For the Win32 platform, protecting resource state poses a more serious challenge. We are currently investigating variations on software fault isolation that can efficiently provide the necessary guarantees.

Policy-Specific Library. We use the resource implementations to generate a policy-specific platform library. The policy-specific library needs to call the appropriate resource operations when a system method would effect the resource. The necessary calls are described in the platform interface.

For Naccio/JavaVM, we modify the Java library classes using the JOIE toolkit [Cohen98]. Using the analyses from the resource generation phase, we determine which platform interface wrappers call necessary resource operations, and which wrappers can be eliminated. The necessary wrappers are then added to the library classes. We rename the original method by adding a unique prefix to the method name. This is necessary since the wrapped version of the method and other methods in the class library will need to be able to call the original method. The wrapper code from the platform interface is compiled into Java byte codes and inserted into the class file in place of the original method.

Next, we need to ensure that the original (unwrapped) version of the method is called in the rest of the library. This is necessary to prevent implementation-dependent duplication of safety checking. For example, if the library provides one method for writing a single byte to a file and another method for writing an array of bytes to a file, the platform interface should describe each method independently in terms of how it effects resources. If the write array of bytes method happens to be implemented by calling the write byte method once for each byte in the array, the safety checks associated with the write byte method should not be done for the inner call. Hence, we need to replace internal calls to wrapped API methods with calls to the unwrapped version of the method.

Finally, we need to replace library class names to refer to the policy-specific library. This is done by adding a unique package name to each class and placing the output classes in a new directory (e.g., java.io.File becomes policy825.java.io.File).

A few complications are introduced by constructors and native methods. Since the names of constructors are fixed, we can't simply rename the original constructor. Instead, we add one or more extra unused arguments to distinguish the original constructor. For native methods, we cannot change the method name without providing a way for the JavaVM to find the correct native method implementation. In order to be able to rename a native method, we need to either modify the VM so that it can still map the new name to the correct native method, or produce a new object file implementing the native method using the new name. The current Naccio/JavaVM implementation depends on new native method implementations being generated manually, however there is no reason they cannot be generated automatically. Unfortunately, native method formats are VM-dependent, so handling them in this way means our implementation is not portable across different VMs without some effort. The problem would be avoided if the Java class file format were extended to allow indirect naming of native methods.

For the Win32 implementation, the policy generator produces policy-specific versions of the library DLLs. The policy-specific versions of the library DLLs contain export table entries for all functions in the original DLL. These entries identify wrapper routines, which call the library API routines using the original DLLs. Entries for which no wrapper is necessary can be implemented simply as forwarding pointers, causing the Win32 loader to substitute the original API call with no extra runtime overhead. Policy Description File. The other output of the policy generator is the policy description file. This file contains transformation rules that describe compactly the changes the application transformer must perform. The policy description file is platform independent.

For most policies, it contains a single rule that indicates the policy-specific package name that should be appended to library classes. Occasionally, it may contain other directives, such as to modify the main method to record the command line arguments so that they can be used in a safety policy.

Next - Transforming Applications
Return to Policy Generation Overview

Naccio Home Page
David Evans
University of Virginia, Computer Science