Why Object Placement Is a Security Boundary

This chapter uses the allocator model behind Fence2Pwn to answer a focused question: why does the location and reuse of a kernel object matter to security, rather than only to performance? The answer lies in the division between the page allocator and the slab allocator, and in the way hardened slab caches restrict which objects can meet in a reclaimed location. Once that model is clear, KFENCE’s shared pool can be understood as a change in the rules governing who owns and reuses an object slot.

Two Allocators, Two Kinds of Placement

The Linux kernel needs memory at very different scales. Some operations need whole physical pages or groups of pages. Others need a small object—a credential record, a message structure, a buffer descriptor, or a piece of metadata—without paying the cost of treating that object as an entire page. Linux’s page allocator supplies the coarse-grained foundation. The slab allocator builds a finer-grained service above it, dividing memory into areas from which many similarly sized objects can be served.

That layering gives each allocator a different responsibility. The page allocator is concerned with physical memory units and page-level reuse. The slab allocator is concerned with turning those units into a fast stream of objects. A request made by a kernel subsystem generally names an object need rather than a physical address. The slab layer decides which cache can satisfy that need, obtains backing memory when necessary, and tracks which object locations are available. The requesting subsystem owns the object’s meaning; the allocator owns the storage decision and its immediate lifecycle.

A slab cache is best understood as a controlled neighborhood. Objects in a cache share size constraints, and the cache’s association with an allocation context or object class limits the kinds of occupants likely to appear there. The term “type” in this setting does not require a programming-language type check at every access. It refers to the kernel’s allocation categories and the allocator structures that keep objects with different security or size properties apart. The important fact is that a freed location is not normally an invitation to every possible object in the system.

This organization serves performance first. Reusing a prepared cache avoids repeatedly arranging page-level memory for every small request. But the same organization changes the consequences of corruption. If an object has a stale reference after it is freed, the next occupant of that location matters. A replacement from the same cache may have similar size and layout. A replacement from a cache holding security-sensitive data may expose a more valuable set of fields. Separating those populations therefore reduces not only fragmentation and bookkeeping cost, but also the range of objects that can be reached through a damaged reference.

It is useful to distinguish two broad forms of reuse. In-cache reuse occurs when a later allocation is served from the same cache that held the freed object. The size and allocation category already line up, so the allocator’s normal behavior can make the replacement relatively plausible. Cross-cache-style reuse seeks to cross that boundary: an object from a different cache, often with different size or security significance, ends up occupying memory associated with the first object. The paper treats this capability as powerful because it reaches targets that cache segregation is intended to isolate.

A memory-safety bug does not automatically grant either capability. A use-after-free gives the program a reference to storage whose ownership has ended, but the attacker still needs a suitable successor object at the relevant location. An out-of-bounds write can damage adjacent data, but the value of that damage depends on what is adjacent and how the allocator placed it. Placement is the bridge between a raw memory error and a useful target. Hardening aims to make that bridge narrow, unpredictable, or absent.

The distinction also clarifies why a kernel exploit is usually described as a progression rather than a single act. The paper’s background divides exploitation into establishing a read primitive, establishing a write primitive, and triggering an event that turns the altered state into a security consequence. The allocator affects all three stages. Object placement may determine whether a corrupted pointer can reveal a useful address, whether the stale reference can reach a field that changes control or credentials, and whether the final modified object is accepted by the kernel at all.

This is why “the kernel has a heap” is too coarse a model. There is a hierarchy of ownership and constraints. Pages back slabs. Slabs organize caches. Caches restrict object populations. Subsystems create and release objects according to their own lifecycles. Security hardening adds further constraints to the allocator’s decisions. A new allocation path can preserve the same external API while changing one or more of those internal relationships.

Why Segregation Changes the Attack Surface

Modern Linux hardening tries to make useful reuse harder in several complementary ways. Some measures separate objects by size or allocation context. User-controlled sizes can be placed in distinct buckets so that an attacker cannot as easily request data shaped to match a sensitive object. Randomized kmalloc caches make the mapping from an allocation site to a particular cache less predictable. Freelist randomization and pointer protection make the order and metadata of available objects more difficult to infer or corrupt. A virtualized-heap approach such as the one discussed in the paper aims at an even stronger property: a location used for one object category should not simply be reused for another category.

These measures do not all solve the same problem. Randomization creates uncertainty; segregation limits the set of possible neighbors; metadata protection makes allocator manipulation harder; address-space rules can prevent a cross-cache replacement from having the expected identity. Their common assumption is broader: the allocator path being used will honor the constraints. If an object reaches a pool that does not maintain the same boundaries, the protection may remain strong on the ordinary path while failing to describe the whole system.

The threat model in Fence2Pwn makes this point against a hardened target rather than an empty baseline. The paper assumes protections such as kernel address-space layout randomization, restrictions on executing or accessing user memory from the kernel, control-flow integrity, freelist hardening and randomization, bucket segregation, randomized kmalloc caches, and kernel-heap virtualization. The authors also assume a known use-after-free and an unprivileged process able to trigger native-code kernel interfaces. The experiment therefore asks whether one allocation route can reopen a capability that these measures otherwise constrain; it does not assume that ordinary slab allocation is undefended.

