[linux kernel]slub内存管理分析(7) MEMCG的影响与绕过

背景

前情回顾

关于slab几个结构体的关系和初始化和内存分配和释放的逻辑请见:

[linux kernel]slub内存管理分析(0) 导读

[linux kernel]slub内存管理分析(1) 结构体

[linux kernel]slub内存管理分析(2) 初始化

[linux kernel]slub内存管理分析(2.5) slab重用

[linux kernel]slub内存管理分析(3) kmalloc

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

[linux kernel]slub内存管理分析(5) kfree

[linux kernel]slub内存管理分析(6) 销毁slab

描述方法约定

PS:为了方便描述,这里我们将一个用来切割分配内存的page 称为一个slab page,而struct kmem_cache我们这里称为slab管理结构,它管理的真个slab 体系成为slab cachestruct kmem_cache_node这里就叫node。单个堆块称为object或者堆块内存对象

MEMCG总览

省流总结

  • 如果开启了memcg,那么kmalloc 申请相同大小的附带ACCOUNT关键字的flag的内存和不带ACCOUNT 关键字的内存可能是从不同slab 中申请的:
    • 在kernel 版本小于5.9(不包括5.9) 的情况下,两者申请的内存属于不同slab
    • 在kernel 版本属于5.9-5.14(包括5.9,不包括5.14)的情况下,两者申请的内存属于相同slab
    • 在kernel 版本大于5.14(包括5.14)的情况下,两者申请的内存属于不同slab,但和第一种情况的实现不同
  • 对于跨slab 的漏洞利用,可以考虑使用cross cache attack 或页面级堆风水来做。

其实memcg 本质上来说是一个统计功能,用于统计在cgroup 场景下申请内存的多少什么的,但它的实现方式可能会对我们的漏洞利用带来麻烦,下面简单从源码角度分析一下。

简介

memcg 全称memory cgroup,是cgroup 的一种,其实现了内存资源的隔离与限制功能。在内核中的整体实现是一个包括cgroup模块、内存管理模块等跨模块的实现。而为什么要在这里提到MEMCG呢,是因为我们分析slub 算法源码的主要目的还是为了做内核的漏洞利用。而memcg 的一个在内存管理模块的"记账(统计)"机制会给我们的漏洞利用带来一些麻烦,需要我们注意。

内核编译选项CONFIG_MEMCG 决定是否开启memcg 功能,但这个是默认开启的。

slub 相关 memcg机制

kernel 5.9 版本之前

结构体
struct kmem_cache {
   
	··· ···
#ifdef CONFIG_MEMCG
	struct memcg_cache_params memcg_params;//
	/* For propagation, maximum size of a stored attr */
	unsigned int max_attr_size;
#ifdef CONFIG_SYSFS
	struct kset *memcg_kset;
#endif
#endif
··· ···
};

开启CONFIG_MEMCG 的情况下在slab 管理结构kmem_cache中会多出一个比较重要的成员memcg_params,属于memcg_cache_params 结构体,memcg_cache_params 用于记录memcg 在slab 相关的一些参数:

struct memcg_cache_params {
   
	struct kmem_cache *root_cache;//指向根slab
	union {
   
		struct {
   
			struct memcg_cache_array __rcu *memcg_caches;//存放若干子memcg slab 管理结构
			struct list_head __root_caches_node;
			struct list_head children;
			bool dying;
		};
		··· ···
	};
};

root_cache 指向根slab,也就是说可以通过根slab的memcg_params 找到子slab,也可以通过子slab 的root_cache 找到所属的跟slab。

memcg_caches 是一个memcg_cache_array 结构的成员,存放子memcg slab 的数组:

struct memcg_cache_array {
   
	struct rcu_head rcu;
	struct kmem_cache *entries[0];//子memcg slab 的数组
};

entries 是子memcg slab 的数组,通过memcg id 进行下标寻找。

所以可以理解为每一个根slab 管理结构(根slab 管理结构根据大小分类)都有一个对应的子memcg slab 列表。

初始化

主要的初始化函数是init_memcg_params:

mm\slab_common.c : init_memcg_params

static int init_memcg_params(struct kmem_cache *s,
			     struct kmem_cache *root_cache)
{
   
	struct memcg_cache_array *arr;

	if (root_cache) {
   
		int ret = percpu_ref_init(&s->memcg_params.refcnt,
					  kmemcg_cache_shutdown,
					  0, GFP_KERNEL);
		if (ret)
			return ret;

		s->memcg_params.root_cache = root_cache;
		INIT_LIST_HEAD(&s->memcg_params.children_node);
		INIT_LIST_HEAD(&s->memcg_params.kmem_caches_node);
		return 0;
	}

	slab_init_memcg_params(s);

	if (!memcg_nr_cache_ids)
		return 0;

	arr = kvzalloc(sizeof(struct memcg_cache_array) + //最多含有memcg_nr_cache_ids 个子slab
		       memcg_nr_cache_ids * sizeof(void *),
		       GFP_KERNEL);
	if (!arr)
		return -ENOMEM;

	RCU_INIT_POINTER(s->memcg_params.memcg_caches, arr);
	return 0;
}

除此之外,memcg 在slab 相关的初始化可以关注调用栈中多出的memcg 相关函数:

kmem_cache_init

  • create_boot_cache
    • slab_init_memcg_params //初始化memcg
    • __kmem_cache_create
      • ··· ···
      • memcg_propagate_slab_attrs //初始化memcg
  • bootstrap
    • ··· ···
    • slab_init_memcg_params//初始化memcg
    • memcg_link_cache //初始化memcg
  • create_kmalloc_caches
    • new_kmalloc_cache
      • create_kmalloc_cache
        • ··· ···
        • memcg_link_cache //初始化memcg
具体实现

我们这里以kernel 5.4 版本的源码为例,首先看关键的部分,经过之前的分析,我们知道,在kmalloc 的调用栈中有slab_alloc_node 函数,在其中调用的slab_pre_alloc_hook 函数我们没有分析,现在看slab_pre_alloc_hook 函数的实现:

static __always_inline void *slab_alloc_node
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值