forked from mirrors/linux
		
	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
	
	 Jinbum Park
						Jinbum Park