Commit 25362dc4 authored by Kees Cook's avatar Kees Cook Committed by Russell King

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: default avatarKees Cook <keescook@chromium.org>
Reviewed-by: default avatarLaura Abbott <labbott@fedoraproject.org>
Signed-off-by: default avatarRussell King <rmk+kernel@arm.linux.org.uk>
parent 4138323e
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
#include <asm/thread_info.h> #include <asm/thread_info.h>
#include <asm/memory.h> #include <asm/memory.h>
#include <asm/page.h> #include <asm/page.h>
#ifdef CONFIG_ARM_KERNMEM_PERMS #ifdef CONFIG_DEBUG_RODATA
#include <asm/pgtable.h> #include <asm/pgtable.h>
#endif #endif
...@@ -94,7 +94,7 @@ SECTIONS ...@@ -94,7 +94,7 @@ SECTIONS
HEAD_TEXT HEAD_TEXT
} }
#ifdef CONFIG_ARM_KERNMEM_PERMS #ifdef CONFIG_DEBUG_RODATA
. = ALIGN(1<<SECTION_SHIFT); . = ALIGN(1<<SECTION_SHIFT);
#endif #endif
...@@ -117,7 +117,7 @@ SECTIONS ...@@ -117,7 +117,7 @@ SECTIONS
ARM_CPU_KEEP(PROC_INFO) ARM_CPU_KEEP(PROC_INFO)
} }
#ifdef CONFIG_DEBUG_RODATA #ifdef CONFIG_DEBUG_ALIGN_RODATA
. = ALIGN(1<<SECTION_SHIFT); . = ALIGN(1<<SECTION_SHIFT);
#endif #endif
RO_DATA(PAGE_SIZE) RO_DATA(PAGE_SIZE)
...@@ -153,7 +153,7 @@ SECTIONS ...@@ -153,7 +153,7 @@ SECTIONS
_etext = .; /* End of text and rodata section */ _etext = .; /* End of text and rodata section */
#ifndef CONFIG_XIP_KERNEL #ifndef CONFIG_XIP_KERNEL
# ifdef CONFIG_ARM_KERNMEM_PERMS # ifdef CONFIG_DEBUG_RODATA
. = ALIGN(1<<SECTION_SHIFT); . = ALIGN(1<<SECTION_SHIFT);
# else # else
. = ALIGN(PAGE_SIZE); . = ALIGN(PAGE_SIZE);
...@@ -231,7 +231,7 @@ SECTIONS ...@@ -231,7 +231,7 @@ SECTIONS
__data_loc = ALIGN(4); /* location in binary */ __data_loc = ALIGN(4); /* location in binary */
. = PAGE_OFFSET + TEXT_OFFSET; . = PAGE_OFFSET + TEXT_OFFSET;
#else #else
#ifdef CONFIG_ARM_KERNMEM_PERMS #ifdef CONFIG_DEBUG_RODATA
. = ALIGN(1<<SECTION_SHIFT); . = ALIGN(1<<SECTION_SHIFT);
#else #else
. = ALIGN(THREAD_SIZE); . = ALIGN(THREAD_SIZE);
......
...@@ -1037,24 +1037,26 @@ config ARCH_SUPPORTS_BIG_ENDIAN ...@@ -1037,24 +1037,26 @@ config ARCH_SUPPORTS_BIG_ENDIAN
This option specifies the architecture can support big endian This option specifies the architecture can support big endian
operation. operation.
config ARM_KERNMEM_PERMS config DEBUG_RODATA
bool "Restrict kernel memory permissions" bool "Make kernel text and rodata read-only"
depends on MMU depends on MMU
default y if CPU_V7
help help
If this is set, kernel memory other than kernel text (and rodata) If this is set, kernel text and rodata memory will be made
will be made non-executable. The tradeoff is that each region is read-only, and non-text kernel memory will be made non-executable.
padded to section-size (1MiB) boundaries (because their permissions The tradeoff is that each region is padded to section-size (1MiB)
are different and splitting the 1M pages into 4K ones causes TLB boundaries (because their permissions are different and splitting
performance problems), wasting memory. the 1M pages into 4K ones causes TLB performance problems), which
can waste memory.
config DEBUG_RODATA config DEBUG_ALIGN_RODATA
bool "Make kernel text and rodata read-only" bool "Make rodata strictly non-executable"
depends on ARM_KERNMEM_PERMS depends on DEBUG_RODATA
default y default y
help help
If this is set, kernel text and rodata will be made read-only. This If this is set, rodata will be made explicitly non-executable. This
is to help catch accidental or malicious attempts to change the provides protection on the rare chance that attackers might find and
kernel's executable code. Additionally splits rodata from kernel use ROP gadgets that exist in the rodata section. This adds an
text so it can be made explicitly non-executable. This creates additional section-aligned split of rodata from kernel text so it
another section-size padded region, so it can waste more memory can be made explicitly non-executable. This padding may waste memory
space while gaining the read-only protections. space to gain the additional protection.
...@@ -572,8 +572,9 @@ void __init mem_init(void) ...@@ -572,8 +572,9 @@ void __init mem_init(void)
} }
} }
#ifdef CONFIG_ARM_KERNMEM_PERMS #ifdef CONFIG_DEBUG_RODATA
struct section_perm { struct section_perm {
const char *name;
unsigned long start; unsigned long start;
unsigned long end; unsigned long end;
pmdval_t mask; pmdval_t mask;
...@@ -584,6 +585,7 @@ struct section_perm { ...@@ -584,6 +585,7 @@ struct section_perm {
static struct section_perm nx_perms[] = { static struct section_perm nx_perms[] = {
/* Make pages tables, etc before _stext RW (set NX). */ /* Make pages tables, etc before _stext RW (set NX). */
{ {
.name = "pre-text NX",
.start = PAGE_OFFSET, .start = PAGE_OFFSET,
.end = (unsigned long)_stext, .end = (unsigned long)_stext,
.mask = ~PMD_SECT_XN, .mask = ~PMD_SECT_XN,
...@@ -591,14 +593,16 @@ static struct section_perm nx_perms[] = { ...@@ -591,14 +593,16 @@ static struct section_perm nx_perms[] = {
}, },
/* Make init RW (set NX). */ /* Make init RW (set NX). */
{ {
.name = "init NX",
.start = (unsigned long)__init_begin, .start = (unsigned long)__init_begin,
.end = (unsigned long)_sdata, .end = (unsigned long)_sdata,
.mask = ~PMD_SECT_XN, .mask = ~PMD_SECT_XN,
.prot = PMD_SECT_XN, .prot = PMD_SECT_XN,
}, },
#ifdef CONFIG_DEBUG_RODATA #ifdef CONFIG_DEBUG_ALIGN_RODATA
/* Make rodata NX (set RO in ro_perms below). */ /* Make rodata NX (set RO in ro_perms below). */
{ {
.name = "rodata NX",
.start = (unsigned long)__start_rodata, .start = (unsigned long)__start_rodata,
.end = (unsigned long)__init_begin, .end = (unsigned long)__init_begin,
.mask = ~PMD_SECT_XN, .mask = ~PMD_SECT_XN,
...@@ -607,10 +611,10 @@ static struct section_perm nx_perms[] = { ...@@ -607,10 +611,10 @@ static struct section_perm nx_perms[] = {
#endif #endif
}; };
#ifdef CONFIG_DEBUG_RODATA
static struct section_perm ro_perms[] = { static struct section_perm ro_perms[] = {
/* Make kernel code and rodata RX (set RO). */ /* Make kernel code and rodata RX (set RO). */
{ {
.name = "text/rodata RO",
.start = (unsigned long)_stext, .start = (unsigned long)_stext,
.end = (unsigned long)__init_begin, .end = (unsigned long)__init_begin,
#ifdef CONFIG_ARM_LPAE #ifdef CONFIG_ARM_LPAE
...@@ -623,7 +627,6 @@ static struct section_perm ro_perms[] = { ...@@ -623,7 +627,6 @@ static struct section_perm ro_perms[] = {
#endif #endif
}, },
}; };
#endif
/* /*
* Updates section permissions only for the current mm (sections are * Updates section permissions only for the current mm (sections are
...@@ -670,8 +673,8 @@ void set_section_perms(struct section_perm *perms, int n, bool set, ...@@ -670,8 +673,8 @@ void set_section_perms(struct section_perm *perms, int n, bool set,
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
if (!IS_ALIGNED(perms[i].start, SECTION_SIZE) || if (!IS_ALIGNED(perms[i].start, SECTION_SIZE) ||
!IS_ALIGNED(perms[i].end, SECTION_SIZE)) { !IS_ALIGNED(perms[i].end, SECTION_SIZE)) {
pr_err("BUG: section %lx-%lx not aligned to %lx\n", pr_err("BUG: %s section %lx-%lx not aligned to %lx\n",
perms[i].start, perms[i].end, perms[i].name, perms[i].start, perms[i].end,
SECTION_SIZE); SECTION_SIZE);
continue; continue;
} }
...@@ -712,7 +715,6 @@ void fix_kernmem_perms(void) ...@@ -712,7 +715,6 @@ void fix_kernmem_perms(void)
stop_machine(__fix_kernmem_perms, NULL, NULL); stop_machine(__fix_kernmem_perms, NULL, NULL);
} }
#ifdef CONFIG_DEBUG_RODATA
int __mark_rodata_ro(void *unused) int __mark_rodata_ro(void *unused)
{ {
update_sections_early(ro_perms, ARRAY_SIZE(ro_perms)); update_sections_early(ro_perms, ARRAY_SIZE(ro_perms));
...@@ -735,11 +737,10 @@ void set_kernel_text_ro(void) ...@@ -735,11 +737,10 @@ void set_kernel_text_ro(void)
set_section_perms(ro_perms, ARRAY_SIZE(ro_perms), true, set_section_perms(ro_perms, ARRAY_SIZE(ro_perms), true,
current->active_mm); current->active_mm);
} }
#endif /* CONFIG_DEBUG_RODATA */
#else #else
static inline void fix_kernmem_perms(void) { } static inline void fix_kernmem_perms(void) { }
#endif /* CONFIG_ARM_KERNMEM_PERMS */ #endif /* CONFIG_DEBUG_RODATA */
void free_tcmmem(void) void free_tcmmem(void)
{ {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment