The Difference Between Detecting an Error and Blocking Its Consequences

This chapter connects the ordinary slab path, KFENCE’s sampled pool, and KASAN’s access checks to answer the judgment Fence2Pwn requires: which protections follow an object when the allocation path changes? The useful conclusion is not that one tool is “secure” and another is “insecure.” It is that placement, detection, access enforcement, and failure handling are separate guarantees. A reader who keeps them separate can identify the five path-specific weaknesses without assuming that a detected error has already been prevented from becoming consequential.

One Request, Unequal Guarantees

A kernel subsystem can make what appears to be one ordinary allocation request, yet the request can end in two materially different places. If the normal slab path serves it, the object belongs to a cache organized around size and allocation category. The cache’s placement rules and hardening measures constrain the possible neighbors and future occupants. If KFENCE serves it, the object belongs to a separate pool with one object per page, guard pages, diagnostic metadata, and its own slot lifecycle.

Both outcomes can be described as “an object was allocated,” but that description hides the security-relevant state. The ordinary path carries the cache’s assumptions: objects of certain sizes and categories are kept together or apart, and the allocator’s hardening tries to make reuse difficult to predict or cross. The KFENCE path carries a different set of assumptions: the object is isolated so invalid access can be observed, the page can be protected after free, and the pool can retain evidence long enough to report the failure.

Those guarantees address different questions. Cache segregation asks which other object can occupy a location. A guard page asks whether an access crosses a protected page boundary. A poison pattern asks whether bytes that should have remained untouched were modified. A sanitizer asks whether the pointer and memory state are compatible at the moment of access. An error policy asks whether the kernel reports, faults, or halts after a violation. No one answer subsumes the others.

The difference can be expressed as a path-sensitive model:

flowchart LR R[Same kernel allocation request] --> D{Allocator path decision} D --> S[Slab-served object] D --> K[KFENCE-served object] S --> S1[Size and type segregation] S --> S2[KASAN shadow or tag checks when configured] K --> K1[Shared guarded pool] K --> K2[KFENCE diagnostics] K --> K3[Different coverage of segregation and tagging] S1 --> G[Path-specific security guarantees] S2 --> G K1 --> G K2 --> G K3 --> G G --> Q[Can an invalid reference reach a useful replacement?]

The model does not say that a KFENCE object has no protection. It says the protection has a different shape. A dedicated object page surrounded by guard pages is a strong spatial boundary between that page and its neighbors. It is not automatically a type boundary between the object that was freed and the object that later reclaims the slot. Likewise, a diagnostic report can establish that memory was misused without guaranteeing that the misuse was stopped before the surrounding kernel continued.

This distinction matters for security because exploitation is about consequences, not merely detection. Suppose a stale reference remains after a use-after-free. Whether it becomes useful depends on the next occupant’s layout, the validity of the address, the interpretation of the fields, and the checks that apply when the stale reference is used. A detector can record the event. An allocator boundary can prevent a valuable object from arriving at the location. A tag check can reject a pointer whose identity no longer matches the memory. These interventions happen at different points in the object’s life.