Slab and kmalloc: Object Reuse, Space Retention, and Lifecycle

On this page

Objects have been kfreed, but the system's free memory has not immediately increased. This phenomenon requires accounting for two ledgers: how many objects the caller still holds, and how many backing pages the allocator retains.

Starting with a 200-byte object, we first calculate internal slab overhead and spare capacity, then trace allocation, cross-CPU release, failure rollback, and address reuse.

Phenomenon to ExplainReading Location
Why does a 200-byte object occupy more space?Section 1: Objects, Slabs, and Full Pages
How to choose between kmalloc, cache, and kvmalloc?Section 2: Allocation and Release Contracts
When are pages returned after kfree?Sections 3–4: Caches and Ownership
How do evidence for leaks and UAF differ?Sections 5–6: Detection and Read-Only Statistics

1. Calculating Three Types of Space with a 200-Byte Object

Assume the business logic requires 200 bytes, the actual object stride of the used cache is 256 bytes, the backing page size is 4096 bytes, and we ignore out-of-page management metadata. One slab page can hold 16 slots; if all 16 slots are occupied, the effective business content is 3200 bytes, and the total internal alignment and other overheads amount to 896 bytes.

If only 10 objects are alive, the 4096 bytes of the same page should be accounted for as follows:

DestinationCalculationBytes
Business Content10 × 2002000
Used Slot Internal Overhead10 × (256 − 200)560
Reusable Empty Slots6 × 2561536
TotalOne backing page4096

The 2096 bytes excluding business content have specific purposes and cannot all be counted as leaks.

Why Can a Few Long-Lived Objects Retain Many Pages?

If two slabs each have only one object alive, totaling only 400 bytes of business content, two pages might still need to be retained. The allocator cannot arbitrarily move objects together because the caller holds pointers; arbitrary relocation would invalidate these pointers. Some subsystems can release objects through their own reclamation protocols, but this is different from the ability to defragment and migrate user pages.

Objects have been released, but the slabs backing them may still need to be retained Two slabs, each with one object remaining slab A slab B Assume objects are concentrated into one page slab A slab B Each cell represents a slot; regular slab objects cannot be arbitrarily moved. The row below illustrates how occupancy distribution affects the number of releasable pages.

This also explains why increasing the slab order has a dual effect: obtaining more object slots at once reduces the frequency of replenishing shared resources, but relies more heavily on high-order contiguous pages; a single long-lived object may also prevent larger backing blocks from being returned. Debugging redzones, allocation tracking, and alignment changes object strides, so enabling checks may alter space usage and allocation success rates. See Slab Management and Debugging.

Keeping the number of objects the same, switch between packed and spread layouts. Below, we account for content, internal slot overhead, empty slots, and backing pages separately using the same slot size; clicking on slots allows you to construct your own lifetime distribution.

In this model, there are slots per page, B, B, B; is the number of pages with still-living objects, and is the total number of living objects.

Preparing the visual
Why do four objects occupy four times the pages?

Effective content, internal overhead of used slots, and full-page retention are different quantities. A small number of scattered live objects can prevent multiple slabs from being returned; freeing a slot does not equal freeing a page.

2. What Do kmalloc and kmem_cache Decide for the Caller?

kmalloc selects the general allocation path based on request size and flags; small objects usually enter an appropriate size class; larger requests can directly rely on high-order pages, rather than requiring a named cache for every size. Size classes and grouping by purpose vary by architecture, configuration, and version; you cannot assume all machines have the same set of cache names.

A dedicated kmem_cache expresses fixed object layout, alignment, flags, and optional construction requirements. Similar caches can be merged if conditions are met, but conditions are not just size proximity; they also involve flags, alignment, constructors, and debug requirements; different names in sysfs may point to the same underlying cache. The reason for defining a dedicated cache should come from object usage and management needs, not just to get a nice statistical name.

Initialization Responsibility Remains with the Caller When Reusing Objects

Constructors are typically used for the initial establishment of new objects in the slab, not re-run every time an object is allocated to the caller. Fields written by the previous user cannot be expected to be automatically restored by the ctor on the next alloc. kmalloc/kmem_cache_alloc also do not guarantee zero initialization; use the appropriate interface or explicitly initialize fields when zeroing is needed, then publish the object to other threads.

When Choosing Interfaces, Continuity and Release Interfaces Are a Contract

