Security and CPU-Virtualization

The CPU is a just another ressource when dealing with virtualization and virtualization technologies differ very much in the way they try to virtualize this ressource. It’s easy to imagine that this results in different security aspects as well. In fact, the virtualization of the CPU is a security issue in general, because a CPU offers some instructions that manipulate hardware ressources. Anyone who can call these instructions directly would be the owner of the hardware and no security limits would apply when dealing with sensitive information. A CPU has a mechanism to enforce security, namely Protection Rings, 2 at a minimum (Ring 0 and Ring 1). Hierarchical protection rings distinguish user-mode in an outer ring and kernel-mode at the innermost ring. An inner ring offers more privileges than an outer ring does, which only offers a subset of the instruction set. The innermost ring offers all privileges, software running at that privilege level can use the whole instruction set of the CPU. Trying to call a highly privileged instruction from an outer ring fails.

Gerald J. Popek et al. defined the formal requirements of a virtualizable architecture within their paper “Formal requirements for virtualizable third generation architectures”. Privileged Instructions are instructions that trap when they are called from user-mode. No trap is generated when they are called from kernel-mode. A trap passes control to a predefined, trustworthy routine, a trap-handler, so that processor mode changes. In a usual operating system trap handlers are deeply located in the kernel. A sensitive instruction is an instruction that reads from or writes to sensitive registers or memory locations. For an architecture to be virtualizable, the set of sensitive instructions has to be a subset of the privileged instructions: all sensitive instructions have to trap. On such an architecture control can be passed to the VMM, which maintains control over sensitive instructions and the hardware. A guest is unable to access hardware directly - it can only access hardware using the way through the VMM that enforces the security policy. In addition the VMM is able to emulate the behaviour a guest expects. A guest trying to access sensitive data would read sensitive data of the real machine typically, but because the instruction traps and control is passed to the VMM the VMM can return data of the virtual machine by bookkeeping Shadow Structures for each virtual machine. This method is called Trap & Emulate.

We want secure systems to sit on top of small, isolated components. Trap & Emulate is very easy to implement, we only have to implement the trap handlers, so we win in a rather low complexity through it. Traps are existing mechanisms, deeply studied and in use for many years. These existent mechanisms can be used for virtualization resulting in high degree of logical isolation. The VMM remains small, easy and formally verifyable. The Trusted Computing Base (TCB) remains small promising a high degree of security, because it reduces the usual risks of flaws contained in complex code - and complexity is the point where x86-virtualization comes in.

Trap & Emulate is only possible on “virtualizable” architectures and unfortunately x86 is not virtualizable in that manner. Today many other ways exist to virtualize an architecture. So the formal requirements of Popek & Goldberg don’t tell if an architecture generally is virtualizable, they tell whether it is virtualizable using Trap & Emulate or not. x86 contains 17 sensitive, non-privileged instructions (they fail silently) disabling Trap & Emulate, because control won’t be passed to the VMM when these sensitive instructions are called. Therefore the VMM can’t emulate the expected behaviour and for example the guest can discover that it is running in a virtual environment, because the behaviour differs from the behaviour the guest usually expects.

Binary Translation tries to overcome the limits of x86 by translating the sensitive, unprivileged instructions on the fly. Binary Translation can insert Traps into the binary code so that these instructions will trap afterwards. Other instructions can be executed without any changes. The sensitive, unprivileged instructions can occur anywhere within the binary code. Therefore the Binary Translator has to scan through the whole code, because only scanned code is safe to execute. A special problem a binary translator has to deal with is self-modifying code. Self-modifying code is able to insert problematic instructions after the code is scanned by the binary translator. Trap & Emulate is resistent against self-modifying code, because a trap is generated exactly at the moment of execution. Binary Translation has to scan for this kind of code explicitly. Therefore, a Binary Translator is a so called Sanitizer, because the code gets cleaned explicitly to make it trustworthy. A Sanitizer is in conflict with the principle of Security by Design and fail safe defaults, because every flaw within a sanitizer puts system integrity at risk. The sensitive, unprivileged instructions get filtered explicitly instead of explicitly allowed. The whole code is scanned, filtered and translated, therefore the VMM has to use special ways to reach a satisfying performance. The Binary Translator can try to optimize the code just like a compiler tries to or it can establish a trace cache containing already scanned blocks of code to speed translation. According to Kevin Lawton in his paper on “Running multiple operating systems concurrently on an IA32 PC using virtualization techniques”, a Binary Translator has to insert breakpoints to interrupt execution. Such a Binary Translator has to deal with Code, reading parts of its own code and reading inserted breakpoints, too.

Binary Translation is a very complex method to overcome the x86-penalties for virtualization. The source and destination instruction set is identical, but much work is needed to scan, filter and translate a small subset of the instruction set: the sensitive, unprivileged instructions. According to John Scott Robin et al. in their paper “Analysis of the Intel Pentium’s Ability to Support a Secure Virtual Machine Monitor” “the complexity of this approach may render a highly secure VMM unachievable”. Therefore, a VMM using Binary Translation is not as trustworthy as a VMM using Trap & Emulate.

Paravirtualization uses an other idea to overcome x86. The set of 17 sensitive, unprivileged instructions is just a small subset of the instruction set of x86. These 17 instructions are called by the Guest-OS primarily. Therefore paravirtualization usually adapts each Guest-OS. Afterwards a Guest-OS does not use problematic x86 instructions and the Guest-OS can be run in an outer protection ring - and can’t call sensitive Instructions without VMM-intervention. The Guest-OS runs on top of an idealized Abstraction of x86, similar to the underlying architecture, but not the same. Any attempt to access hardware has to be permitted by the VMM first (fail-safe-defaults!). A paravirtual Guest-OS is “enlightened” and therefore VMM-Implementatation is much easier, because we don’t have to keep the illusion for the virtual environment to appear as the real one and the TCB can remain small. The VMM just has to offer a small interface encapsulating the problematic x86-instruction-subset. The VMM forces the Guests to cooperate or, if they won’t,  fail. Therefore a paravirtual VMM is more trustworthy than a VMM using Binary Translation and similar to a VMM using Trap & Emulate.

A parvirtual Guest-OS usually runs unmodified User-Space-Applications. Security-Patches usually have to be written for each specific architecture software runs on. Patches for User-Space-Applications will become public in time, but the Guest-OS runs on an additional paravirtual architecture. Conceptually, security patches for these Guest-OS-Kernels are not guaranteed to become public in time, because the patches have to be written or compiled for this architecture explicitly. Therefore, the risk of Zero-Day-Exploits for paravirtual Guest-Kernels rise.

Newer generations of x86-CPUs contain technologies to support virtualization (Vanderpool/Pacifica). A design goal of these technologies was to eliminate the need for Binary-Translation and Paravirtualization on the x86-Architecture. The CPU creates containers that conceptually are virtual CPUs and introduce new modes of operation. The modes distinguish whether the CPU is virtual or real and which privileges are associated with it. The VMM runs within a container of highest privileges, conceptually Protection Ring: -1. Guests run within a lower privileged container, but conceptually this container is a protection ring 0. Therefore, Guest-OS-Kernels can run in their usual ring. The Guests sit in their virtual environment, which will be left (exited) when the guest tries to execute privileged or sensitive instructions and trap to the VMM. That is just another way of Trap & Emulate, therefore all security issues of Trap & Emulate apply to CPU-supported Virtualization as well.

But, actually CPU-supported Virtualization does not solve all security issues, because we have to deal with hardware-emulation on x86, which is another big problem for security of virtualization technologies. But.. more about this next time.

Leave a Reply

CAPTCHA Image Audio Version
Reload Image