CHERI-seL4: 6 reasons why CHERI support in seL4

I have long debated that question, “what’s the point of CHERI if you have seL4 (and Rust)?” In a nutshell, CHERI C offers rich, deterministic memory safety and intra-address-space software compartmentalisation, which enhances the overall security of your software system (see the Figure below). How it does that and why memory-safety is crucial for software security are explained in the CHERI literature [1-5] and is beyond the scope of this blog post. The focus of this post is why CHERI, at all, on seL4?

Memory safety and software compartmentalisation offer enhanced software security

To quickly list 6 main reasons why, having CHERI on seL4 would allow seL4 developers to:

  1. Follow secure coding practices, especially when it comes to pointers

  2. Entertain memory-safety for your existing C/C++ userspace, without rewriting everything in Rust

  3. Be able to further automatically compartmentalise your projects in a single AS seL4 protection domain

  4. Maintain C/C++ source-code backward compatibility

  5. Protect against dynamic zero-day vulnerabilities

  6. Be able to run completely memory-safe Linux/FreeBSD VMs

Secure C/C++ coding practices

Most spatial memory-safety bugs come from the fact that C/C++ allow obscure usage of pointers and confuse that with integers. Microsoft has indicated [10] that 70% of their vulnerabilities in their Windows system are memory-safety related, which could’ve been detected using CHERI. Building with CHERI alone gets rid of that attack surface (read: unintentional insecure programming practice). That is, if you build your program with a CHERI toolchain, address all CHERI-related warnings (e.g., conversions between pointers and integers), you’ll get some form of static memory safety guarantees, without even needing to run it on CHERI hardware. This could also simply be achieved by following CHERI C guidelines [6] while coding your applications (that will also work with non-CHERI builds). If you manage to run that on CHERI hardware, catch any runtime CHERI violations, and fix them; you’ll cover more runtime attack surfaces, even when you move the same code to non-CHERI processors. So, it’s a win-win; CHERI helps you detect your memory-safety bugs in your existing C/C++ code and trains you to use better coding practices when it comes to memory-safety.

No need to re-write your 100k LoC C/C++ project in Rust

Rust is great if you have experienced Rust programmers and seL4 experts at the same time. They could either develop completely new seL4-based Rust projects, or re-write everything in an existing C/C++ in Rust (with potentially introducing other bugs). But how many Rust and seL4 experts, in reality, are there? What if you don’t have the desire nor resources to do so for your existing 100k LoC seL4 user-space project, yet you want to enhance its (memory-safety) security? That’s where CHERI comes to use. For example, we’ve found out that, for a relatively cleanly-written C/C++ application code, there’s almost no effort at all required to make it build and run in CHERI, while entertaining runtime deterministic spatial memory safety. This is observed in projects such as Amazon’s OTA (>100k LoC) [2-3], and Busybox [4]. If that’s not convincing enough, David Chisnall has written up a great article on CHERI vs Rust: CHERI Myths: I don't need CHERI if I have safe languages [7].

Automatic single-AS software compartmentalisation

Figure source from the Arm Morello program [8].

Figure source from the Arm Morello program [8].

CHERI doesn’t only offer you memory safety, but also software compartmentalisation. You can think of software compartmentalisation as the process of taking a single monolithic software project, running in the same address space and same privilege ring, and try to split it up into smaller compartments in order to reduce the attack vector (e.g., if one compartment is vulnerable), as in the above figure. Now you could (simply?) do that with seL4’s processes and MMUs. But imagine if you’re porting a big monolithic library OS on top of seL4 such as rump kernels, in order to get a POSIX-based OS personality and TCP/IP stack. You’ll likely need to:

  1. Grasp the entire monolithic project to a good level

  2. Identify and draw security boundaries between logical compartments

  3. Manually port each compartment (code, data, threads, etc) to use seL4’s processes, objects, API

  4. Set up RPC-like calls using seL4’s secure IPC and/or shared memory

That’s all if it’s even feasible. How much maintenance, development, performance, and error-prone overheads would that introduce, especially if you don’t feel too confident developing secure policies using seL4?

Now what if I told you that one of CHERI-based compartmentalisation models such as CompartOS [2, 3] could offer you light-weight software compartmentalisation with very minimal effort, compared to the above, without needing to know much about seL4 and CHERI internals? We’ve shown that for a large project like Amazon’s AWS, with different third-party libraries from different vendors (e.g, AWS, Intel, Arm, FreeRTOS), this was feasible without code modifications at all. You just need to draw the security boundaries (in that case it was libraries), and an (optional custom) security policy between them (we used default linkage-based visibility). This was all done in a separate address-space, in a separate privilege ring, and allowed us to automatically compartmentalise a vulnerable FreeRTOS’ TCP/IP stack (e.g., CVE-2018-16526), protecting OTA’s secret keys at the application compartment, while being able to maintain the availability of the rest of the system. We even managed to partially restart the TCP/IP stack without having to restart the entire system. The following figure is generated from a real CHERI build of the AWS-OTA example.

