\HeadingLevelB{Privilege Level Switching}
\label{sec:privilege_switches}

Any architecture that implements software privilege levels must provide a method for
less privileged software to invoke the services of software with higher privilege.
For example, application software needs the OS kernel's assistance to
perform network or disk I/O, as that requires access to privileged memory or
to the I/O address space.

At the same time, less privileged software cannot be offered the ability to
jump arbitrarily into more privileged code, as that would compromise the
privileged software's ability to enforce security and isolation invariants. In
our example, when an application wishes to write a file to the disk, the kernel
must check if the application's user has access to that file. If the ring 3
code could perform an arbitrary jump in kernel space, it would be able to skip
the access check.

For these reasons, the Intel architecture includes privilege-switching
mechanisms used to transfer control from less privileged software to
well-defined entry points in more privileged software. As suggested above, an
architecture's privilege-switching mechanisms have deep implications for the
security properties of its software. Furthermore, securely executing the
software inside a protected container requires the same security considerations
as privilege level switching.

Due to historical factors, the Intel architecture has a vast number of
execution modes, and an intimidating amount of transitions between them. We
focus on the privilege level switching mechanisms used by modern 64-bit
software, summarized in Figure~\ref{fig:cpu_ring_switch}.

\begin{figure}[htpb]
  \center
  \includegraphics[width=85mm]{figures/cpu_ring_switch.pdf}
  \caption{
    Modern privilege switching methods in the 64-bit Intel architecture.
  }
  \label{fig:cpu_ring_switch}
\end{figure}


\HeadingLevelC{System Calls}
\label{sec:syscalls}

% Fast System Calls in 64-Bit Mode: SDM S 5.8.8

On modern processors, application software uses the \texttt{SYSCALL}
instruction to invoke ring 0 code, and the kernel uses \texttt{SYSRET} to
switch the privilege level back to ring 3. \texttt{SYSCALL} jumps into a
predefined kernel location, which is specified by writing to a pair of
architectural MSRs (\S~\ref{sec:address_spaces}).

All MSRs can only be read or written by ring 0 code. This is a crucial security
property, because it entails that application software cannot modify
\texttt{SYSCALL}'s MSRs. If that was the case, a rogue application could abuse
the \texttt{SYSCALL} instruction to execute arbitrary kernel code, potentially
bypassing security checks.

The \texttt{SYSRET} instruction switches the current privilege level from ring
0 back to ring 3, and jumps to the address in RCX, which is set by the
\texttt{SYSCALL} instruction. The \texttt{SYSCALL} / \texttt{SYSRET} pair does
not perform any memory access, so it out-performs the Intel architecture's
previous privilege switching mechanisms, which saved state on a stack. The
design can get away without referencing a stack because kernel calls are not
recursive.


\HeadingLevelC{Faults}
\label{sec:faults}

% Interrupt and Exception Handling: SDM S 6.1, S 6.2
% Access Rights: SDM S 4.6
% Page-Fault Exceptions: SDM S 4.7

The processor also performs a switch from ring 3 to ring 0 when a
\textit{hardware exception} occurs while executing application code. Some
exceptions indicate bugs in the application, whereas other exceptions require
kernel action.

