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:
Jinbum Park 2017-02-27 14:30:22 -08:00 committed by Linus Torvalds
parent 8d85063adb
commit 2959a5f726
11 changed files with 90 additions and 105 deletions

View file

@ -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

View file

@ -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 */

View file

@ -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

View file

@ -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;
}

View file

@ -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);

View file

@ -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);

View 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 */

View file

@ -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

View file

@ -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.

View file

@ -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
View 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");
}