Linux 内核主线相关补丁:
1ecd5f82e05dd LSM: Infrastructure management of the ipc security blob
2019bcca4626a Smack: Abstract use of ipc security blobs
37c6538280ae9 SELinux: Abstract use of ipc security blobs
4f4ad8f2c4076 LSM: Infrastructure management of the task security
5afb1cbe37440 LSM: Infrastructure management of the inode security
6fb4021b6fb58 Smack: Abstract use of inode security blob
780788c229116 SELinux: Abstract use of inode security blob
833bf60cabcc7 LSM: Infrastructure management of the file security
9f28952ac9008 Smack: Abstract use of file security blob
10bb6c6b02ccb7 SELinux: Abstract use of file security blob
11bbd3662a8348 Infrastructure management of the cred security blob
1243fc460907dc TOMOYO: Abstract use of cred security blob
1369b5a44a95bb AppArmor: Abstract use of cred security blob
143d252529480c SELinux: Remove unused selinux_is_enabled
1598c886513657 SELinux: Remove cred security blob poisoning
160c6cfa622cf5 SELinux: Abstract use of cred security blob
17b17103a8b8ae Smack: Abstract use of cred security blob
原理解析
内核代码中一些重要结构体,如 struct cred
、struct file
、struct inode
、struct task_struct
、struct kern_ipc_perm
,都有一个 void *
类型的 security 成员:cred->security
、file->f_security
、inode->i_security
、task->security
、ipc->security
,指向的都是自身实例与 LSM 相关的属性与信息。
LSM 本质上是一套数量众多的钩子函数,各自嵌入到系统执行的各个安全关键路径上。它们的具体实现则是由如 SELinux、AppArmor、Smack 这样的安全模块来提供。直到 5.1 之前(也就是这组补丁合入之前),这些安全模块在代码逻辑上都是独占上述那些 security 成员的,并且它们都会在内部直接操作那些 security 成员1。从而系统一次只能部署一个安全模块(如 SELinux 与 AppArmor 不能同时使能),毕竟各结构体只有一个 security 指针。另外,各安全模块保存信息的内容和形式不同,相关结构体的定义也不同,因此那些 security 成员就都只能定义为 void *
类型。
1 S E L I N U X
2 ┌──────────────────────────────────────────────────────────────────────┐
3 │ │
4 struct struct struct │ ┌────────────────────┐ ┌─────────────────────┐ ┌───────────────────┐ │
5 cred inode kern_ipc_perm │ │task_security_struct│ │inode_security_struct│ │ipc_security_struct│ │
6┌──────────┐ ┌──────────┐ ┌──────────┐ │ └────────────────────┘ └─────────────────────┘ └───────────────────┘ │
7│ │ │ │ │ │ │ │
8│ │ │ │ │ │ │ ┌────────────────────┐ ┌────┐ ┌───────────────────┐ │
9├──────────┤ ├──────────┤ ├──────────┤ │ │file_security_struct│ │NULL│ │msg_security_struct│ │
10│ security ├─┐ │i_security├─┐ │ security ├─┐ │ └────────────────────┘ └────┘ └───────────────────┘ │
11├──────────┤ │ ├──────────┤ │ ├──────────┤ │ │ │
12│ │ │ │ │ │ │ │ │ └──────────────────────────────────────────────────────────────────────┘
13└──────────┘ │ └──────────┘ │ └──────────┘ │ A P P A R M O R
14 │ │ │ ┌───────────────────────────────────┐
15 struct │ struct │ struct │ │ │
16 file │ task │ msg_msg │ │ ┌──────────┐ ┌────┐ ┌────┐ │
17┌──────────┐ │ ┌──────────┐ │ ┌──────────┐ │ │ │aa_label *│ │NULL│ │NULL│ │
18│ │ │ │ │ │ │ │ │ │ └──────────┘ └────┘ └────┘ │
19│ │ │ │ │ │ │ │ │ │ │
20├──────────┤ │ ├──────────┤ │ ├──────────┤ │ │ ┌────┐ ┌───────────┐ ┌────┐ │
21│f_security├─┤ │ security ├─┤ │ security ├─┤ │ │NULL│ │aa_task_ctx│ │NULL│ │
22├──────────┤ │ ├──────────┤ │ ├──────────┤ │ │ └────┘ └───────────┘ └────┘ │
23│ │ │ │ │ │ │ │ │ │ │
24└──────────┘ │ └──────────┘ │ └──────────┘ │ └───────────────────────────────────┘
25 │ │ └───► S M A C K
26 │ │ ┌───────────────────────────────────────────┐
27 │ └──────────────────► │ │
28 │ │ ┌──────────┐ ┌───────────┐ ┌───────────┐ │
29 └─────────────────────────────────► │ │task_smack│ │inode_smack│ │smack_known│ │
30 │ └──────────┘ └───────────┘ └───────────┘ │
31 │ │
32 │ ┌───────────┐ ┌────┐ ┌───────────┐ │
33 │ │smack_known│ │NULL│ │smack_known│ │
34 │ └───────────┘ └────┘ └───────────┘ │
35 │ │
36 └───────────────────────────────────────────┘
37 . . . . . .
38 ┌─────────────────────────┐
39 │ │
40 │ ┌─────┐ ┌─────┐ ┌─────┐ │
41 │ │. . .│ │. . .│ │. . .│ │
42 │ └─────┘ └─────┘ └─────┘ │
43 │ │
44 │ ┌─────┐ ┌─────┐ ┌─────┐ │
45 │ │. . .│ │. . .│ │. . .│ │
46 │ └─────┘ └─────┘ └─────┘ │
47 │ │
48 └─────────────────────────┘
这种做法有以下两个问题:第一,系统在同时启用多种安全模块的时候,对 security 成员的操作可能会相互冲突;第二,对于像 SELinux 这种可以在运行时动态关闭的安全模块来说,动态关闭可能会导致内存泄漏。具体操作举例如下:
- 以使能 SELinux 的方式启动系统;
- 做相关操作创建一个
struct file
实例,此时 SELinux 会为f_security
成员分配内存空间; - 动态关闭 SELinux,此时 LSM 会解绑所有 SELinux 的钩子函数实现;
- 释放
struct file
实例,由于执行f_security
释放的 SELinux 钩子函数实现已被解绑,导致内存泄漏。
为了解决这些问题,上游社区引入了上述的补丁集,主要做了两件事:
- 将各安全模块内的信息结构体视为 Blob,在系统初始化时一次性分配长度为各 Blob 之和的连续内存空间,各安全模块记录自己 Blob 在整段空间中的起始位置(即 offset)。
- 将各 security 成员指向的内存统一收归 LSM 框架来分配和释放,各安全模块不再管理这块内存,只能查、改
*security
空间中自己 Blob 的部分。
1 struct
2 cred
3┌──────────┐
4│ │ sel_c aa_c smack_c
5│ │ ┌──────────────────────┬────────────┬────────────┬───────┐
6├──────────┤ │ │ │ │ │
7│ security ├─────►│ task_security_struct │ aa_label * │ task_smack │ . . . │
8├──────────┤ │ │ │ │ │
9│ │ └──────────────────────┴────────────┴────────────┴───────┘
10└──────────┘
11 struct
12 file
13┌──────────┐
14│ │ sel_f smack_f
15│ │ ┌──────────────────────┬─────────────┬───────┐ S E L I N U X A P P A R M O R
16├──────────┤ │ │ │ │ ┌─────────────────┐ ┌─────────────────┐
17│f_security├─────►│ file_security_struct │ smack_known │ . . . │ │ struct │ │ struct │
18├──────────┤ │ │ │ │ │ lsm_blob_sizes │ │ lsm_blob_sizes │
19│ │ └──────────────────────┴─────────────┴───────┘ │ ┌─────────┐ │ │ ┌─────────┐ │
20└──────────┘ │ │ sel_c │ │ │ │ aa_c │ │
21 struct │ ├─────────┤ │ │ ├─────────┤ │
22 inode │ │ sel_f │ │ │ │ N/A │ │
23┌──────────┐ │ ├─────────┤ │ │ ├─────────┤ │
24│ │ sel_i smack_i │ │ sel_i │ │ │ │ N/A │ │
25│ │ ┌───────────────────────┬─────────────┬───────┐ │ ├─────────┤ │ │ ├─────────┤ │
26├──────────┤ │ │ │ │ │ │ N/A │ │ │ │ aa_t │ │
27│i_security├─────►│ inode_security_struct │ inode_smack │ . . . │ │ ├─────────┤ │ │ ├─────────┤ │
28├──────────┤ │ │ │ │ │ │ sel_ipc │ │ │ │ N/A │ │
29│ │ └───────────────────────┴─────────────┴───────┘ │ ├─────────┤ │ │ ├─────────┤ │
30└──────────┘ │ │ sel_m │ │ │ │ N/A │ │
31 struct │ └─────────┘ │ │ └─────────┘ │
32 task │ │ │ │
33┌──────────┐ └─────────────────┘ └─────────────────┘
34│ │ aa_t
35│ │ ┌─────────────┬───────┐ S M A C K . . . . . .
36├──────────┤ │ │ │ ┌─────────────────┐ ┌─────────────────┐
37│ security ├─────►│ aa_task_ctx │ . . . │ │ struct │ │ struct │
38├──────────┤ │ │ │ │ lsm_blob_sizes │ │ lsm_blob_sizes │
39│ │ └─────────────┴───────┘ │ ┌─────────┐ │ │ ┌─────────┐ │
40└──────────┘ │ │ smack_c │ │ │ │ ..._c │ │
41 struct │ ├─────────┤ │ │ ├─────────┤ │
42 kern_ipc_perm │ │ smack_f │ │ │ │ ..._f │ │
43┌──────────┐ │ ├─────────┤ │ │ ├─────────┤ │
44│ │ sel_ipc smack_ipc │ │ smack_i │ │ │ │ ..._i │ │
45│ │ ┌─────────────────────┬─────────────┬───────┐ │ ├─────────┤ │ │ ├─────────┤ │
46├──────────┤ │ │ │ │ │ │ N/A │ │ │ │ ..._t │ │
47│ security ├─────►│ ipc_security_struct │ smack_known │ . . . │ │ ├─────────┤ │ │ ├─────────┤ │
48├──────────┤ │ │ │ │ │ │smack_ipc│ │ │ │ ..._ipc │ │
49│ │ └─────────────────────┴─────────────┴───────┘ │ ├─────────┤ │ │ ├─────────┤ │
50└──────────┘ │ │ smack_m │ │ │ │ ..._m │ │
51 struct │ └─────────┘ │ │ └─────────┘ │
52 msg_msg │ │ │ │
53┌──────────┐ └─────────────────┘ └─────────────────┘
54│ │ sel_m smack_m
55│ │ ┌─────────────────────┬─────────────┬───────┐
56├──────────┤ │ │ │ │
57│ security ├─────►│ msg_security_struct │ smack_known │ . . . │
58├──────────┤ │ │ │ │
59│ │ └─────────────────────┴─────────────┴───────┘
60└──────────┘
重点代码注解
lsm_set_blob_size
在 bbd3662a8348 (“Infrastructure management of the cred security blob”) 引入的代码中有如下部分:
1diff --git a/security/security.c b/security/security.c
2index 60b39db95c2f..09be8ce007a2 100644
3--- a/security/security.c
4+++ b/security/security.c
5@@ -139,6 +141,25 @@ static bool __init lsm_allowed(struct lsm_info *lsm)
6 return true;
7 }
8
9+static void __init lsm_set_blob_size(int *need, int *lbs)
10+{
11+ int offset;
12+
13+ if (*need > 0) {
14+ offset = *lbs;
15+ *lbs += *need;
16+ *need = offset;
17+ }
18+}
19+
20+static void __init lsm_set_blob_sizes(struct lsm_blob_sizes *needed)
21+{
22+ if (!needed)
23+ return;
24+
25+ lsm_set_blob_size(&needed->lbs_cred, &blob_sizes.lbs_cred);
26+}
27+
注意这里的 lsm_set_blob_size()
是将旧的 offset
赋值给了 *need
,而这个 needed
实际上指向的是各安全模块内部记录 Offset 的 struct lsm_blob_sizes
实例。因此 needed->lbs_cred
原本记录的是安全模块某个 Blob 所需长度,执行完 lsm_set_blob_size()
之后就变成记录 Blob 的偏移地址了。
POISON_FREE
98c886513657 (“SELinux: Remove cred security blob poisoning”) 涉及对 POISON_FREE
的修改。此文
解释了 POINSON_FREE
由谁设置,代表的含义以及如何使用。
bdfedb76f4f5a (“mm, mempool: poison elements backed by slab allocator”):一个与 POINSON_FREE
相关的主线补丁。
还可以参考:Linux Kernel Heap Tampering Detection by Larry Highsmith, 2009.
-
说实话这一点很奇怪。明明 LSM 的各个钩子函数已经做成了函数指针列表的形式,调用时沿列表逐个调用,摆明了是考虑到了多个安全模块叠加多套函数实现的场景,怎么这里又是独占的呢? ↩︎