【Paper】SeaK: Rethinking the Design of a Secure Allocator for OS Kernel

In recent years, with the increasing number of attacks targeting heap bugs in the Linux kernel, Linux has adopted patches to fix or prevent these attacks. Nevertheless, these patches are either too specific to certain exploits or have prohibitively high overhead. This paper offers a different perspective on this problem.

SeaK

Overview of AA in SeaK

The authors of the paper state that the root cause of high performance overhead from protection measures is that the kernel tries to protect all data structures. However, given that the sensitive data structures that need protection are relatively few, it's enough to protect only those structures.

The authors proposed SeaK, which protects sensitive data structures by changing the behavior of the functions that allocate or free those structures.

The measures for protecting kernel memory can be categorized into three types:

  1. Enabled by default: Includes free-list randomization, free-list obfuscation, and heap-zeroing. However, these are specific to certain exploits and cannot prevent attacks like use-after-free.
  2. Disabled by default: For example, KFENCE adds guard pages to certain structures, but it consumes a lot of memory. In practice, KFENCE randomly adds guard pages to only some (< 1%) of the structures, so it's not very effective.
  3. slub-debug: It can detect memory access errors with low overhead for debugging, but that overhead is unacceptable for protection.

The following chart presents the performance overhead of these three different types of protections. Their performance is negatively correlated with their protective effectiveness.

Overhead of Linux measures

The main idea of SeaK is to apply the strongest protections to a few sensitive data structures while retaining good performance. SeaK inserts eBPF programs at the allocation and free sites of these sensitive structures to manage their memory. The protection measurements SeaK applies include guard pages and offset randomization, and more features can be added in the future.

When a system administrator identifies the target structure of an exploit, they can first hot-patch the kernel with SeaK. When an official patch is pushed to the kernel, they can then replace SeaK with it.

Of course, the performance and memory overhead is low. The results show that the overhead is around 2% at most, since it protects only a subset of kernel data structures.

Ideas

Excellent Perspective

Many studies focus on reaching the best protection without substantial performance loss, but this paper reaches the goal from an orthogonal perspective: protection range. I personally consider research of this kind to be so cool, and I want to do something similar in the future.

Validity of Performance Benchmarks

Although the performance overhead is very low according to the paper, the benchmarks were conducted in a way where SeaK only protected one structure, like struct seq_operations. Take simple syscall in LMbench, for example: seq_operations is not necessarily allocated or freed during every system call. So I doubt the validity of these performance benchmarks.

Validity of Scalability

The paper tested the scalability of SeaK for protecting many structures, using one AA for each structure. The same argument as before applies, are 64 AAs a lot or a little? Are 64 AAs enough to protect the whole kernel? It's not mentioned in the paper. The good performance is meaningless if the kernel is not properly protected.

Scalability benchmark