Table of Contents

Control Flow Integrity (CFI): User vs Kernel Land

Used on CFI blog post

There are typically two modes of operation in operating systems (OS): user mode and kernel mode. These modes provide a privilege separation between types of processes, ensuring that user applications cannot directly interfere with system operations.

Note: Some CPU architectures expose more than two privilege “rings,” and some microkernel designs break certain OS services into separate user-mode processes, but the broad concept of “user mode vs. kernel mode” remains as described here. 

User mode

This is the restricted mode where most applications run. In user mode, processes are prevented from directly accessing hardware or executing privileged instructions. Instead, they rely on system calls to request services from the OS, which runs in kernel mode.

To look at this in practice, I use the process of saving a file. In user mode, I would use a text editor like VS Code. I would type a bunch of notes and then hit Ctrl+S to save.

The text editor (a user-mode application) cannot directly access the disk to save my file. Instead, it makes a system call (e.g., write() in Unix-like systems or WriteFile() on Windows). This is a request for a service that requires privileged access—in this case, writing to the disk.


🌸👋🏻 Join 10,000+ followers! Let’s take this to your inbox. You’ll receive occasional emails about whatever’s on my mind—offensive security, open source, boats, reversing, software freedom, you get the idea.


Kernel mode

On the other hand, there is kernel mode. This is the privileged mode where the OS’s functions and services run. In kernel mode, processes have direct access to the system’s hardware and memory.

Taking the example of saving a file to disk, I can see it operates differently in this space. Here, the system call transfers control to the OS kernel, switching the CPU into kernel mode. The kernel now has full access to hardware and system memory. It handles the request: locating the file on disk, writing the contents, updating file metadata. Once finished, it returns control back to user mode, and my text editor continues running as usual.

User ModeKernel Mode
Limited access (safe sandbox)Full system access
Runs apps like browsers, IDEsRuns core OS code (device drivers)
Makes system calls to request privileged actionsExecutes those privileged actions

Control flow integrity (CFI)

Within these modes, control flow integrity (CFI) operates differently. CFI is a technique that ensures a program’s execution follows only legitimate control flow paths determined at compile time. CFI is primarily used to mitigate against control-flow hijacking attacks like return-oriented programming (ROP) and jump-oriented programming (JOP), which exploit vulnerabilities such as buffer overflows.

Read more: How to use ROP to bypass security mechanisms – Olivia A. Gallucci 

User-land CFI

Let’s start with CFI in the user-land. In user-land, CFI helps secure applications from control flow hijacking, especially in scenarios where I can manipulate user-level programs through memory corruption vulnerabilities (e.g., stack or heap overflows).

Implementation 

CFI in user-land is often implemented via compiler-based mechanisms, where checks are inserted into the compiled code to ensure the control flow adheres to an expected graph. For example, LLVM’s CFI or Microsoft’s Control Flow Guard (CFG) are often used for protecting user-land applications. 

Read more: Compilation Phases Explained: Analysis and Synthesis – Olivia A. Gallucci 

In user-land, CFI protects against attacks that target common programs (e.g., browsers and productivity software) and aims to stop me from executing arbitrary code by forcing the program to follow its legitimate control flow.

Kernel-land CFI

CFI’s role in kernel mode follows the same principle (only allow known/legitimate jump targets) but is implemented differently because of the kernel’s privileged environment.

So, let’s examine CFI in kernel-land! 

Here, the goal is to protect the OS kernel (rather than applications) from control-flow hijacking, which could lead to full system compromise. Exploits in kernel-land usually have higher privileges than in user-land, which makes it extra dangerous. 

Implementation 

Kernel CFI is more complex due to the kernel’s “low-level” operations and performance requirements. This CFI needs to account for dynamic and complex behaviors, like system calls, interrupts, and exception handling.

One example of kernel CFI is Clang. Clang’s kernel CFI is one of the Linux kernel’s security initiatives to provide software-based CFI (specifically, to ensure that kernel code executes as intended).

There is also Kernel Control Flow Integrity (KCFI), which is tailored to enforce CFI within kernel functions and data structures. KCFI aims to prevent hijacking function pointers or misusing control-flow mechanisms. 

Complementing these software-based protections is Intel’s Control-flow Enforcement Technology (CET), which introduces hardware-based CFI support. The main difference is that Clang’s CFI and KCFI are software-based mechanisms focused on enforcing control-flow integrity at compile time, primarily for the kernel, whereas Intel’s CET provides hardware-based enforcement that protects user and kernel space at runtime.


🌸👋🏻 Join 10,000+ followers! Let’s take this to your inbox. You’ll receive occasional emails about whatever’s on my mind—offensive security, open source, boats, reversing, software freedom, you get the idea.


Conclusion

In conclusion, user-land CFI can afford more flexibility since performance penalties might be acceptable in some applications. 

Kernel-level CFI tends to be more difficult to implement because of low overhead requirements, real-time constraints, and the dynamic nature of kernel execution (e.g., interrupts, exceptions, etc.). Additionally, the kernel operates with higher privileges, meaning that a kernel-level control flow attack is potentially more damaging than a user-land attack. 

If you enjoyed this article on CFI, consider reading ASLR, bypass techniques, and circumvention impacts

Portrait of Olivia Gallucci in garden, used in LNP article.

Written by Olivia Gallucci

Olivia is senior security engineer, certified personal trainer, and freedom software advocate. She writes about offensive security, open source software, and professional development.

Discover more from [ret]2read

An OS Internals Newsletter by Olivia Gallucci. Subscribe now to keep reading and get access to the full archive.

Continue reading