Sponsored Link •
Safety features built into the JVM
Several built-in security mechanisms are operating as Java virtual machine bytecodes. You have likely heard these mechanisms listed as features of the Java programming language that make Java programs robust. They are, not surprisingly, also features of the Java virtual machine. The mechanisms are:
Whenever you use an object reference, the JVM watches over you. If you attempt to cast a reference to a different type, the JVM makes sure the cast is valid. If you access an array, the JVM ensures the element you are requesting actually exists within the bounds of the array. If you ever try and use a null reference, the JVM throws an exception.
Safety features and security
Because of the safety features built into the Java virtual machine, running programs can access memory only in safe, structured ways. This helps make Java programs robust, but also makes their execution more secure. Why? There are two reasons.
First, a program that corrupts memory, crashes, and possibly causes other programs to crash represents one kind of security breach. If you are running a mission-critical server process, it is critical that the process doesn't crash. This level of robustness is also important in embedded systems such as a cell phone, which people don't usually expect to have to reboot.
The second reason unrestrained memory access would be a security risk is because a wiley cracker potentially could use the memory to subvert the security system. If, for example, a cracker could learn where in memory a class loader is stored, it could assign a pointer to that memory and manipulate the class loader's data. By enforcing structured access to memory, the Java virtual machine yields programs that are robust -- but also frustrates crackers who dream of harnessing the internal memory of the Java virtual machine for their own devious plots.
Unspecified memory layout
Another safety feature built into the Java virtual machine -- one that serves as a backup to structured memory access -- is the unspecified manner in which the runtime data areas are laid out inside the Java virtual machine. The runtime data areas are the memory areas in which the JVM stores the data it needs to execute a Java application. These data areas are: Java stacks (one for each thread); a method area where bytecodes are stored; and a garbage-collected heap, where the objects created by the running program are stored. If you peer into a class file, you won't find any memory addresses. When the Java virtual machine loads a class file, it decides where in its internal memory to put the bytecodes and other data it parses from the class file. When the Java virtual machine starts a thread, it decides where to put the Java stack it creates for the thread. When it creates a new object, it decides where in memory to put the object.
Thus, a cracker cannot predict, by looking at a class file, where in memory the data representing that class -- or objects instantiated from that class -- will be kept. Furthermore, the cracker can't tell anything about memory layout by reading the Java virtual machine specification. The manner in which a JVM lays out its internal data is not part of the specification. The designers of each JVM implementation decide which data structures their implementation will use to represent the runtime data areas, and where in memory their implementation will place them. As a result, even if a cracker somehow were able to break through the Java virtual machine's memory access restrictions, he or she would next be faced with the difficult task of looking around to find something to subvert.
Safety is built in
The prohibition on unstructured memory access is not something the Java virtual machine must actively enforce on a running program; rather, it is intrinsic to the bytecode instruction set itself. Just as there is no way to express an unstructured memory access in the Java programming language, also there is no way to express it in bytecodes -- even if you write the bytecodes by hand. Thus, the prohibition on unstructured memory access is a solid barrier against the malicious manipulation of memory.
There is, however, a way to penetrate the security barriers erected by the Java virtual machine. Although the bytecode instruction set doesn't give you an unsafe, unstructured way to access memory, through native methods you can go around bytecodes.