The security role of segregation is easiest to see through a freed slot. Before free, the slot has an owner: a particular cache and an object whose lifecycle is still active. At free, the original owner gives up the storage, but the allocator does not necessarily make it globally available without structure. It records the location in a cache-specific pool or freelist. A later request must pass through the allocator’s selection rules before it can become the next occupant. The more those rules preserve size, type, address, and metadata boundaries, the less freedom a stale reference has.

The page allocator complicates this picture in an important way. Since slabs ultimately depend on pages, lower-level page reuse can sometimes allow memory formerly associated with one cache to back another. That is the basis for cross-cache techniques: manipulate or wait for page-level conditions so a different cache can obtain the relevant memory. Modern defenses have specifically tried to limit this route. The Fence2Pwn paper’s claim is not that page-level reuse disappeared from theory, but that moving allocations onto a different, globally shared pool can make the same capability available without asking the hardened slab path to violate its own cache rules.

The following model keeps the interfaces visible. A subsystem requests an object; the slab layer normally selects a cache; the page allocator supplies backing memory when the slab needs it. The cache owns the immediate free-list decision for its objects. A cross-cache-style path changes the layer at which the replacement is chosen, broadening the possible relationship between the old occupant and the new one.

The boundary is crossed twice during an object’s life. On allocation, the request crosses from a subsystem into allocator policy: the caller specifies a size or cache context, but it does not ordinarily choose a physical location. On free, ownership moves back toward the allocator, which decides whether the location is immediately eligible, retained for protection, or placed on a reuse structure. A stale reference sits outside that contract. It still contains an address, but the allocator no longer promises that the address denotes the original object—or any object with the same shape.

That loss of meaning is why object layout matters. A stale reference may interpret bytes according to the fields of the object that used to be there. If a replacement has a compatible layout, an access may remain within mapped memory while changing a field in the replacement. If the replacement is smaller, differently aligned, or governed by another set of invariants, the same access may fail, be detected, or affect a field with an entirely different consequence. Hardening benefits from keeping those interpretations apart even when the underlying bytes are ultimately backed by the same pages.

The page allocator and slab allocator also have different views of reuse. At page level, memory is a resource that can be returned and reassigned. At slab level, that resource is partitioned into objects with cache-specific rules. A page can therefore be physically reusable without every object category being logically interchangeable. Cross-cache defenses try to preserve that distinction as memory moves between layers. The security question raised by KFENCE is whether its own pool maintains an equivalent logical boundary when it accepts objects that ordinary caches would keep apart.

This is also why the paper’s hardening list should be read as a set of constraints, not as a guarantee that one feature solves the problem. Randomization can make allocator state harder to predict, but it does not by itself prevent a collision. Segregation can prevent a class of objects from being neighbors, but it does not address every invalid access. Metadata protection can preserve allocator bookkeeping while leaving an object-level bug unresolved. A complete security argument must ask which constraint applies at the point where the vulnerable object is allocated, freed, and replaced.

flowchart LR P[Page allocator supplies backing pages] --> S[Slab allocator organizes object caches] S --> C1[Cache for one size or object class] S --> C2[Cache for another size or object class] C1 --> F[Freed object slot] F --> I[In-cache replacement] F --> X[Cross-cache-style replacement] C2 --> X H[Segregation and randomization constrain placement] --> C1 H --> C2 H --> F

The diagram is not a map of every Linux allocator feature. It is a security model: the page allocator provides the substrate, the slab layer creates neighborhoods, and hardening tries to preserve the meaning of those neighborhoods when objects are freed and replaced. “Cross-cache-style” describes the security-relevant relationship between the old and new objects; it does not require that every implementation detail be identical across kernels.

A useful analogy is a set of controlled rooms, but the allocator is more exact than a building. The rooms have different capacities, admission rules, and records of who may enter next. A stale reference is like a person holding an old access token for a room whose previous occupant has left. If the replacement must come from the same room, the token may point to an object with familiar shape. If every room shares one unstructured queue, the token’s meaning changes. The danger is not simply that two objects use the same bytes; it is that the allocator has weakened the relationship between a location and the object class expected there.

This does not mean segregation is an absolute guarantee. Allocation patterns, concurrency, page reuse, and configuration can produce complex behavior, and the paper’s own threat model includes techniques designed to manipulate heap state. But a defense need not make a capability mathematically impossible to be valuable. It can lower the probability of a useful collision, force an attacker to cross another boundary, or make the resulting object incompatible with the stale reference. Fence2Pwn becomes important because it targets the boundary itself.

KFENCE is the relevant alternate route. Most requests remain under the ordinary cache rules, but sampled requests can be served from KFENCE’s own pool. That pool is designed around guarded pages, sampling, and error diagnosis. The question is not yet whether those mechanisms detect an invalid access; the question is whether the pool preserves the same placement and segregation assumptions that make ordinary slab hardening meaningful. A shared pool can be excellent at keeping one object away from neighboring bytes while still being less restrictive about which kind of object arrives next.

That distinction gives the next problem a precise shape. The ordinary slab path treats object placement as part of a security boundary. KFENCE treats sampled placement as part of a detection strategy. If both paths serve the same kernel request, their ownership, state transitions, and reuse policies must be compared rather than assumed equivalent. The important question is therefore no longer whether the kernel has hardening, but which allocator path is carrying the object when the hardening is expected to apply.

References