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 Explain
Reading 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:
Destination
Calculation
Bytes
Business Content
10 × 200
2000
Used Slot Internal Overhead
10 × (256 − 200)
560
Reusable Empty Slots
6 × 256
1536
Total
One backing page
4096
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.
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.
SP=Nq+N(s−q)+(Sn−N)s,n=P/s
In this model, there are n=16 slots per page, P=4096 B, q=200 B, s=256 B; S is the number of pages with still-living objects, and N 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.
{"id":"slab-retained-pages","title":"Why do four objects occupy four times the pages?","summary":"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.","height":1000,"html":"<h2 data-i18n=\"heading\"></h2><p class=\"intro\" data-i18n=\"intro\"></p><div class=\"control\"><label for=\"count\" data-i18n=\"count\"></label><output for=\"count\" id=\"count-value\"></output><input id=\"count\" type=\"range\" min=\"0\" max=\"64\" value=\"4\" step=\"1\"></div><div class=\"presets\" role=\"group\" data-i18n-label=\"layout\"><button data-mode=\"packed\" data-i18n=\"packed\"></button><button data-mode=\"spread\" data-i18n=\"spread\"></button></div><div class=\"slabs\"><section class=\"card slab\" id=\"slab-0\"><header>slab 0</header><div class=\"slots\"><button data-slot=\"0\" aria-label=\"slab 0, slot 0\"><i></i></button><button data-slot=\"1\" aria-label=\"slab 0, slot 1\"><i></i></button><button data-slot=\"2\" aria-label=\"slab 0, slot 2\"><i></i></button><button data-slot=\"3\" aria-label=\"slab 0, slot 3\"><i></i></button><button data-slot=\"4\" aria-label=\"slab 0, slot 4\"><i></i></button><button data-slot=\"5\" aria-label=\"slab 0, slot 5\"><i></i></button><button data-slot=\"6\" aria-label=\"slab 0, slot 6\"><i></i></button><button data-slot=\"7\" aria-label=\"slab 0, slot 7\"><i></i></button><button data-slot=\"8\" aria-label=\"slab 0, slot 8\"><i></i></button><button data-slot=\"9\" aria-label=\"slab 0, slot 9\"><i></i></button><button data-slot=\"10\" aria-label=\"slab 0, slot 10\"><i></i></button><button data-slot=\"11\" aria-label=\"slab 0, slot 11\"><i></i></button><button data-slot=\"12\" aria-label=\"slab 0, slot 12\"><i></i></button><button data-slot=\"13\" aria-label=\"slab 0, slot 13\"><i></i></button><button data-slot=\"14\" aria-label=\"slab 0, slot 14\"><i></i></button><button data-slot=\"15\" aria-label=\"slab 0, slot 15\"><i></i></button></div><small id=\"live-0\"></small></section><section class=\"card slab\" id=\"slab-1\"><header>slab 1</header><div class=\"slots\"><button data-slot=\"16\" aria-label=\"slab 1, slot 0\"><i></i></button><button data-slot=\"17\" aria-label=\"slab 1, slot 1\"><i></i></button><button data-slot=\"18\" aria-label=\"slab 1, slot 2\"><i></i></button><button data-slot=\"19\" aria-label=\"slab 1, slot 3\"><i></i></button><button data-slot=\"20\" aria-label=\"slab 1, slot 4\"><i></i></button><button data-slot=\"21\" aria-label=\"slab 1, slot 5\"><i></i></button><button data-slot=\"22\" aria-label=\"slab 1, slot 6\"><i></i></button><button data-slot=\"23\" aria-label=\"slab 1, slot 7\"><i></i></button><button data-slot=\"24\" aria-label=\"slab 1, slot 8\"><i></i></button><button data-slot=\"25\" aria-label=\"slab 1, slot 9\"><i></i></button><button data-slot=\"26\" aria-label=\"slab 1, slot 10\"><i></i></button><button data-slot=\"27\" aria-label=\"slab 1, slot 11\"><i></i></button><button data-slot=\"28\" aria-label=\"slab 1, slot 12\"><i></i></button><button data-slot=\"29\" aria-label=\"slab 1, slot 13\"><i></i></button><button data-slot=\"30\" aria-label=\"slab 1, slot 14\"><i></i></button><button data-slot=\"31\" aria-label=\"slab 1, slot 15\"><i></i></button></div><small id=\"live-1\"></small></section><section class=\"card slab\" id=\"slab-2\"><header>slab 2</header><div class=\"slots\"><button data-slot=\"32\" aria-label=\"slab 2, slot 0\"><i></i></button><button data-slot=\"33\" aria-label=\"slab 2, slot 1\"><i></i></button><button data-slot=\"34\" aria-label=\"slab 2, slot 2\"><i></i></button><button data-slot=\"35\" aria-label=\"slab 2, slot 3\"><i></i></button><button data-slot=\"36\" aria-label=\"slab 2, slot 4\"><i></i></button><button data-slot=\"37\" aria-label=\"slab 2, slot 5\"><i></i></button><button data-slot=\"38\" aria-label=\"slab 2, slot 6\"><i></i></button><button data-slot=\"39\" aria-label=\"slab 2, slot 7\"><i></i></button><button data-slot=\"40\" aria-label=\"slab 2, slot 8\"><i></i></button><button data-slot=\"41\" aria-label=\"slab 2, slot 9\"><i></i></button><button data-slot=\"42\" aria-label=\"slab 2, slot 10\"><i></i></button><button data-slot=\"43\" aria-label=\"slab 2, slot 11\"><i></i></button><button data-slot=\"44\" aria-label=\"slab 2, slot 12\"><i></i></button><button data-slot=\"45\" aria-label=\"slab 2, slot 13\"><i></i></button><button data-slot=\"46\" aria-label=\"slab 2, slot 14\"><i></i></button><button data-slot=\"47\" aria-label=\"slab 2, slot 15\"><i></i></button></div><small id=\"live-2\"></small></section><section class=\"card slab\" id=\"slab-3\"><header>slab 3</header><div class=\"slots\"><button data-slot=\"48\" aria-label=\"slab 3, slot 0\"><i></i></button><button data-slot=\"49\" aria-label=\"slab 3, slot 1\"><i></i></button><button data-slot=\"50\" aria-label=\"slab 3, slot 2\"><i></i></button><button data-slot=\"51\" aria-label=\"slab 3, slot 3\"><i></i></button><button data-slot=\"52\" aria-label=\"slab 3, slot 4\"><i></i></button><button data-slot=\"53\" aria-label=\"slab 3, slot 5\"><i></i></button><button data-slot=\"54\" aria-label=\"slab 3, slot 6\"><i></i></button><button data-slot=\"55\" aria-label=\"slab 3, slot 7\"><i></i></button><button data-slot=\"56\" aria-label=\"slab 3, slot 8\"><i></i></button><button data-slot=\"57\" aria-label=\"slab 3, slot 9\"><i></i></button><button data-slot=\"58\" aria-label=\"slab 3, slot 10\"><i></i></button><button data-slot=\"59\" aria-label=\"slab 3, slot 11\"><i></i></button><button data-slot=\"60\" aria-label=\"slab 3, slot 12\"><i></i></button><button data-slot=\"61\" aria-label=\"slab 3, slot 13\"><i></i></button><button data-slot=\"62\" aria-label=\"slab 3, slot 14\"><i></i></button><button data-slot=\"63\" aria-label=\"slab 3, slot 15\"><i></i></button></div><small id=\"live-3\"></small></section></div><div class=\"legend\"><span data-i18n=\"payload\"></span><span class=\"second\" data-i18n=\"padding\"></span></div><div class=\"space-bar\"><i id=\"payload-bar\"></i><i id=\"padding-bar\"></i><i id=\"empty-bar\"></i></div><div class=\"metrics\"><div><span data-i18n=\"payload\"></span><strong id=\"payload\"></strong></div><div><span data-i18n=\"padding\"></span><strong id=\"padding\"></strong></div><div><span data-i18n=\"capacity\"></span><strong id=\"empty-space\"></strong></div><div><span data-i18n=\"retained\"></span><strong id=\"retained\"></strong></div></div><div class=\"formula\" id=\"sum\"></div><div class=\"copy\"><p id=\"explanation\" role=\"status\"></p><p class=\"reserve\" aria-hidden=\"true\" data-i18n=\"emptyNote\"></p><p class=\"reserve\" aria-hidden=\"true\" data-i18n=\"packedNote\"></p><p class=\"reserve\" aria-hidden=\"true\" data-i18n=\"spreadNote\"></p><p class=\"reserve\" aria-hidden=\"true\" data-i18n=\"customNote\"></p></div><details class=\"scope\"><summary data-i18n=\"scopeLabel\"></summary><p data-i18n=\"scope\"></p></details>","css":".intro{margin:8px 0 18px;color:var(--muted);font-size:14px}.caption,.hint{font-size:12px;color:var(--muted);margin-top:12px}.control{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:6px 12px;align-items:center;margin:14px 0;font-size:13px}.control label{font-size:13px}.control output{color:var(--accent);min-width:5em;text-align:right;font-variant-numeric:tabular-nums}.control input{grid-column:1/-1;width:100%;margin:0}.presets{display:flex;gap:4px;align-items:stretch}.presets button{flex:1 1 0;min-width:0;overflow-wrap:anywhere;font-size:13px}.actions{display:flex;gap:8px;flex-wrap:wrap;margin:14px 0}.actions button{flex:1;min-width:0;font-size:13px;min-height:42px}.copy{display:grid;font-size:14px;margin:16px 0}.copy>*{grid-area:1/1;margin:0}.reserve{visibility:hidden}.scope{margin-top:14px;color:var(--muted);font-size:12px}.scope summary{padding:8px 0;cursor:pointer}.scope p{margin-top:10px}.two{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin:16px 0}.card{border:1px solid var(--rule);border-radius:9px;padding:14px;min-width:0}.card header{font-size:14px;font-weight:600;margin-bottom:10px}.card button{font-size:12px;width:100%;min-width:0}.card small{display:block;font-size:12px;color:var(--muted)}.card code{font-size:12px;overflow-wrap:anywhere}.metrics{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin:16px 0}.metrics>div{border-left:2px solid var(--accent);padding-left:10px;min-width:0}.metrics span{display:block;min-height:3.2em;color:var(--muted);font-size:12px}.metrics strong{font-size:23px;display:block;font-weight:500;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}.formula{border-top:1px solid var(--rule);padding-top:12px;margin-top:14px;font-size:13px;font-variant-numeric:tabular-nums;overflow-wrap:anywhere}.value{font:500 20px var(--viz-font-ui)}.legend{display:flex;flex-wrap:wrap;gap:6px 14px;font-size:12px;color:var(--muted);margin:12px 0}.legend span:before{content:'';display:inline-block;width:8px;height:8px;border-radius:2px;background:var(--accent);margin-right:6px}.legend .second:before{background:var(--second)}.check{display:flex;align-items:center;gap:8px;font-size:13px;margin:12px 0}.check input{width:16px;height:16px;flex-shrink:0;accent-color:var(--accent)}select,input[type=text],input[type=number]{background:var(--paper);color:var(--ink);font:inherit;font-size:13px;padding:10px;border:1px solid var(--rule);border-radius:7px;max-width:100%;min-width:0}select{width:100%;min-height:42px}.band{height:20px;display:flex;background:var(--surface);border-radius:4px;overflow:hidden}.band i{height:100%;display:block;background:var(--accent);transition:width .25s}.band i.second{background:var(--second)}button:disabled{cursor:default}.status{font-size:13px;min-height:4.8em}.mono{font-family:var(--viz-font-mono);font-variant-numeric:tabular-nums}.muted{color:var(--muted)}\n@media(max-width:480px){.two{gap:8px}.card{padding:12px 9px}.actions button,.presets button{font-size:12px}.metrics strong{font-size:20px}.metrics{gap:8px}}\n@media(prefers-reduced-motion:reduce){*,*::before,*::after{transition:none!important;animation:none!important}}\n.track-row{margin:16px 0}.track-row>span{display:block;font-size:12px;color:var(--muted);margin-bottom:7px}.track{display:flex;gap:5px;min-width:0}.cell{flex:1;min-width:0;min-height:48px;border:1px solid var(--rule);border-radius:5px;background:var(--surface);display:flex;align-items:center;justify-content:center;text-align:center;padding:5px 3px;font-size:12px;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}.cell.on{background:var(--accent-soft);border-color:var(--accent)}.cell.gold{background:var(--second-soft);border-color:var(--second)}.cell.empty{border-style:dashed;color:var(--muted)}.cell.error{border-color:var(--second);text-decoration:line-through}.choices{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:6px;margin:14px 0}.choices button{min-width:0;padding:10px 4px;font-size:12px;overflow-wrap:anywhere}.choices button[aria-current=true]{border-color:var(--accent);background:var(--accent-soft)}.relation{display:grid;grid-template-columns:minmax(0,1fr) 28px minmax(0,1fr);align-items:center;gap:6px;margin:12px 0}.relation .cell{min-height:72px}.relation>i{text-align:center;font-style:normal;color:var(--accent)}.readout{font-size:13px;white-space:pre-line;min-height:6.4em;overflow-wrap:anywhere}.plot{position:relative;border-left:1px solid var(--rule);border-bottom:1px solid var(--rule);padding:8px 0;margin:18px 0}.plot-row{height:28px;position:relative;margin:4px 0;background:var(--surface)}.plot-row i{position:absolute;inset-block:5px;background:var(--accent);min-width:2px;transition:width .2s,left .2s}.plot-row b{position:relative;padding:4px 6px;font:11px/28px var(--viz-font-ui);background:var(--paper);border-radius:3px}.axis{display:flex;justify-content:space-between;font-size:11px;color:var(--muted)}\n\n.slabs{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin:18px 0}.slab{transition:opacity .2s}.slab.empty{opacity:.5}.slots{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:4px}.slots button{min-width:0;min-height:26px;height:26px;padding:0;border:1px solid var(--rule);background:var(--surface);border-radius:3px;display:block;overflow:hidden}.slots button.live{background:var(--second-soft);border-color:var(--second)}.slots button i{display:block;height:100%;width:78.125%;background:transparent}.slots button.live i{background:var(--accent)}.slab small{min-height:3.2em;margin-top:10px}.space-bar{display:flex;height:24px;border:1px solid var(--rule);background:var(--paper);margin:16px 0}.space-bar i{display:block;height:100%;background:var(--accent);transition:width .25s}#padding-bar{background:var(--second)}#empty-bar{background:var(--surface)}.formula{min-height:4.8em}@media(max-width:480px){.slabs{gap:8px}.slab{padding:12px 6px}.slots{gap:3px}}","js":"const $=s=>document.querySelector(s);let slots=Array(64).fill(false),mode='packed';function seed(n){slots.fill(false);for(let i=0;i<n;i++)slots[mode==='spread'?(i%4)*16+Math.floor(i/4):i]=true;}function draw(){const n=slots.filter(Boolean).length,live=Array.from({length:4},(_,s)=>slots.slice(s*16,s*16+16).filter(Boolean).length),retained=live.filter(Boolean).length*4096,payload=n*200,padding=n*56,empty=retained-n*256;$('#count').value=n;$('#count-value').textContent=n;document.querySelectorAll('[data-slot]').forEach(e=>{e.classList.toggle('live',slots[+e.dataset.slot]);e.setAttribute('aria-pressed',String(slots[+e.dataset.slot]));});for(let s=0;s<4;s++){$('#slab-'+s).classList.toggle('empty',!live[s]);$('#live-'+s).textContent=live[s]?viz.t('alive')+' '+live[s]+' / 16':viz.t('returned');}for(const[id,value]of [['payload',payload],['padding',padding],['empty-space',empty],['retained',retained]])$('#'+id).textContent=value+' B';$('#payload-bar').style.width=payload/16384*100+'%';$('#padding-bar').style.width=padding/16384*100+'%';$('#empty-bar').style.width=empty/16384*100+'%';$('#sum').textContent=payload+' + '+padding+' + '+empty+' = '+retained+' B';$('#explanation').textContent=viz.t(n===0?'emptyNote':mode==='packed'?'packedNote':mode==='spread'?'spreadNote':'customNote');document.querySelectorAll('[data-mode]').forEach(e=>e.setAttribute('aria-pressed',String(e.dataset.mode===mode)));}document.querySelectorAll('[data-mode]').forEach(e=>e.addEventListener('click',()=>{const n=slots.filter(Boolean).length;mode=e.dataset.mode;seed(n);draw();}));$('#count').addEventListener('input',()=>{if(!mode)mode='packed';seed(+$('#count').value);draw();});document.querySelectorAll('[data-slot]').forEach(e=>e.addEventListener('click',()=>{const i=+e.dataset.slot;slots[i]=!slots[i];mode='';draw();}));seed(4);draw();","audio":false,"strings":{"alive":"Live object","capacity":"Empty slots still retained by the page","count":"Number of live objects","customNote":"Slot status directly determines which pages still have live objects. Only by freeing the last object on a page is the reason for mandatory retention on that page eliminated.","emptyNote":"All objects have been freed. The necessary backing pages for this model are zero; real allocators may still cache empty slabs according to policy.","heading":"Why do four objects occupy four times the pages?","intro":"Compare packed and spread layouts for the same number of objects. Click object slots to allocate or free, paying special attention to the last live object in a slab.","layout":"Allocation Position Snapshot","packed":"Packed into fewer slabs","packedNote":"The same number of objects concentrated into fewer slabs means remaining full pages are no longer forcibly retained by live objects. Effective content and internal slot overhead remain unchanged.","padding":"Internal overhead of used slots","payload":"Effective business content","reset":"Reset","retained":"Page space that must be retained","returned":"No live objects","scope":"Objects 200 B, slots 256 B, slabs 4096 B, total four slabs. Only counts necessary backing pages containing live objects, omitting out-of-page metadata and allocator caching of empty slabs. Layout buttons rebuild snapshots of \"same quantity, different allocation positions,\" not implying the kernel moves live objects. Blue parts in slots are content, gold parts are internal slot overhead.","scopeLabel":"Model Scope and Conventions","spread":"Spread across four slabs","spreadNote":"When live objects are scattered, every involved slab must be retained. Even if most slots are free, remaining objects cannot be arbitrarily moved to return pages."}}
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 Method
Suitable Requirements
Release and Boundaries
kmalloc/kzalloc
Standard kernel buffers, physically contiguous allocation results
kfree; do not access beyond the requested effective range
kmalloc_array/kcalloc
Arrays of element count times element size
Allocation interface checking for multiplication overflow, still need to handle NULL
kmem_cache_alloc
Fixed objects from a created cache
Corresponding kmem_cache_free, do not mix with other caches
kvmalloc/kvzalloc
Larger buffers where virtual continuity is acceptable, physical discontinuity is not
kvfree; usage scenarios and GFP must meet fallback path requirements
vmalloc
Virtual continuous mapping of multiple backing pages
vfree; 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.
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 Order
What to Release
Why It Must Be Handled at This Step
1
The 18 successfully allocated objects
They still belong to the initializer, addresses cannot be lost
2
Pointer array
Indices are no longer needed only after all objects are released
3
Cache
Destroy 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>structkb_item{unsignedint id;unsignedchar payload[196];};staticint __init kb_alloc_init(void){struct kmem_cache *cache;struct kb_item **items;unsignedint 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(unsignedint 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;}staticvoid __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
frompathlibimportPathimportosroot=Path("/sys/kernel/slab/dentry")ifnotroot.exists():print("dentry slab sysfs not currently exposed")else:print("Actual cache path:",root.resolve())values={}fornamein("object_size","slab_size","order","objects","slabs"):try:values[name]=(root/name).read_text().strip()exceptOSErroraserror:values[name]=type(error).__name__print(name,values[name])ifvalues["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:
Field
How to Understand
object_size
Field related to object content size
slab_size
Field related to object slot size, not the total slab byte count
order
Needs to be combined with base page size to calculate total backing space
objects, slabs
May 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.