Sponsored Link •
This article looks at the class verifier of the Java virtual machine (JVM). The class verifier enables untrusted code to be verified up front, rather than on the fly as the code is executed. This ability provides uninterrupted execution (the program can't "crash" uncontrollably) at a minimal cost in speed degradation.
This month's article continues the discussion of Java's security model begun in August's "Under the Hood." In that article, I gave a general overview of the security mechanisms built into the Java virtual machine (JVM). I also looked closely at one aspect of those security mechanisms: the JVM's built-in safety features. In September's "Under the Hood," I examined the class loader architecture, another aspect of the JVM's built-in security mechanisms. This month I'll focus on the third prong of the JVM's security strategy: the class verifier.
The class-file verifier
Every Java virtual machine has a class-file verifier, which ensures that loaded class files have a proper internal structure. If the class-file verifier discovers a problem with a class file, it throws an exception. Because a class file is just a sequence of binary data, a virtual machine can't know whether a particular class file was generated by a well-meaning Java compiler or by shady crackers bent on compromising the integrity of the virtual machine. As a consequence, all JVM implementations have a class-file verifier that can be invoked on untrusted classes, to make sure the classes are safe to use.
One of the security goals that the class-file verifier helps achieve is program robustness. If a buggy compiler or savvy cracker generated a class file that contained a method whose bytecodes included an instruction to jump beyond the end of the method, that method could, if it were invoked, cause the virtual machine to crash. Thus, for the sake of robustness, it is important that the virtual machine verify the integrity of the bytecodes it imports.
Although Java virtual machine designers are allowed to decide when
their virtual machines will perform these checks, many implementations
will do most checking just after a class is loaded. Such a virtual
machine analyzes bytecodes (and verifies their integrity) once, before
they are ever executed. As part of its verification of bytecodes, the
Java virtual machine makes sure all jump instructions -- for example,
goto (jump always),
ifeq (jump if top of
stack zero), etc. -- cause a jump to another valid instruction in the
bytecode stream of the method. As a consequence, the virtual machine
need not check for a valid target every time it encounters a jump
instruction as it executes bytecodes. In most cases, checking all
bytecodes once before they are executed is a more efficient way to
guarantee robustness than checking each bytecode instruction every time
it is executed.
A class-file verifier that performs its checking as early as possible most likely operates in two distinct phases. During phase one, which takes place just after a class is loaded, the class-file verifier checks the internal structure of the class file, including verifying the integrity of the bytecodes it contains. During phase two, which takes place as bytecodes are executed, the class-file verifier confirms the existence of symbolically referenced classes, fields, and methods.