Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ARM: 8501/1: mm: flip priority of CONFIG_DEBUG_RODATA

The use of CONFIG_DEBUG_RODATA is generally seen as an essential part of
kernel self-protection:
http://www.openwall.com/lists/kernel-hardening/2015/11/30/13
Additionally, its name has grown to mean things beyond just rodata. To
get ARM closer to this, we ought to rearrange the names of the configs
that control how the kernel protects its memory. What was called
CONFIG_ARM_KERNMEM_PERMS is realy doing the work that other architectures
call CONFIG_DEBUG_RODATA.

This redefines CONFIG_DEBUG_RODATA to actually do the bulk of the
ROing (and NXing). In the place of the old CONFIG_DEBUG_RODATA, use
CONFIG_DEBUG_ALIGN_RODATA, since that's what the option does: adds
section alignment for making rodata explicitly NX, as arm does not split
the page tables like arm64 does without _ALIGN_RODATA.

Also adds human readable names to the sections so I could more easily
debug my typos, and makes CONFIG_DEBUG_RODATA default "y" for CPU_V7.

Results in /sys/kernel/debug/kernel_page_tables for each config state:

# CONFIG_DEBUG_RODATA is not set
# CONFIG_DEBUG_ALIGN_RODATA is not set

---[ Kernel Mapping ]---
0x80000000-0x80900000 9M RW x SHD
0x80900000-0xa0000000 503M RW NX SHD

CONFIG_DEBUG_RODATA=y
CONFIG_DEBUG_ALIGN_RODATA=y

---[ Kernel Mapping ]---
0x80000000-0x80100000 1M RW NX SHD
0x80100000-0x80700000 6M ro x SHD
0x80700000-0x80a00000 3M ro NX SHD
0x80a00000-0xa0000000 502M RW NX SHD

CONFIG_DEBUG_RODATA=y
# CONFIG_DEBUG_ALIGN_RODATA is not set

---[ Kernel Mapping ]---
0x80000000-0x80100000 1M RW NX SHD
0x80100000-0x80a00000 9M ro x SHD
0x80a00000-0xa0000000 502M RW NX SHD

Signed-off-by: Kees Cook <keescook@chromium.org>
Reviewed-by: Laura Abbott <labbott@fedoraproject.org>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

authored by

Kees Cook and committed by
Russell King
25362dc4 4138323e