A \textit{general protection fault}~(\#GP) occurs when software attempts to
perform a disallowed action, such as setting the CR3 register from ring 3.

A \textit{page fault}~(\#PF) occurs when address translation encounters a page
table entry whose P flag is 0, or when the memory inside a page is accessed in
way that is inconsistent with the access bits in the page table entry. For
example, when ring 3 software accesses the memory inside a page whose S bit is
set, the result of the memory access is \#PF.

% Interrupt Descriptor Table (IDT): SDM S 6.10

When a hardware exception occurs in application code, the CPU performs a ring
switch, and calls the corresponding \textit{exception handler}. For example,
the \#GP handler typically terminates the application's process, while the \#PF
handler reads the swapped out page back into RAM and resumes the application's
execution.

The exception handlers are a part of the OS kernel, and their locations are
specified in the first 32 entries of the Interrupt Descriptor Table (IDT),
whose structure is shown in Table~\ref{fig:idt_entry}. The IDT's physical
address is stored in the IDTR register, which can only be accessed by ring 0
code. Kernels protect the IDT memory using page tables, so that ring 3 software
cannot access it.

\begin{table}[hbt]
  \centering
  \caption{
    The essential fields of an IDT entry in 64-bit mode. Each entry points to a
    hardware exception or interrupt handler.
  }
  \label{fig:idt_entry}
  \begin{tabular}{| l | r |}
  \hline
  \textbf{Field} & \textbf{Bits} \\
  \hline
  Handler RIP & 64 \\
  \hline
  Handler CS & 16 \\
  \hline
  Interrupt Stack Table (IST) index & 3 \\
  \hline
  \end{tabular}
\end{table}

Each IDT entry has a 3-bit index pointing into the Interrupt Stack Table (IST),
which is an array of 8 stack pointers stored in the TSS described in
\S~\ref{sec:segments}.

% 64-Bit Mode Stack Frame: SDM S 6.14.2
% IRET in IA-32e Mode: SDM S 6.14.3
% Stack Switching in IA-32e Mode: SDM S 6.14.4
% Interrupt Stack Table: SDM S 6.14.5

When a hardware exception occurs, the execution state may be corrupted, and the
current stack cannot be relied on. Therefore, the CPU first uses the handler's
IDT entry to set up a known good stack. SS is loaded with a null descriptor,
and RSP is set to the IST value to which the IDT entry points. After switching
to a reliable stack, the CPU pushes the snapshot in Table~\ref{fig:fault_stack}
on the stack, then loads the IDT entry's values into the CS and RIP registers,
which trigger the execution of the exception handler.

\begin{table}[hbt]
  \centering
  \caption{
    The snapshot pushed on the handler's stack when a hardware exception
    occurs. IRET restores registers from this snapshot.
  }
  \label{fig:fault_stack}
  \begin{tabular}{| l | r |}
  \hline
  \textbf{Field} & \textbf{Bits} \\
  \hline
  Exception SS & 64 \\
  \hline
  Exception RSP & 64 \\
  \hline
  RFLAGS & 64 \\
  \hline
  Exception CS & 64 \\
  \hline
  Exception RIP & 64 \\
  \hline
  Exception code & 64 \\
  \hline
  \end{tabular}
\end{table}

After the exception handler completes, it uses the \texttt{IRET} (interrupt
return) instruction to load the registers from the on-stack snapshot and switch
back to ring 3.

The Intel architecture gives the fault handler complete control over the
execution context of the software that incurred the fault. This privilege is
necessary for handlers (e.g., \#GP) that must perform context switches
(\S~\ref{sec:registers}) as a consequence of terminating a thread that
encountered a bug. It follows that all fault handlers must be trusted to not
leak or tamper with the information in an application's execution context.


\HeadingLevelC{VMX Privilege Level Switching}
\label{sec:vmx}

% Life Cycle of VMM Software: SDM S 23.4
% Virtual-Machine Control Structure: SDM S 23.5

Intel systems that take advantage of the hardware virtualization support to host
multiple operating systems concurrently use a hypervisor to manage the
VMs. The hypervisor creates a \textit{Virtual Machine Control Structure} (VMCS)
for each operating system instance that it wishes to run, and uses the
\texttt{VMENTER} instruction to assign a logical processor to the VM.

% VM Exists: SDM S 27
% EP{T_Induced VM Exits: SDM S 28.2.3

When a logical processor encounters a fault that must be handled by the
hypervisor, the logical processor performs a VM exit. For example, if the
address translation process encounters an EPT entry with the P flag set to 0,
the CPU performs a VM exit, and the hypervisor has an opportunity to bring the
page into RAM.

The VMCS shows a great application of the encapsulation
principle~\citep{liskov1974adt}, which is generally used in high-level software,
to computer architecture. The Intel architecture specifies that each VMCS
resides in DRAM and is 4~KB in size. However, the architecture does not specify
the VMCS format, and instead requires the hypervisor to interact with the VMCS
via CPU instructions such as \texttt{VMREAD} and \texttt{VMWRITE}.

This approach allows Intel to add VMX features that require VMCS format
changes, without the burden of having to maintain backwards compatibility.
This is no small feat, given that huge amounts of complexity in the Intel
architecture were introduced due to compatibility requirements.