Figure: Amazon’s Over-The-Air generated compartmentalised graph that is statically and automatically generated by CheriFreeRTOS during the build stage. Small boxes represent linkage-based library compartments, and the edges are the API references a compartment makes to another. Dotted edges indicate a trusted call to FreeRTOS without trampolines or domain switches. Compartments are placed into larger boxes to categorise them into core, third-party, and application libraries.

Protect against dynamic zero-day vulnerabilities

Software compartmentalisation draws security boundaries for a piece of software. The concept isn’t bound to CHERI or seL4, and it should be independent of the underlying fault types, and shouldn’t make assumptions about what kind of faults are going to be triggered. That is, it should always assume zero-day vulnerabilities exist, and allow further fault-triggering mechanisms to be added.

This could be manual compartmentalisation like in seL4’s threads with MMU, or automatic like with CHERI on libraries or single-AS threads. The fault triggering mechanism is orthogonal to software compartmentalisation. For instance, this could be CHERI’s memory-safety violations, MMU violations, software violations that trigger (software) exceptions, etc.

Be able to run completely memory-safe Linux/FreeBSD VMs

One of the most important selling points of seL4, besides formal verification, is the fact that it could be used as a type-1 hypervisor to provide isolation guarantees between VMs as well as native seL4 threads running side-by-side at the same time. Those VMs could be used to support massively rich drivers, services (e.g., TCP/IP, filesystems, VirtIO, etc), and OS personalities (e.g, POSIX) for existing seL4 threads and UNIX-based applications.

Now what would you do if you want to have memory safety within those VMs? Obviously no one is going to re-write Linux or FreeBSD in Rust. But with very minimal effort in CHERI-seL4, relatively, this could be achieved, and this is a big step towards having a completely memory safe software stack including firmware, hypervisor, microkernel, VMs, seL4 native threads, etc.

Yes, but it increases my application code size and incurs performance overhead

This is a long trade-off between security and performance. Adopting a new security mechanism isn’t free. Is formal verification free? Was moving software projects from non-secure states to use MMU-based processes free? Is moving to hypervisor-based isolation free? Is re-writing your project in Rust free? It is the same argument for supporting CHERI, but CHERI gives you way less effort and/or performance overhead compared to those technologies, at a great benefit of enforcing complete memory safety and software compartmentalisation. In fact, CHERI works best side-by-side with such technologies, it’s not a competition. The question is, does the security benefits it adds outweigh the effort and performance overhead it provides? I’d say absolutely yes, but I am, of course, biased. Same analogy goes for adding new 64-bit in your 32-bit systems that didn’t have 64-bit support before (though that’s not necessarily a security feature).

Current CHERI-seL4 project status

We currently have a fork of the seL4 kernel [9] that supports building and running the kernel on both hybrid and purecap CHERI modes, and passing all sel4tests in purecap mode. We also submitted an upstream PR to seL4 with that support. The next stages will include releasing the remaining ecosystem, including user libraries, sel4tests, sel4bench, ELF loader, OpenSBI, etc. This will come with documentation and tutorials on how to try it yourself and build your own memory-safe seL4 user applications with CHERI.

References

[1] Watson, Robert NM, et al. Capability hardware enhanced RISC instructions: CHERI instruction-set architecture (version 9). No. UCAM-CL-TR-987. University of Cambridge, Computer Laboratory, 2023.

[2] Almatary, Hesham, et al. "CompartOS: CHERI compartmentalization for embedded systems." arXiv preprint arXiv:2206.02852 (2022).

[3] Almatary, Hesham. CHERI compartmentalisation for embedded systems. Diss. 2022.

[4] Almatary, Hesham, Alfredo Mazzinghi, and Robert NM Watson. "Case Study: Securing MMU-less Linux Using CHERI." SE 2024-Companion. Gesellschaft für InformatikeV, 2024.

[5] CHERI Website: https://www.cl.cam.ac.uk/research/security/ctsrd/cheri/

[6] Watson, Robert NM, et al. Cheri c/c++ programming guide. No. UCAM-CL-TR-947. University of Cambridge, Computer Laboratory, 2020.

[7] https://cheriot.org/cheri/myths/2024/08/28/cheri-myths-safe-languages.html

[8] https://www.arm.com/architecture/cpu/morello

[9] https://github.com/CTSRD-CHERI/seL4

[10] https://www.zdnet.com/article/microsoft-70-percent-of-all-security-bugs-are-memory-safety-issues/