Linux - 内核 - 安全机制 - 内存页表安全

说明

  • 内核页表安全的最终目标是:将内核使用到的内存页(内核与module占用)的属性(读/写/可执行)配置成安全的,即:代码段和rodata段只读,非代码段不能执行等,用来防御堆栈执行和代码段被修改的攻击。

内核页表安全

相关选项

CONFIG_DEBUG_KERNEL=y (4.11版本之前)
CONFIG_DEBUG_RODATA=y (4.11版本)
CONFIG_STRICT_KERNEL_RWX=y (4.11至最新版本)
  • 开启该选项后,内核的text段和rodata段内存将会变成只读,并且非代码段的内存将会变成不可执行。

影响

  1. kernel code, init等占用的预留内存会变大。
  • 开启配置后,编译使用的链接脚本中会对kernel的代码段等内存做size对齐操作(默认2MB对齐),预留内存占用会多3~4MB,如下:
* 开启配置,启动log (kernel code,init大小2M对齐)
Memory: 32360K/131072K available (4096K kernel code, 277K rwdata, 1240K rodata, 2048K init, 139K bss, 98712K reserved, 0K cma-reserved)
* 未开启配置,启动log
Memory: 35788K/131072K available (3122K kernel code, 282K rwdata, 1304K rodata, 144K init, 140K bss, 95284K reserved, 0K cma-reserved)
  • 链接脚本
file: arch/arm/include/asm/pgtable-2level.h:
#define SECTION_SHIFT		20
...

file: arch/arm/kernel/vmlinux.lds.S
...
#ifdef CONFIG_STRICT_KERNEL_RWX
        . = ALIGN(1<
  1. 阻止使用gdb调试软件断点,jtag硬件断点可用,blog 说明。

module页表安全

相关选项

CONFIG_STRICT_MODULE_RWX
  • 作用和CONFIG_STRICT_KERNEL_RWX一样,只是作用对象变成了module。

实现原理

  1. 初始化 rodata_enabled值,开启配置后,默认为true。
//file: init/main.c
...
#if defined(CONFIG_STRICT_KERNEL_RWX) || defined(CONFIG_STRICT_MODULE_RWX)
bool rodata_enabled __ro_after_init = true;
static int __init set_debug_rodata(char *str)
{
        return strtobool(str, &rodata_enabled);
}
__setup("rodata=", set_debug_rodata);
#endif
...
  1. mmap
//file: arch/arm64/mm/mmu.c
static void __init map_kernel(pgd_t *pgdp)
{
    static struct vm_struct vmlinux_text, vmlinux_rodata, vmlinux_inittext,
                            vmlinux_initdata, vmlinux_data;
    
    /*
     * External debuggers may need to write directly to the text
     * mapping to install SW breakpoints. Allow this (only) when
     * explicitly requested with rodata=off.
     */
    pgprot_t text_prot = rodata_enabled ? PAGE_KERNEL_ROX : PAGE_KERNEL_EXEC;
    
    /*
     * If we have a CPU that supports BTI and a kernel built for
     * BTI then mark the kernel executable text as guarded pages
     * now so we don't have to rewrite the page tables later.
     */
    if (arm64_early_this_cpu_has_bti())
            text_prot = __pgprot_modify(text_prot, PTE_GP, PTE_GP);
    
    /*
     * Only rodata will be remapped with different permissions later on,
     * all other segments are allowed to use contiguous mappings.
     */
    map_kernel_segment(pgdp, _text, _etext, text_prot, &vmlinux_text, 0,
                       VM_NO_GUARD);
    .....
}

现状

  • 未开启相关配置的Linux内核,内存页权限没做什么限制。
  • 以上选项对于较新的CPU架构和kernel版本,已经是默认开启,Linux内核中会根据ARCH(CPU架构)来确定是否默认支持,如下:
//file: arch/Kconfig
config ARCH_OPTIONAL_KERNEL_RWX
    def_bool n

config ARCH_OPTIONAL_KERNEL_RWX_DEFAULT
    def_bool n

config ARCH_HAS_STRICT_KERNEL_RWX
    def_bool n

config STRICT_KERNEL_RWX
    bool "Make kernel text and rodata read-only" if ARCH_OPTIONAL_KERNEL_RWX
    depends on ARCH_HAS_STRICT_KERNEL_RWX
    default !ARCH_OPTIONAL_KERNEL_RWX || ARCH_OPTIONAL_KERNEL_RWX_DEFAULT
    ...

//file: arch/riscv/Kconfig   // riscv架构cpu配置
config RISCV
    def_bool y
    ...
    select ARCH_HAS_STRICT_KERNEL_RWX if MMU
    ...
    select ARCH_OPTIONAL_KERNEL_RWX if ARCH_HAS_STRICT_KERNEL_RWX
    select ARCH_OPTIONAL_KERNEL_RWX_DEFAULT
  • 也可以手动对以上选项进行配置,如下:
General architecture-dependent options  --->
    [*] Make kernel text and rodata read-only (NEW)
    [*] Set loadable kernel module data as NX and text as RO (NEW)
  • 对于服务器产品是默认开启选项,但是在封闭的小型嵌入式设备上的Linux时常会选择不开启该配置,来节省内存。

你可能感兴趣的:(#,Linux,内核知识,linux,安全,运维)