HostedDB - Dedicated UNIX Servers

-->
Internet Security Professional Reference:Java Security
Previous Table of Contents Next


One possible solution is the encrypted signature that is used in e-mail programs to ensure that the file is from the correct person. By setting up a public-private key pair, in which the owner of the class files encrypts each class file and provides it with a signature, the individual class files can be checked for authenticity before being downloaded. This sort of encryption protection is now available in version 1.1 of the Java SDK. However, the only browser that currently supports the feature is Sun’s HotJava browser. When using a browser that doesn’t support signed class files, remember to exercise extreme caution if you are using dynamic classes loaded from across the web in any critical application.

Running Code

The job of running the code compiled for the JVM falls to the interpreter. The interpreter process can be divided into three steps:

  Loading code
  Verification
  Execution

The loading of code is done by the class loader. This section of the interpreter brings in not only the Java file that is referenced, but also any inherited or referenced classes that the code needs. Next, all the code is sent through the bytecode verifier to ensure that the code sticks to the Java standard and does not violate system integrity. Finally, the code passes to the runtime system for interpreted execution on the hardware (see fig. 13.1). These three steps in the interpreter process are discussed in greater detail in the next section.


Figure 13.1  The Java runtime system.

Class Loader

The class loader consolidates all the code needed for execution of an application, including classes you have inherited from and any classes you call. When the class loader brings in a class, it places it in its own namespace. This is similar to the virtual machines within which applications run in an operating system. Without explicit calls to classes outside their namespace that are referenced symbolically, classes cannot interfere with each other. The classes local to the machine are each given one address space, and all classes imported are given their own namespace. This allows local classes the added performance benefit of sharing a namespace, while still protecting them from imported classes, and vice versa.

After all the classes have been imported, the memory layout for the total executable can be determined. Symbolic references can have specific memory spaces attached, and the lookup table can be created. By creating the memory layout at this late stage, the interpreter protects against fragile superclasses and illegal addressing in code.

Bytecode Verifier

The interpreter does not, however, start assuming at this point that the code is safe. Instead, the code passes through a bytecode verifier that checks each line for consistency with the Java specification and the program itself. By using a theorem prover, the bytecode verifier can trap several of the following problems with code:

  No forged pointers
  No access restriction violations
  No object mismatching
  No operand stack over- or underflows
  Parameters for bytecodes are all correct
  No illegal data conversion

The use of the bytecode verifier serves two purposes. First, because all these conditions are known, the interpreter can be sure that the executable will not crash the system through errant procedures. Second, the interpreter can execute the code as quickly as possible, knowing that it will not run into problems for which it might otherwise have to stop and check during the run. In both cases, the code is subject to the procedure once and can then run unimpeded for its duration.

Code Execution

After the code has been collected and laid out in memory by the loader and checked by the verifier, it is passed on to be executed. The execution of the code consists of converting it to operations that the client system can perform. This can happen in two ways:

  The interpreter can compile native code at runtime, and then allow this native code to run at full speed, or
  The interpreter can handle all the operations, converting the Java bytecodes into the correct configuration for the platform, an opcode at a time.

Typically, the second method is used. The virtual machine specification is flexible enough to be converted to the client machine without copious amounts of overhead. The current method used by the Java Development Kit released by Sun relies on the interpreter to execute the bytecodes directly. For the most computationally intensive problems, the interpreter can provide a just-in-time compiler that will convert the intermediate Java bytecode into the machine code of the client system. This enables the code to be both portable and high performance.

The stages of the runtime system are a balance among three issues:

  Portability. Portability is dealt with by using an intermediate bytecode format that is easily converted to specific machine code form. In addition, the interpreter determines memory layout at runtime to ensure that imported classes remain usable.
  Security. Security issue is addressed at every stage of the runtime system. Specifically, though, the bytecode verifier ensures that the program executes correctly according to the Java specification.
  Performance. Performance is dealt with by making sure that all overhead is either performed at the beginning of the load-execute cycle or runs as a background thread, such as the garbage collector.

In these ways, Java takes modest performance hits to guarantee a portable, secure environment, while still ensuring that performance is available when needed most.


Previous Table of Contents Next