PERFORMANCE Signal 528 2 feeds carried it
TigerStyle's static allocation and constant work patterns avoid pool use-after-free by pre-allocating all objects at startup
Illustration only Photo by Steve A Johnson on Unsplash
matklad discusses how object pools create a memory-safety blind spot analogous to tagged-union type confusion, and advocates two TigerStyle patterns, pre-allocating every object at startup and rejecting overflow rather than allocating dynamically, to eliminate use-after-free in systems like order-matching engines.
If you build systems with object pools, the type system does not track which generation of object occupies a slot, so a stale pointer can silently read bytes belonging to a different object. Pre-allocating a fixed maximum at startup and rejecting surplus requests trades a small loss of flexibility for a guarantee that overload degrades gracefully instead of triggering an OOM kill that loses every in-flight request.
Written by elseif from the cluster below · every claim links back to a sourceThe three things worth knowing
An object pool behaves like a tagged union whose tag is the current generation in each slot, and nothing in the type system tracks that tag, so use-after-free becomes silent aliasing rather than a caught error.
Type-segregated pools, where the allocator takes a compile-time type parameter instead of a runtime size, prevent most physical type confusion but cannot help when objects contain inline enums, and C's untyped allocator interface makes the technique unavailable there.
Static allocation pre-allocates a fixed maximum number of objects at startup and rejects overflow at runtime, so the system either starts with enough memory or fails immediately, never hitting the kernel's OOM killer mid-operation.
THE READ
What the cluster adds up to.
The discussion starts from a concrete bug: a limit-order matching engine shipped a use-after-free where a cancelled order was returned to the pool while still linked into its price level, so the next allocation handed that memory to a new order and the stale link kept resolving. The author initially filed this under carelessness with lifetimes, but reframes it after recognizing that a recycling pool is effectively a tagged union where the tag is which generation of object currently lives in a slot, and nothing in the type system tracks that tag.
The severity of pool-based use-after-free depends on whether type confusion occurs. With raw malloc and free, two objects of different types sharing the same memory location can turn a user-controlled integer in one object into a function pointer in another, yielding an exploitable primitive. With a type-homogeneous pool, the aliasing is still logically wrong but the physical behavior is deterministic and defined, no type confusion, unless the object stores an inline enum, which reintroduces the problem. Heap-allocating enum variants separately restores the safety property, but C's untyped allocator interface prevents this approach in Fil-C.
The first TigerStyle pattern, static allocation, takes the pool idea to its logical conclusion: specify a maximum number of objects at startup, allocate them all once, and never allocate again. If runtime demand exceeds the limit, surplus requests are rejected. The argument is that a system operating at capacity without strict limits fails catastrophically, an attempt to allocate one more object could trigger the kernel's OOM killer, terminating the entire process and losing every in-flight order, or killing the supervisor so the system cannot even restart. Static allocation means the system either starts with enough memory or fails immediately, and then handles overload by shedding load rather than crashing.
The second pattern, constant work, addresses what to do with the pre-allocated slice: track free objects with a bit set rather than a dynamic structure, keeping per-acquire and per-release work bounded. The author notes they have only a vague understanding of order-matching engines but suspects these patterns fit that domain, where predictable latency under load matters as much as correctness. The cost of both patterns is a fixed memory ceiling and the rejection of requests beyond it; the benefit is the elimination of an entire class of use-after-free bugs and a graceful-degradation guarantee under overload.
Written by elseif from the cluster below · checked for specifics the sources never containedTHE CLUSTER