kmem_cache
kmem_cache定义如下:https://elixir.bootlin.com/linux/v6.13.1/source/mm/slab.h#L258
cpu_slab
cpu_slab是一个__per_cpu变量,这意味着kmem_cache创建时会为每一个CPU创建一个__per_cpu变量,所以不同的CPU分配资源的时候就会从不同的slab上进行分配;
freelist
:指向下一个空闲对象的指针slab
:当前用以进行内存分配的 slabpartial
:percpu partial slab 链表,链表上为仍有一定空闲对象的 slab
slab 的 freelist 仅当其在 partial 链表上时有用,当一张 slab 为当前 CPU 正在使用的 slab 时,其 freelist 为 NULL,由 kmem_cache_cpu.freelist
指向第一个空闲对象:
kmem_cache_node
kmem_cache_node *node[]是一个 kmem_cache_node
数组,对应多个不同 node 的后备内存池,这里的node即内存三级结构”页-区-节点“中的节点
partial指向一个部分使用的slab的链表;
full则是指向一个完全使用的slab的链表;
slab
源码定义
slab再源码中的定义位置:https://elixir.bootlin.com/linux/v6.13.1/source/mm/slab.h#L52
inuse
:已被使用的对象数量objects
:该 slab 上的对象总数frozen
:是否被冻结,即已经归属于特定的 CPU
所以在double-free漏洞中,释放vul-obj两次,会使inuse变量递减两次,从而导致在有一个obj尚且存活的情况下,使得该slab被判定为empty。
slab的object管理结构分析
再回过头来看一个kmem_cache的成员:
size是包含了上述object之外的“红区”等结构的size(当然可能这些空间压根不存在,取决于内核编译配置),object_size则是真正的obj的size,而offset成员则表示了freepointer指针在一个object中的偏移:
slab object的其他保护措施
CONFIG_SLAB_FREELIST_RANDOM
初始化slab的内存空间时,随机顺序选取free-object形成freelist链表;
CONFIG_SLAB_FREELIST_HARDENED
CONFIG_HARDENED_USERCOPY
分配&&释放
内存分配浅析
slab分配对象的过程大致如下:
内存回收浅析
快速释放:释放对象所在的 slab 刚好是 slab cache 在本地 cpu 缓存 kmem_cache_cpu->page 缓存的 slab;
慢释放:
如果 slab 本来就在 slab cache 本地 cpu 缓存 kmem_cache_cpu->partial 链表中,那么对象在释放之后,slab 的位置不做任何改变;
如果 slab 不在 kmem_cache_cpu->partial 链表中,并且该 slab 由于对象的释放刚好由一个 full slab 变为了一个 partial slab,为了利用局部性的优势,内核需要将该 slab 插入到 kmem_cache_cpu->partial 链表中。
如果 slab 不在 kmem_cache_cpu->partial 链表中,并且该 slab 由于对象的释放刚好由一个 partial slab 变为了一个 empty slab,说明该 slab 并不是很活跃,内核会将该 slab 放入对应 NUMA 节点缓存 kmem_cache_node->partial 链表中,刀枪入库,马放南山。
在将这个 empty slab 插入到 kmem_cache_node->partial 链表之前,同样需要检查当前 partial 链表中的容量
kmem_cache_node->nr_partial
不能超过 kmem_cache-> min_partial 的限制。如果超过限制了,直接将这个 empty slab 释放回伙伴系统中。如果不符合第 2, 3 种场景,但是 slab 本来就在对应的 NUMA 节点缓存 kmem_cache_node->partial 链表中,那么对象在释放之后,slab 的位置不做任何改变。
参考
https://arttnba3.cn/2023/02/24/OS-0X04-LINUX-KERNEL-MEMORY-6.2-PART-III/
https://www.cnblogs.com/binlovetech/p/17434311.html