[linux kernel]slub内存管理分析(4) 细节操作以及安全加固

本文详细分析了Linux内核slab管理系统中的两个安全加固机制:CONFIG_SLAB_FREELIST_HARDENED和CONFIG_SLAB_FREELIST_RANDOM。前者通过对freelist指针进行混淆以增强内存安全,防止泄露或非法修改;后者通过随机化freelist顺序,增加攻击难度。文章还介绍了这两个机制的初始化和具体操作,如freelist的混淆与去混淆、随机化过程以及slab的强制下架和补充流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

背景

前情回顾

关于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 cachestruct kmem_cache_node这里就叫node。单个堆块称为object或者堆块内存对象

简介

本篇主要就kmalloc 中的一些层数比较深并且很多逻辑中都要调用的操作和两个安全加固CONFIG_SLAB_FREELIST_HARDENEDCONFIG_SLAB_FREELIST_RANDOM进行分析,比如(安全加固下)对freelist的操作、cpu_slab->page的强制下架等。

本章介绍的内容不影响分析slub算法的整体逻辑,对细节没有兴趣可以跳过。

freelist操作与CONFIG_SLAB_FREELIST_HARDENED

CONFIG_SLAB_FREELIST_HARDENED简介

CONFIG_SLAB_FREELIST_HARDENED 安全加固宏是给freelist 链表指针进行混淆:

混淆后的指针=原指针 ^ 随机数random ^ 指针地址

直接在内存中/通过内存泄露获得freelistfreelist成员的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 的指向下一个objectnext指针从真实值计算出混淆值或从混淆值还原出真实值:

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 也就是指向下一个objectnext指针地址是object + s->offsets->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,如果要释放的objectlist 指针一样,则是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 则会打乱freelistobject 的顺序,即便是刚申请好的新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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值