文章目录
背景
前情回顾
关于slab几个结构体的关系和初始化和内存分配的逻辑请见:
[linux kernel]slub内存管理分析(0) 导读
[linux kernel]slub内存管理分析(1) 结构体
[linux kernel]slub内存管理分析(2) 初始化
[linux kernel]slub内存管理分析(2.5) slab重用
[linux kernel]slub内存管理分析(3) kmalloc
描述方法约定
PS:为了方便描述,这里我们将一个用来切割分配内存的page 称为一个slab page,而struct kmem_cache
我们这里称为slab管理结构,它管理的真个slab 体系成为slab cache,struct kmem_cache_node
这里就叫node。单个堆块称为object或者堆块或内存对象。
简介
本篇主要就kmalloc
中的一些层数比较深并且很多逻辑中都要调用的操作和两个安全加固CONFIG_SLAB_FREELIST_HARDENED
与CONFIG_SLAB_FREELIST_RANDOM
进行分析,比如(安全加固下)对freelist
的操作、cpu_slab->page
的强制下架等。
本章介绍的内容不影响分析slub算法的整体逻辑,对细节没有兴趣可以跳过。
freelist操作与CONFIG_SLAB_FREELIST_HARDENED
CONFIG_SLAB_FREELIST_HARDENED简介
CONFIG_SLAB_FREELIST_HARDENED
安全加固宏是给freelist
链表指针进行混淆:
混淆后的指针=原指针 ^ 随机数random ^ 指针地址
直接在内存中/通过内存泄露获得freelist
和freelist
成员的next
指针是混淆后的,避免直接泄露地址或直接修改地址。在每次存取freelist
指针之前都会进行一次上面的异或操作得到真实指针或从真实指针转换成混淆指针。
CONFIG_SLAB_FREELIST_HARDENED初始化
linux\include\linux\slub_def.h : struct kmem_cache
struct kmem_cache {
··· ···
#ifdef CONFIG_SLAB_FREELIST_HARDENED
unsigned long random; //开启CONFIG_SLAB_FREELIST_HARDENED宏会多一个random 成员
#endif
··· ···
};
开启CONFIG_SLAB_FREELIST_HARDENED
宏会多一个random
成员,在初始化流程中的kmem_cache_open
函数中会对random
进行初始化(完整初始化流程在前文已经分析过了):
linux\mm\slub.c : kmem_cache_open
static int kmem_cache_open(struct kmem_cache *s, slab_flags_t flags)
{
s->flags = kmem_cache_flags(s->size, flags, s->name);
#ifdef CONFIG_SLAB_FREELIST_HARDENED
s->random = get_random_long(); //初始化random 成员
#endif
··· ···
··· ···
}
这里调用get_random_long
函数获取一个随机的long
类型数据,所以random
就是一个随机数而已。
CONFIG_SLAB_FREELIST_HARDENED实现与freelist相关操作
freelist_ptr 混淆/去混淆指针
freelist_ptr
函数用于将freelist
中一个object
的指向下一个object
的next
指针从真实值计算出混淆值或从混淆值还原出真实值:
linux\mm\slub.c : freelist_ptr
static inline void *freelist_ptr(const struct kmem_cache *s, void *ptr,
unsigned long ptr_addr)//[1] 传入参数
{
#ifdef CONFIG_SLAB_FREELIST_HARDENED
/*
* When CONFIG_KASAN_SW/HW_TAGS is enabled, ptr_addr might be tagged.
* Normally, this doesn't cause any issues, as both set_freepointer()
* and get_freepointer() are called with a pointer with the same tag.
* However, there are some issues with CONFIG_SLUB_DEBUG code. For
* example, when __free_slub() iterates over objects in a cache, it
* passes untagged pointers to check_object(). check_object() in turns
* calls get_freepointer() with an untagged pointer, which causes the
* freepointer to be restored incorrectly.
*/
return (void *)((unsigned long)ptr ^ s->random ^ //[2] 混淆/去混淆操作
swab((unsigned long)kasan_reset_tag((void *)ptr_addr)));
#else
return ptr; // [3] 没开启混淆则直接返回
#endif
}
[1] 传入的ptr
参数就是要去混淆的指针,ptr_addr
是该指针的地址。
[2] 混淆公式为:混淆后的指针=原指针 ^ 随机数random ^ 指针地址
,所以去混淆就是 混淆后的指针 ^ 随机数random ^ 指针地址
。因为是异或,所以操作相同。
[3] 如果没开启CONFIG_SLAB_FREELIST_HARDENED
则直接返回ptr即可。
get_freepointer 获取next指针
linux\mm\slub.c : get_freepointer
static inline void *get_freepointer(struct kmem_cache *s, void *object)
{
object = kasan_reset_tag(object);//默认直接返回地址
return freelist_dereference(s, object + s->offset);//[1]next指针为object+s->offset
}
static inline void *freelist_dereference(const struct kmem_cache *s,
void *ptr_addr) //[2]直接调用freelist_ptr
{
return freelist_ptr(s, (void *)*(unsigned long *)(ptr_addr),
(unsigned long)ptr_addr);
}
[1] 这里调用freelist_dereference
,根据参数,传入的ptr_addr
也就是指向下一个object
的next
指针地址是object + s->offset
,s->offset
一般是该slab 大小的一半。
[2] freelist_dereference
函数中直接调用上面分析的freelist_ptr
获取真实指针
set_freepointer 设置next指针
linux\mm\slub.c : set_freepointer
static inline void set_freepointer(struct kmem_cache *s, void *object, void *fp)
{
unsigned long freeptr_addr = (unsigned long)object + s->offset; //获得next指针地址
#ifdef CONFIG_SLAB_FREELIST_HARDENED
BUG_ON(object == fp); /* naive detection of double free or corruption */
#endif//[1] 这里检测double free
freeptr_addr = (unsigned long)kasan_reset_tag((void *)freeptr_addr);
*(void **)freeptr_addr = freelist_ptr(s, fp, freeptr_addr); //[2] 调用freelist_ptr 混淆指针值
}
[1] 首先会在开启CONFIG_SLAB_FREELIST_HARDENED
的时候检测double free,如果要释放的object
和list
指针一样,则是double free,类似用户层malloc
。
[2] 在给指针复制之前调用了freelist_ptr
进行混淆操作,如果没有开启混淆,则freelist_ptr
会直接返回指针原本的值。
CONFIG_SLAB_FREELIST_RANDOM
CONFIG_SLAB_FREELIST_RANDOM简介
CONFIG_SLAB_FREELIST_RANDOM
安全加固是将freelist
列表顺序随机化,正常freelist
列表就是从开始往后按顺序排列,但如果开启了CONFIG_SLAB_FREELIST_RANDOM
则会打乱freelist
中object
的顺序,即便是刚申请好的新slab,其中的freelist
列表顺序也是随机的。
CONFIG_SLAB_FREELIST_RANDOM初始化
linux\include\linux\slub_def.h : struct kmem_cache
struct kmem_cache {
··· ···
#ifdef CONFIG_SLAB_FREELIST_RANDOM
unsigned int *random_seq;
#endif
··· ···
};
开启CONFIG_SLAB_FREELIST_RANDOM
之后会多一个叫做random_seq
的数组,以指针成员的形式出现在kmem_cache
也就是slab 管理结构体中。在 kmem_cache_init
中会进行初始化:
linux\mm\slub.c : kmem_cache_init
void __init kmem_cache_init(void)
{
··· ···
create_kmalloc_caches(0);//正式初始化各个slab
/* Setup random freelists for each cache */
init_freelist_randomization();//开启了CONFIG_SLAB_FREELIST_RANDOM需要操作一下
··· ···
}
在create_kmalloc_caches
函数已经完成了各个slab 的初始化之后,调用init_freelist_randomization
对所有slab 进行freelist random
初始化:
linux\mm\slub.c : init_freelist_randomization
static void __init init_freelist_randomization(void)
{
struct kmem_cache