mm: add arch-independent testcases for RODATA
This patch makes arch-independent testcases for RODATA. Both x86 and x86_64 already have testcases for RODATA, But they are arch-specific because using inline assembly directly. And cacheflush.h is not a suitable location for rodata-test related things. Since they were in cacheflush.h, If someone change the state of CONFIG_DEBUG_RODATA_TEST, It cause overhead of kernel build. To solve the above issues, write arch-independent testcases and move it to shared location. [jinb.park7@gmail.com: fix config dependency] Link: http://lkml.kernel.org/r/20170209131625.GA16954@pjb1027-Latitude-E5410 Link: http://lkml.kernel.org/r/20170129105436.GA9303@pjb1027-Latitude-E5410 Signed-off-by: Jinbum Park <jinb.park7@gmail.com> Acked-by: Kees Cook <keescook@chromium.org> Cc: Ingo Molnar <mingo@redhat.com> Cc: H. Peter Anvin <hpa@zytor.com> Cc: Arjan van de Ven <arjan@linux.intel.com> Cc: Laura Abbott <labbott@redhat.com> Cc: Russell King <linux@armlinux.org.uk> Cc: Valentin Rothberg <valentinrothberg@gmail.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
parent
8d85063adb
commit
2959a5f726
11 changed files with 90 additions and 105 deletions
|
@ -74,14 +74,6 @@ config EFI_PGT_DUMP
|
||||||
issues with the mapping of the EFI runtime regions into that
|
issues with the mapping of the EFI runtime regions into that
|
||||||
table.
|
table.
|
||||||
|
|
||||||
config DEBUG_RODATA_TEST
|
|
||||||
bool "Testcase for the marking rodata read-only"
|
|
||||||
default y
|
|
||||||
---help---
|
|
||||||
This option enables a testcase for the setting rodata read-only
|
|
||||||
as well as for the change_page_attr() infrastructure.
|
|
||||||
If in doubt, say "N"
|
|
||||||
|
|
||||||
config DEBUG_WX
|
config DEBUG_WX
|
||||||
bool "Warn on W+X mappings at boot"
|
bool "Warn on W+X mappings at boot"
|
||||||
select X86_PTDUMP_CORE
|
select X86_PTDUMP_CORE
|
||||||
|
|
|
@ -90,18 +90,8 @@ void clflush_cache_range(void *addr, unsigned int size);
|
||||||
|
|
||||||
#define mmio_flush_range(addr, size) clflush_cache_range(addr, size)
|
#define mmio_flush_range(addr, size) clflush_cache_range(addr, size)
|
||||||
|
|
||||||
extern const int rodata_test_data;
|
|
||||||
extern int kernel_set_to_readonly;
|
extern int kernel_set_to_readonly;
|
||||||
void set_kernel_text_rw(void);
|
void set_kernel_text_rw(void);
|
||||||
void set_kernel_text_ro(void);
|
void set_kernel_text_ro(void);
|
||||||
|
|
||||||
#ifdef CONFIG_DEBUG_RODATA_TEST
|
|
||||||
int rodata_test(void);
|
|
||||||
#else
|
|
||||||
static inline int rodata_test(void)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* _ASM_X86_CACHEFLUSH_H */
|
#endif /* _ASM_X86_CACHEFLUSH_H */
|
||||||
|
|
|
@ -100,7 +100,6 @@ obj-$(CONFIG_HPET_TIMER) += hpet.o
|
||||||
obj-$(CONFIG_APB_TIMER) += apb_timer.o
|
obj-$(CONFIG_APB_TIMER) += apb_timer.o
|
||||||
|
|
||||||
obj-$(CONFIG_AMD_NB) += amd_nb.o
|
obj-$(CONFIG_AMD_NB) += amd_nb.o
|
||||||
obj-$(CONFIG_DEBUG_RODATA_TEST) += test_rodata.o
|
|
||||||
obj-$(CONFIG_DEBUG_NMI_SELFTEST) += nmi_selftest.o
|
obj-$(CONFIG_DEBUG_NMI_SELFTEST) += nmi_selftest.o
|
||||||
|
|
||||||
obj-$(CONFIG_KVM_GUEST) += kvm.o kvmclock.o
|
obj-$(CONFIG_KVM_GUEST) += kvm.o kvmclock.o
|
||||||
|
|
|
@ -1,75 +0,0 @@
|
||||||
/*
|
|
||||||
* test_rodata.c: functional test for mark_rodata_ro function
|
|
||||||
*
|
|
||||||
* (C) Copyright 2008 Intel Corporation
|
|
||||||
* Author: Arjan van de Ven <arjan@linux.intel.com>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License
|
|
||||||
* as published by the Free Software Foundation; version 2
|
|
||||||
* of the License.
|
|
||||||
*/
|
|
||||||
#include <asm/cacheflush.h>
|
|
||||||
#include <asm/sections.h>
|
|
||||||
#include <asm/asm.h>
|
|
||||||
|
|
||||||
int rodata_test(void)
|
|
||||||
{
|
|
||||||
unsigned long result;
|
|
||||||
unsigned long start, end;
|
|
||||||
|
|
||||||
/* test 1: read the value */
|
|
||||||
/* If this test fails, some previous testrun has clobbered the state */
|
|
||||||
if (!rodata_test_data) {
|
|
||||||
printk(KERN_ERR "rodata_test: test 1 fails (start data)\n");
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* test 2: write to the variable; this should fault */
|
|
||||||
/*
|
|
||||||
* If this test fails, we managed to overwrite the data
|
|
||||||
*
|
|
||||||
* This is written in assembly to be able to catch the
|
|
||||||
* exception that is supposed to happen in the correct
|
|
||||||
* case
|
|
||||||
*/
|
|
||||||
|
|
||||||
result = 1;
|
|
||||||
asm volatile(
|
|
||||||
"0: mov %[zero],(%[rodata_test])\n"
|
|
||||||
" mov %[zero], %[rslt]\n"
|
|
||||||
"1:\n"
|
|
||||||
".section .fixup,\"ax\"\n"
|
|
||||||
"2: jmp 1b\n"
|
|
||||||
".previous\n"
|
|
||||||
_ASM_EXTABLE(0b,2b)
|
|
||||||
: [rslt] "=r" (result)
|
|
||||||
: [rodata_test] "r" (&rodata_test_data), [zero] "r" (0UL)
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
if (!result) {
|
|
||||||
printk(KERN_ERR "rodata_test: test data was not read only\n");
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* test 3: check the value hasn't changed */
|
|
||||||
/* If this test fails, we managed to overwrite the data */
|
|
||||||
if (!rodata_test_data) {
|
|
||||||
printk(KERN_ERR "rodata_test: Test 3 fails (end data)\n");
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
/* test 4: check if the rodata section is 4Kb aligned */
|
|
||||||
start = (unsigned long)__start_rodata;
|
|
||||||
end = (unsigned long)__end_rodata;
|
|
||||||
if (start & (PAGE_SIZE - 1)) {
|
|
||||||
printk(KERN_ERR "rodata_test: .rodata is not 4k aligned\n");
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
if (end & (PAGE_SIZE - 1)) {
|
|
||||||
printk(KERN_ERR "rodata_test: .rodata end is not 4k aligned\n");
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
|
@ -864,9 +864,6 @@ static noinline int do_test_wp_bit(void)
|
||||||
return flag;
|
return flag;
|
||||||
}
|
}
|
||||||
|
|
||||||
const int rodata_test_data = 0xC3;
|
|
||||||
EXPORT_SYMBOL_GPL(rodata_test_data);
|
|
||||||
|
|
||||||
int kernel_set_to_readonly __read_mostly;
|
int kernel_set_to_readonly __read_mostly;
|
||||||
|
|
||||||
void set_kernel_text_rw(void)
|
void set_kernel_text_rw(void)
|
||||||
|
@ -939,7 +936,6 @@ void mark_rodata_ro(void)
|
||||||
set_pages_ro(virt_to_page(start), size >> PAGE_SHIFT);
|
set_pages_ro(virt_to_page(start), size >> PAGE_SHIFT);
|
||||||
printk(KERN_INFO "Write protecting the kernel read-only data: %luk\n",
|
printk(KERN_INFO "Write protecting the kernel read-only data: %luk\n",
|
||||||
size >> 10);
|
size >> 10);
|
||||||
rodata_test();
|
|
||||||
|
|
||||||
#ifdef CONFIG_CPA_DEBUG
|
#ifdef CONFIG_CPA_DEBUG
|
||||||
printk(KERN_INFO "Testing CPA: undo %lx-%lx\n", start, start + size);
|
printk(KERN_INFO "Testing CPA: undo %lx-%lx\n", start, start + size);
|
||||||
|
|
|
@ -1000,9 +1000,6 @@ void __init mem_init(void)
|
||||||
mem_init_print_info(NULL);
|
mem_init_print_info(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
const int rodata_test_data = 0xC3;
|
|
||||||
EXPORT_SYMBOL_GPL(rodata_test_data);
|
|
||||||
|
|
||||||
int kernel_set_to_readonly;
|
int kernel_set_to_readonly;
|
||||||
|
|
||||||
void set_kernel_text_rw(void)
|
void set_kernel_text_rw(void)
|
||||||
|
@ -1071,8 +1068,6 @@ void mark_rodata_ro(void)
|
||||||
all_end = roundup((unsigned long)_brk_end, PMD_SIZE);
|
all_end = roundup((unsigned long)_brk_end, PMD_SIZE);
|
||||||
set_memory_nx(text_end, (all_end - text_end) >> PAGE_SHIFT);
|
set_memory_nx(text_end, (all_end - text_end) >> PAGE_SHIFT);
|
||||||
|
|
||||||
rodata_test();
|
|
||||||
|
|
||||||
#ifdef CONFIG_CPA_DEBUG
|
#ifdef CONFIG_CPA_DEBUG
|
||||||
printk(KERN_INFO "Testing CPA: undo %lx-%lx\n", start, end);
|
printk(KERN_INFO "Testing CPA: undo %lx-%lx\n", start, end);
|
||||||
set_memory_rw(start, (end-start) >> PAGE_SHIFT);
|
set_memory_rw(start, (end-start) >> PAGE_SHIFT);
|
||||||
|
|
23
include/linux/rodata_test.h
Normal file
23
include/linux/rodata_test.h
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
* rodata_test.h: functional test for mark_rodata_ro function
|
||||||
|
*
|
||||||
|
* (C) Copyright 2008 Intel Corporation
|
||||||
|
* Author: Arjan van de Ven <arjan@linux.intel.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; version 2
|
||||||
|
* of the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _RODATA_TEST_H
|
||||||
|
#define _RODATA_TEST_H
|
||||||
|
|
||||||
|
#ifdef CONFIG_DEBUG_RODATA_TEST
|
||||||
|
extern const int rodata_test_data;
|
||||||
|
void rodata_test(void);
|
||||||
|
#else
|
||||||
|
static inline void rodata_test(void) {}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* _RODATA_TEST_H */
|
|
@ -82,6 +82,7 @@
|
||||||
#include <linux/proc_ns.h>
|
#include <linux/proc_ns.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/cache.h>
|
#include <linux/cache.h>
|
||||||
|
#include <linux/rodata_test.h>
|
||||||
|
|
||||||
#include <asm/io.h>
|
#include <asm/io.h>
|
||||||
#include <asm/bugs.h>
|
#include <asm/bugs.h>
|
||||||
|
@ -935,9 +936,10 @@ __setup("rodata=", set_debug_rodata);
|
||||||
#ifdef CONFIG_STRICT_KERNEL_RWX
|
#ifdef CONFIG_STRICT_KERNEL_RWX
|
||||||
static void mark_readonly(void)
|
static void mark_readonly(void)
|
||||||
{
|
{
|
||||||
if (rodata_enabled)
|
if (rodata_enabled) {
|
||||||
mark_rodata_ro();
|
mark_rodata_ro();
|
||||||
else
|
rodata_test();
|
||||||
|
} else
|
||||||
pr_info("Kernel memory protection disabled.\n");
|
pr_info("Kernel memory protection disabled.\n");
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
|
|
@ -90,3 +90,9 @@ config DEBUG_PAGE_REF
|
||||||
careful when enabling this feature because it adds about 30 KB to the
|
careful when enabling this feature because it adds about 30 KB to the
|
||||||
kernel code. However the runtime performance overhead is virtually
|
kernel code. However the runtime performance overhead is virtually
|
||||||
nil until the tracepoints are actually enabled.
|
nil until the tracepoints are actually enabled.
|
||||||
|
|
||||||
|
config DEBUG_RODATA_TEST
|
||||||
|
bool "Testcase for the marking rodata read-only"
|
||||||
|
depends on STRICT_KERNEL_RWX
|
||||||
|
---help---
|
||||||
|
This option enables a testcase for the setting rodata read-only.
|
||||||
|
|
|
@ -85,6 +85,7 @@ obj-$(CONFIG_MEMORY_FAILURE) += memory-failure.o
|
||||||
obj-$(CONFIG_HWPOISON_INJECT) += hwpoison-inject.o
|
obj-$(CONFIG_HWPOISON_INJECT) += hwpoison-inject.o
|
||||||
obj-$(CONFIG_DEBUG_KMEMLEAK) += kmemleak.o
|
obj-$(CONFIG_DEBUG_KMEMLEAK) += kmemleak.o
|
||||||
obj-$(CONFIG_DEBUG_KMEMLEAK_TEST) += kmemleak-test.o
|
obj-$(CONFIG_DEBUG_KMEMLEAK_TEST) += kmemleak-test.o
|
||||||
|
obj-$(CONFIG_DEBUG_RODATA_TEST) += rodata_test.o
|
||||||
obj-$(CONFIG_PAGE_OWNER) += page_owner.o
|
obj-$(CONFIG_PAGE_OWNER) += page_owner.o
|
||||||
obj-$(CONFIG_CLEANCACHE) += cleancache.o
|
obj-$(CONFIG_CLEANCACHE) += cleancache.o
|
||||||
obj-$(CONFIG_MEMORY_ISOLATION) += page_isolation.o
|
obj-$(CONFIG_MEMORY_ISOLATION) += page_isolation.o
|
||||||
|
|
56
mm/rodata_test.c
Normal file
56
mm/rodata_test.c
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
/*
|
||||||
|
* rodata_test.c: functional test for mark_rodata_ro function
|
||||||
|
*
|
||||||
|
* (C) Copyright 2008 Intel Corporation
|
||||||
|
* Author: Arjan van de Ven <arjan@linux.intel.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; version 2
|
||||||
|
* of the License.
|
||||||
|
*/
|
||||||
|
#include <linux/uaccess.h>
|
||||||
|
#include <asm/sections.h>
|
||||||
|
|
||||||
|
const int rodata_test_data = 0xC3;
|
||||||
|
EXPORT_SYMBOL_GPL(rodata_test_data);
|
||||||
|
|
||||||
|
void rodata_test(void)
|
||||||
|
{
|
||||||
|
unsigned long start, end;
|
||||||
|
int zero = 0;
|
||||||
|
|
||||||
|
/* test 1: read the value */
|
||||||
|
/* If this test fails, some previous testrun has clobbered the state */
|
||||||
|
if (!rodata_test_data) {
|
||||||
|
pr_err("rodata_test: test 1 fails (start data)\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* test 2: write to the variable; this should fault */
|
||||||
|
if (!probe_kernel_write((void *)&rodata_test_data,
|
||||||
|
(void *)&zero, sizeof(zero))) {
|
||||||
|
pr_err("rodata_test: test data was not read only\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* test 3: check the value hasn't changed */
|
||||||
|
if (rodata_test_data == zero) {
|
||||||
|
pr_err("rodata_test: test data was changed\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* test 4: check if the rodata section is PAGE_SIZE aligned */
|
||||||
|
start = (unsigned long)__start_rodata;
|
||||||
|
end = (unsigned long)__end_rodata;
|
||||||
|
if (start & (PAGE_SIZE - 1)) {
|
||||||
|
pr_err("rodata_test: start of .rodata is not page size aligned\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (end & (PAGE_SIZE - 1)) {
|
||||||
|
pr_err("rodata_test: end of .rodata is not page size aligned\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pr_info("rodata_test: all tests were successful\n");
|
||||||
|
}
|
Loading…
Reference in a new issue