+34 -31
+5 -5
arch/arm/kernel/vmlinux.lds.S
··· 8 8 #include <asm/thread_info.h> 9 9 #include <asm/memory.h> 10 10 #include <asm/page.h> 11 - #ifdef CONFIG_ARM_KERNMEM_PERMS 11 + #ifdef CONFIG_DEBUG_RODATA 12 12 #include <asm/pgtable.h> 13 13 #endif 14 14 ··· 94 94 HEAD_TEXT 95 95 } 96 96 97 - #ifdef CONFIG_ARM_KERNMEM_PERMS 97 + #ifdef CONFIG_DEBUG_RODATA 98 98 . = ALIGN(1<<SECTION_SHIFT); 99 99 #endif 100 100 ··· 117 117 ARM_CPU_KEEP(PROC_INFO) 118 118 } 119 119 120 - #ifdef CONFIG_DEBUG_RODATA 120 + #ifdef CONFIG_DEBUG_ALIGN_RODATA 121 121 . = ALIGN(1<<SECTION_SHIFT); 122 122 #endif 123 123 RO_DATA(PAGE_SIZE) ··· 153 153 _etext = .; /* End of text and rodata section */ 154 154 155 155 #ifndef CONFIG_XIP_KERNEL 156 - # ifdef CONFIG_ARM_KERNMEM_PERMS 156 + # ifdef CONFIG_DEBUG_RODATA 157 157 . = ALIGN(1<<SECTION_SHIFT); 158 158 # else 159 159 . = ALIGN(PAGE_SIZE); ··· 231 231 __data_loc = ALIGN(4); /* location in binary */ 232 232 . = PAGE_OFFSET + TEXT_OFFSET; 233 233 #else 234 - #ifdef CONFIG_ARM_KERNMEM_PERMS 234 + #ifdef CONFIG_DEBUG_RODATA 235 235 . = ALIGN(1<<SECTION_SHIFT); 236 236 #else 237 237 . = ALIGN(THREAD_SIZE);
+19 -17
arch/arm/mm/Kconfig
··· 1037 1037 This option specifies the architecture can support big endian 1038 1038 operation. 1039 1039 1040 - config ARM_KERNMEM_PERMS 1041 - bool "Restrict kernel memory permissions" 1042 - depends on MMU 1043 - help 1044 - If this is set, kernel memory other than kernel text (and rodata) 1045 - will be made non-executable. The tradeoff is that each region is 1046 - padded to section-size (1MiB) boundaries (because their permissions 1047 - are different and splitting the 1M pages into 4K ones causes TLB 1048 - performance problems), wasting memory. 1049 - 1050 1040 config DEBUG_RODATA 1051 1041 bool "Make kernel text and rodata read-only" 1052 - depends on ARM_KERNMEM_PERMS 1042 + depends on MMU 1043 + default y if CPU_V7 1044 + help 1045 + If this is set, kernel text and rodata memory will be made 1046 + read-only, and non-text kernel memory will be made non-executable. 1047 + The tradeoff is that each region is padded to section-size (1MiB) 1048 + boundaries (because their permissions are different and splitting 1049 + the 1M pages into 4K ones causes TLB performance problems), which 1050 + can waste memory. 1051 + 1052 + config DEBUG_ALIGN_RODATA 1053 + bool "Make rodata strictly non-executable" 1054 + depends on DEBUG_RODATA 1053 1055 default y 1054 1056 help 1055 - If this is set, kernel text and rodata will be made read-only. This 1056 - is to help catch accidental or malicious attempts to change the 1057 - kernel's executable code. Additionally splits rodata from kernel 1058 - text so it can be made explicitly non-executable. This creates 1059 - another section-size padded region, so it can waste more memory 1060 - space while gaining the read-only protections. 1057 + If this is set, rodata will be made explicitly non-executable. This 1058 + provides protection on the rare chance that attackers might find and 1059 + use ROP gadgets that exist in the rodata section. This adds an 1060 + additional section-aligned split of rodata from kernel text so it 1061 + can be made explicitly non-executable. This padding may waste memory 1062 + space to gain the additional protection.
+10 -9
arch/arm/mm/init.c
··· 572 572 } 573 573 } 574 574 575 - #ifdef CONFIG_ARM_KERNMEM_PERMS 575 + #ifdef CONFIG_DEBUG_RODATA 576 576 struct section_perm { 577 + const char *name; 577 578 unsigned long start; 578 579 unsigned long end; 579 580 pmdval_t mask; ··· 585 584 static struct section_perm nx_perms[] = { 586 585 /* Make pages tables, etc before _stext RW (set NX). */ 587 586 { 587 + .name = "pre-text NX", 588 588 .start = PAGE_OFFSET, 589 589 .end = (unsigned long)_stext, 590 590 .mask = ~PMD_SECT_XN, ··· 593 591 }, 594 592 /* Make init RW (set NX). */ 595 593 { 594 + .name = "init NX", 596 595 .start = (unsigned long)__init_begin, 597 596 .end = (unsigned long)_sdata, 598 597 .mask = ~PMD_SECT_XN, 599 598 .prot = PMD_SECT_XN, 600 599 }, 601 - #ifdef CONFIG_DEBUG_RODATA 600 + #ifdef CONFIG_DEBUG_ALIGN_RODATA 602 601 /* Make rodata NX (set RO in ro_perms below). */ 603 602 { 603 + .name = "rodata NX", 604 604 .start = (unsigned long)__start_rodata, 605 605 .end = (unsigned long)__init_begin, 606 606 .mask = ~PMD_SECT_XN, ··· 611 607 #endif 612 608 }; 613 609 614 - #ifdef CONFIG_DEBUG_RODATA 615 610 static struct section_perm ro_perms[] = { 616 611 /* Make kernel code and rodata RX (set RO). */ 617 612 { 613 + .name = "text/rodata RO", 618 614 .start = (unsigned long)_stext, 619 615 .end = (unsigned long)__init_begin, 620 616 #ifdef CONFIG_ARM_LPAE ··· 627 623 #endif 628 624 }, 629 625 }; 630 - #endif 631 626 632 627 /* 633 628 * Updates section permissions only for the current mm (sections are ··· 673 670 for (i = 0; i < n; i++) { 674 671 if (!IS_ALIGNED(perms[i].start, SECTION_SIZE) || 675 672 !IS_ALIGNED(perms[i].end, SECTION_SIZE)) { 676 - pr_err("BUG: section %lx-%lx not aligned to %lx\n", 677 - perms[i].start, perms[i].end, 673 + pr_err("BUG: %s section %lx-%lx not aligned to %lx\n", 674 + perms[i].name, perms[i].start, perms[i].end, 678 675 SECTION_SIZE); 679 676 continue; 680 677 } ··· 715 712 stop_machine(__fix_kernmem_perms, NULL, NULL); 716 713 } 717 714 718 - #ifdef CONFIG_DEBUG_RODATA 719 715 int __mark_rodata_ro(void *unused) 720 716 { 721 717 update_sections_early(ro_perms, ARRAY_SIZE(ro_perms)); ··· 737 735 set_section_perms(ro_perms, ARRAY_SIZE(ro_perms), true, 738 736 current->active_mm); 739 737 } 740 - #endif /* CONFIG_DEBUG_RODATA */ 741 738 742 739 #else 743 740 static inline void fix_kernmem_perms(void) { } 744 - #endif /* CONFIG_ARM_KERNMEM_PERMS */ 741 + #endif /* CONFIG_DEBUG_RODATA */ 745 742 746 743 void free_tcmmem(void) 747 744 {