1 背景
最近一直在关注内核安全加固相关技术。从安全和可靠性领域来看,有一部分技术是重叠的。安全领域将内存USE After Free,Stack溢出,DOS攻击作为安全漏洞。可靠性认为这些问题会导致系统重启故障,属于可靠性范畴。从今年开始,我专门跟踪了kernel几个安全领域项目,收获不少。
2,kernel开源项目
大家有兴趣的可以订阅一下邮件列表:
http://www.openwall.com/lists/kernel-hardening/
技术1:arm64: kernel: Add support for Privileged Access Never
'Privileged Access Never' is a new arm8.1 feature which prevents privileged code from accessing any virtual address where read or write access is also permitted at EL0. This patch enables the PAN feature on all CPUs, and modifies {get,put}_user helpers temporarily to permit access.This will catch kernel bugs where user memory is accessed directly. 'Unprivileged loads and stores' using ldtrb et al are unaffected by PAN. 这个特性是kernel-hardening最近提交的一个特性,原理是这样的:
ARM8.1 CPU特性增加了一个flag:
A new Privileged Access Never (PAN) state bit. This bit provides control that prevents privileged access to user data unless explicitly enabled; an additional security mechanism against possible software attacks
这个特性可以实现的功能:当对cpu使能PAN bit后,cpu执行在EL0(内核代码执行的时),是无法访问TTBR0指向的虚拟空间地址的。例如当你在内核态代码执行的时候,计划访问0x0-0x0000FFFFFFFFFF,如果不打开PAN状态,则内核直接会导致OOPS。
具体实现代码:https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=338d4f49d6f7114a017d294ccf7374df4f998edc
对我们参考价值:可靠性领域合入此特性后,可以杜绝内核态通过进程虚拟地址去修改进程数据,提供了一定内核和用户态的内存隔离。
技术2:Add support for eXclusive Page Frame Ownership (XPFO)
https://lwn.net/Articles/700606/
技术1只能杜绝内核态通过进程虚拟地址去修改进程数据问题,但是还有一种内核态修改用户进程的场景无法进行隔离。内核态执行可以通过内核映射页表直接修改物理页数据,如果对应的物理页已经被用户态程序使用了,则会直接破坏对应程序的用户态数据。Linux内核会在开机启动的时候创建内核页表,将大部分物理页映射到内核的虚拟地址空间内。考虑一种场景,内核申请一个物理page,然后释放了这个page给BUDDY系统,由于BUG等原因,后续代码仍旧在使用它;这个时候此page被分配到了一个用户态进程,这样就出现了内核态破坏用户进程的问题。此patch给出了一个方案,其实我们在两年前的一个思路,就是将用户态空间的物理页对应的内核页表设置为只读或清零。当时由于技术水平有限,无法实现。没想到他们作为一个安全特性进行了提交。现在此patch只实现了x86上,但是对于ARM是类似的。
现在提交patch有如下几个:
http://www.openwall.com/lists/kernel-hardening/2016/11/04/3
http://www.openwall.com/lists/kernel-hardening/2016/11/04/4
http://www.openwall.com/lists/kernel-hardening/2016/11/04/5
依赖的特性:https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=46f24fd857b37bb86ddd5d0ac3d194e984dfdf1c
本patch实现原理:
1.在page_alloc路径上根据GFP_FLAGS里面HIGHUSER,决定是否page是内核还是用户态,如果没有这个标记,则在page_ext上设置状态为PAGE_EXT_XPFO_KERNEL,否则清理PAGE_EXT_XPFO_KERNEL标记。
2. 在kunmap增加hook,如果kunmap的page不是PAGE_EXT_XPFO_KERNEL,则将page设置PAGE_EXT_XPFO_UNMAPPED,将对应page的内核页表设置为0。这个地方没有理解清楚,kunmap的内存不是内核态申请的,具体是什么场景?
3. kmap的page不是PAGE_EXT_XPFO_KERNEL,即用户态的内存,则将page的PAGE_EXT_XPFO_UNMAPPED清除,然后页表恢复
4. 当free page到buddy系统时候,将page的PAGE_EXT_XPFO_UNMAPPED清除,然后页表恢复。
不足:
1. 如果内核地址空间映射不是4K page怎么办?
对于arm/arm64来说,内核映射不是4k为单位进行映射的,使用的1M section映射模式
2. 性能影响是多大呢
主要涉及到tlb shootdown(The actions of one processor causing the TLBs to be flushed on other processors is what is called a TLB shootdown)问题
3. 此patch现在解决是内存先被用户态申请之后,内核通过kmap/kunmap访问这个page,如果调用kunmap之后,内核再使用addr去访问物理内存,则会直接panic。如果内存没有被用户态使用,内核仍旧拿着这个指针,当用户态申请之后,则无法访问。
对我们的参加价值:我们可以做一个类似patch,将用户态申请走的内存不允许内核态进行访问,可以作为一种故障隔离定位手段。这个后续需要专门研究一下:为什么不在page alloc时候直接将内核态页表设置unmapped state,当内核使用kmap/kunmap显示访问用户进程内存的时候,再放开权限。
技术三:CONFIG_HARDENED_USERCOPY特性
本身这个特性https://lwn.net/Articles/691012/
已经合入主线:
https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=f5509cc18daa7f82bcc553be70df2117c8eedc16
https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=dfd45b6103c973bfcea2341d89e36faf947dbc33
https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=faf5b63e294151d6ac24ca6906d6f221bd3496cd
想法也比较简单,就是在内核调用
copy_to_user() and copy_from_user() on the kernel object being copied to/from:
- address range doesn't wrap around
- address range isn't NULL or zero-allocated (with a non-zero copy size)
- if on the slab allocator:
- object size must be less than or equal to copy size (when check is implemented in the allocator, which appear in subsequent patches)
- otherwise, object must not span page allocations (excepting Reserved and CMA ranges)
- if on the stack
- object must not extend before/after the current process stack
- object must be contained by a valid stack frame (when there is arch/build support for identifying stack frames)
- object must not overlap with kernel text
现在ARM/ARM64已经支持,后续可以移植到kernel 4.1上看看是否我们有类似的问题。
后记:
这三个特性对我们后续在内核可靠性方面是有比较大的帮助。
第二个特性社区实现还很不完善,后续可以沿着他的思路做一部优化。