How KFENCE Works When the Kernel Is Live
This chapter follows KFENCE’s live allocation lifecycle to answer a specific question: what changes when an ordinary kernel allocation is selected for sampled detection? The answer is not simply that guard pages appear. A request passes through a sampling decision, enters a separately managed pool, acquires a protected object page, and later follows its own free and reuse rules. Tracing those transitions makes it possible to distinguish what KFENCE isolates for diagnosis from the cache segregation that protects the ordinary slab heap.
From an Ordinary Request to a Guarded Object
KFENCE is designed to be present in a production kernel without imposing the cost of checking every allocation and access. The kernel therefore continues to use its normal allocation machinery for most requests. At intervals determined by KFENCE’s sampling configuration, the next suitable request can be diverted into the KFENCE pool. The sampled path is a detour inside the main allocation interface: the subsystem asks for an object in the same general way, but KFENCE may satisfy the request before ordinary slab service completes.
This is a selection event, not a permanent mode for a subsystem. The sampling gate opens according to a timer or equivalent scheduling mechanism, and a candidate allocation is considered while the gate is open. The documentation describes a default in which one heap allocation is attempted per sampling interval; burst settings can extend that behavior. The paper reports a 100-millisecond cadence in the desktop configurations it measured and a 500-millisecond cadence for the Android configuration it evaluated. Those values belong to the examined configurations, not to every kernel build.
Eligibility narrows the set of requests that can take the detour. The documented path supports allocations up to PAGE_SIZE, requires suitable memory-zone conditions, and can exclude caches or allocation sources that are not meant to use KFENCE. The pool must also have capacity. Under pressure, KFENCE limits repeated long-lived allocations from the same source so that one busy code path does not occupy the entire pool. These checks mean that “the gate is open” does not imply “this particular request becomes a KFENCE object.” The decision is the combination of timing, request properties, pool state, and source history.
When the request is accepted, KFENCE takes ownership of a free pool slot. The slot consists of an object page and neighboring guard pages. The object page contains one object rather than a collection of ordinary cache objects. The surrounding pages are protected so an access that crosses from the object into a guard page causes a fault. Within the object page, unused bytes are filled with a canary or poison pattern so that corruption on the unguarded side can be recognized later.
The object can be placed at either end of its page, subject to alignment. That choice matters because the object needs room for its requested layout while preserving a redzone on the other side. A page-sized allocation removes much of this placement choice because it occupies the relevant page more fully. A smaller allocation can have different offsets depending on which side is selected. This is an implementation detail with a later security consequence, but its immediate purpose is to let KFENCE detect invalid accesses without arranging a conventional multi-object slab.
KFENCE then initializes the object or invokes the relevant object-construction behavior before returning it to the caller. From the subsystem’s perspective, the result is an object pointer. From KFENCE’s perspective, the pool now owns a live slot whose address, requested size, allocation source, placement, and diagnostic metadata must remain associated. That association is what lets a later fault be described as an error involving a particular allocation rather than as an anonymous page fault.
The lifecycle can be represented as a set of state transitions. The important triggers are the sampling gate, eligibility and capacity checks, allocation and free operations, the expiry of protection, and the utilization threshold. Each transition changes what can happen to the memory and who controls its next state.
The diagram includes two outcomes that are easy to conflate. A request that is not eligible or that arrives when the pool cannot serve it continues through the ordinary slab path. A request that is accepted becomes an active KFENCE object. KFENCE is therefore not a replacement for slab allocation; it is an occasional alternate owner of the object’s storage. The path decision is part of the object’s history even if the calling subsystem does not expose it.
The sampling interval also should not be read as a promise about the spacing of all allocations. It describes when KFENCE attempts to make a guarded allocation, while the kernel may perform many ordinary allocations between two sampled events. A busy system can create many opportunities around the gate, and several callers may compete for the one opportunity that satisfies the eligibility checks. The resulting behavior is global and workload-dependent. That is why KFENCE’s production coverage is statistical: over enough activity and uptime, more code paths may be sampled, but any particular object is not continuously watched.
Guard pages make the sampled object spatially distinctive. An out-of-bounds access that reaches a protected neighboring page can be trapped immediately by the memory-management hardware and handled by KFENCE. The detector can report the access, its direction, and the object’s allocation information. The Linux documentation also describes pattern-based redzones for the part of the object page not occupied by the object. Those bytes cannot rely on a guard page on every side, so their integrity is checked when the object is released.
There is a corresponding distinction between an access that crosses a page boundary and one that merely changes bytes inside the object page’s poison region. The first can trigger a page fault at the moment of access. The second may leave no immediate architectural signal, because the page is still present and accessible; KFENCE discovers the discrepancy when it later validates the page on free. The two mechanisms provide different temporal guarantees. A report at free can still identify a corruption, but it does not mean the invalid write was stopped at the instant it happened.
The error-handling policy determines what follows a fault. KFENCE can report an error and continue, or be configured to produce a more severe kernel response. The documentation lists reporting, oops, and panic behavior. The paper observes that production systems it studied used the non-fatal logging mode, because a mechanism intended for high-uptime deployments generally favors recording a problem over stopping the entire kernel. That is a rational diagnostic choice, but it means a detected access does not necessarily end the object’s surrounding execution immediately. The policy is therefore part of the lifecycle: a fault can terminate the transition, or it can produce a diagnostic state while the surrounding kernel continues.
What Persists After Free
Freeing a KFENCE object is not the same as returning an ordinary slab object to an immediately reusable cache. KFENCE first checks that the address corresponds to the metadata for the slot being released. This gives it a way to identify invalid frees such as repeated attempts to release the same object. It then checks the canary or poison pattern on the object page. A write into a redzone may therefore remain undiagnosed until this point, provided the page has not already been accessed in a way that triggers another check.
After those checks, KFENCE marks the object as freed and protects the page again. The protection is the mechanism that makes a later use-after-free visible: the stale reference still contains an address, but an access to the protected page produces a fault that KFENCE can report. The page remains in this protected state until the slot is allocated again. In that interval, the old object’s memory is not simply an unowned, ordinary heap location waiting for the next cache-local request.
The freed slot is appended to the tail of a FIFO freelist. Future KFENCE allocations take available slots from the head. This gives the pool a deliberate residence time for freed objects. A recently freed object waits behind other available entries rather than being immediately recycled. The documentation presents this as a way to increase the chance of detecting use-after-free accesses that occur soon after free. The same ordering also makes the history of the pool relevant to the timing of later reuse.
FIFO ordering is a policy for the pool, not a claim about all Linux heap allocation. Ordinary slab caches have their own reuse structures and hardening features. KFENCE’s queue governs only slots in the KFENCE-managed region. That distinction matters because a protected page can provide strong spatial and temporal diagnostics inside the pool while the pool’s decision about which kind of object uses a slot may follow different rules from the slab caches described earlier.
Pool utilization adds another state transition. The documentation says that when utilization reaches a configured threshold—75 percent by default—KFENCE limits currently covered allocations from the same source from filling the pool further. The purpose is to preserve diverse coverage and avoid long-lived allocation patterns exhausting the fixed pool. The paper analyzes this as a pressure regime: requests from a call source that already has an active KFENCE object may stop being sampled, making the change in behavior observable.
The pressure rule is a coverage policy with two effects. Operationally, it protects KFENCE from being monopolized by a frequent, long-lived allocation source. Analytically, it creates a recognizable relation between pool occupancy and which requests can enter. A system below the threshold can admit more sampled objects from a source; a system in the pressure regime may reject further objects from that source even while other slots remain in circulation. Pool state therefore influences future allocation decisions rather than merely recording past errors.
Long-lived systems can develop uneven slot histories. The paper’s measurements found that activity can concentrate in a small subset of slots while many others change state rarely, especially on systems with substantial uptime. That observation does not mean every slot becomes predictable or that the pool reaches full utilization. It means the fixed pool has persistence: some allocations remain active for a long time, some freed objects wait for reuse, and ordinary system activity can cause the available portion to change gradually.
The difference between KFENCE’s isolation and slab segregation is now precise. A guard page isolates one object page from adjacent pages. A poison pattern records corruption in otherwise unused bytes. Protection after free preserves evidence of a stale access. None of those facts, by themselves, says that every future KFENCE occupant must have the same size, type, or allocation context as the object that previously used the slot. Spatial isolation answers “what happens if this object crosses its page boundary?” Cache segregation answers “which class of object can arrive at this location next?”
That separation is not a defect in KFENCE’s stated diagnostic goal. A single-object page surrounded by guard pages is a sensible way to catch memory errors at low sampling frequency. Requiring every diagnostic slot to reproduce the full set of slab-cache boundaries could increase memory use and complicate coverage. The design trade-off is that the pool optimizes for observing invalid access and preserving diagnostic information, while the ordinary slab allocator also carries assumptions about object populations and controlled reuse.
This is the point at which the lifecycle becomes security-relevant. A sampled object begins as an ordinary request that crosses a different ownership boundary. It becomes active in a guarded page, then freed and protected, then eventually available through a pool-specific reuse order. The detector is doing what it was designed to do: creating a durable, inspectable opportunity to catch an error. But the same durable state and alternate reuse policy must be compared with the protections that apply when the request stays in its original slab cache. KFENCE’s value as a detector does not settle that comparison; it makes the comparison necessary.