Allocation MethodSuitable RequirementsRelease and Boundaries
kmalloc/kzallocStandard kernel buffers, physically contiguous allocation resultskfree; do not access beyond the requested effective range
kmalloc_array/kcallocArrays of element count times element sizeAllocation interface checking for multiplication overflow, still need to handle NULL
kmem_cache_allocFixed objects from a created cacheCorresponding kmem_cache_free, do not mix with other caches
kvmalloc/kvzallocLarger buffers where virtual continuity is acceptable, physical discontinuity is notkvfree; usage scenarios and GFP must meet fallback path requirements
vmallocVirtual continuous mapping of multiple backing pagesvfree; cannot be treated as a single-segment DMA physical buffer

The actual allocation size may be larger than the requested size, but tail space is not an implicit permission for business logic to use arbitrarily; if size-class rounding is needed, use the appropriate interface and adhere to the request/allocation contract. Whether a DMA device can access the buffer also goes through the DMA API; do not deduce that "kmalloc is contiguous" means any device can directly use its CPU address. See Memory Allocation Guide.

3. Completing an Allocation, Cross-CPU Release, and Page Return

When CPU 0 requests an object, if there is already a suitable empty slot locally, it can obtain it quickly. When there are no local objects, it needs to replenish from shared reserves or partial slabs; only when these are insufficient does it request backing pages from the page allocator and create more slots. Small object requests can also enter the page allocator slow path, so "only requesting 200 bytes" does not mean it cannot sleep or fail.

When an object is handed to CPU 1 for processing and release, it may not belong to the local slab/cache currently in use by CPU 1. The allocator needs to maintain information such as the owning slab, free count, and cache ownership, using atomic updates or locks when necessary; one cannot assume all frees are just writing two pointers to the head of the current CPU's list.

From object request to physical page: fast path relies on cache, slow path replenishes resources Object Allocation / Release kmalloc or dedicated cache CPU-side Object Cache Prioritize local reuse and batch processing Shared Resources and Partial Slabs Coordinate other object reserves when necessary Page Allocator Allocate / Return Backing Pages Illustrates cache hierarchy, does not fix a specific version's freelist/sheaf structure; debugging and NUMA constraints can alter the fast path.

Releasing an object merely returns its usage rights to the allocator. Only when certain batches of objects are emptied and the slab meets reclaimable conditions can the backing page be returned to the page allocator. Retaining a certain amount of empty slab or local object capacity accelerates the next round of allocations, but causes memory to not drop instantly after low load. Judging anomalies requires combining whether it continues to grow under stable load, the cache's design retention policy, and reference chains, rather than just comparing MemFree before and after a single kfree.

Source Code Reading: Cache Organization Varies by Version

Older versions of SLUB often explained the fast path using per-CPU active slab/freelist; the Linux 7.2 mm/slub.c checked this time has already used CPU-side sheaves object arrays and NUMA node barns for batch caching and swapping.

They both aim to reduce the frequency of shared resource operations, but synchronization, RT, and debug paths need to be read according to the target version. When learning interfaces like kmalloc, internal organization changes should be viewed separately.

4. On Failure, Half-Built Allocations Must Have Clear Ownership

Consider an initialization process that needs to create a cache, then allocate a pointer array, then allocate 32 objects. When the 19th object fails, the 18 previously successful objects still belong to the initializer; releasing only the array would lose their addresses, and destroying only the cache cannot replace releasing the live objects.

When the 19th object allocation fails, cleanup responsibility unfolds in reverse order of success:

Reversal OrderWhat to ReleaseWhy It Must Be Handled at This Step
1The 18 successfully allocated objectsThey still belong to the initializer, addresses cannot be lost
2Pointer arrayIndices are no longer needed only after all objects are released
3CacheDestroy the backing mechanism only after confirming no live objects remain

Complex drivers also need to stop entry points, background tasks, and asynchronous references first. The allocator knows if a slot is free, but does not know if external callbacks will still access it.

Experimental Code: Look at Rollback Order First, Then Each Error Label

Below is the initialization path for the complete teaching module. It allocates a small number of objects, verifies fields, and cleans up before returning; the code is used to demonstrate step-by-step rollback, compilation checks do not require loading the module.

#include <linux/errno.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>

struct kb_item {
    unsigned int id;
    unsigned char payload[196];
};

static int __init kb_alloc_init(void)
{
    struct kmem_cache *cache;
    struct kb_item **items;
    unsigned int made = 0;
    int result = -ENOMEM;

    cache = kmem_cache_create("kb_alloc_example", sizeof(struct kb_item),
                              0, 0, NULL);
    if (!cache)
        return -ENOMEM;
    items = kcalloc(32, sizeof(*items), GFP_KERNEL);
    if (!items)
        goto destroy_cache;
    while (made < 32) {
        struct kb_item *item = kmem_cache_zalloc(cache, GFP_KERNEL);
        if (!item)
            goto free_items;
        item->id = made;
        items[made++] = item;
    }
    result = 0;
    for (unsigned int i = 0; i < made; ++i) {
        if (items[i]->id != i || items[i]->payload[0] != 0) {
            result = -EINVAL;
            break;
        }
    }
free_items:
    while (made)
        kmem_cache_free(cache, items[--made]);
    kfree(items);
destroy_cache:
    kmem_cache_destroy(cache);
    return result;
}

static void __exit kb_alloc_exit(void) { }
module_init(kb_alloc_init);
module_exit(kb_alloc_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Slab allocation and rollback teaching example");

Kbuild can use obj-m += kb_alloc_example.o. Whether actual failure branches execute as expected requires separate controlled failure injection; compilation success alone cannot be treated as having run the 19th failure path. Do not "fix leaks" by destroying the cache while production objects are still referenced.

5. Post-Release Reuse Explains UAF and Different Evidence for Detection Tools

Assume object A is freed, and its original address is subsequently allocated to object B. A's old pointer still points to the same address, so an erroneous write might corrupt B rather than crash immediately. Filling freed memory with poison helps identify anomalies later, but cannot guarantee that every erroneous access is intercepted at the moment it occurs; allocation/release tracking may also discover corruption at later checkpoints.

Detection Tools: Illegal Access and Unreachable Allocations Are Two Types of Evidence

Different modes of KASAN use mechanisms such as shadow memory or tags to check for illegal access, with different costs, coverage, and granularity. The 1:8 shadow ratio of Generic KASAN is not the only overhead in the entire system; there may also be quarantine, alignment, and other metadata. Tag-based detection also cannot guarantee identifying all errors with the same tag or falling within the check granularity. See KASAN.

kmemleak mainly reports potential leaks by scanning whether suspect objects are still reachable from known roots. It is not a prover of all C pointer semantics: hidden or encoded pointers may cause false positives, and values that accidentally look like pointers may make leaks appear reachable. Therefore, it answers "which allocations are worth checking," while KASAN is more directly targeted at a specific illegal access. See kmemleak.

The reclamation responsibility for ordinary slab objects belongs to the subsystem using them. Subsystems with reclaimable cache semantics can register shrinkers to release objects no longer needed under pressure; simply marking a cache as a certain reclaimable purpose will not let the kernel automatically understand and destroy business objects still in use.

6. Read-Only Comparison of Object Size, Slot Stride, and Page Order

from pathlib import Path
import os

root = Path("/sys/kernel/slab/dentry")
if not root.exists():
    print("dentry slab sysfs not currently exposed")
else:
    print("Actual cache path:", root.resolve())
    values = {}
    for name in ("object_size", "slab_size", "order", "objects", "slabs"):
        try:
            values[name] = (root / name).read_text().strip()
        except OSError as error:
            values[name] = type(error).__name__
        print(name, values[name])
    if values["order"].isdigit():
        print("Calculated slab bytes based on current order:",
              os.sysconf("SC_PAGE_SIZE") * (1 << int(values["order"])))

When reading results, confirm units first:

FieldHow to Understand
object_sizeField related to object content size
slab_sizeField related to object slot size, not the total slab byte count
orderNeeds to be combined with base page size to calculate total backing space
objects, slabsMay include node distribution information, cannot directly convert entire line to integer

The actual order, debug mode, and fallback of existing slabs may require more detailed statistics. Current configuration values cannot reverse-engineer all historical allocations.

When combining with /proc/slabinfo, look at active objects, capacity, and backing pages separately. Statistics are also affected by local caches and implementation accounting methods; do not unconditionally treat active counts as the precise number of objects still held by business logic. Next, in Page Reclaim and Swap, we will place slab shrinkers alongside ordinary file pages and anonymous pages into the same pressure model.