-
Notifications
You must be signed in to change notification settings - Fork 8.2k
[RFC] samples: Add a self-protection test suite #493
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| BOARD ?= frdm_k64f | ||
| CONF_FILE = prj.conf | ||
|
|
||
| include ${ZEPHYR_BASE}/Makefile.test |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,84 @@ | ||
| .. _protection_tests: | ||
|
|
||
| Protection tests | ||
| ################################# | ||
|
|
||
| Overview | ||
| ******** | ||
| This test case verifies that protection is provided | ||
| against the following security issues: | ||
|
|
||
| * Write to read-only data. | ||
| * Write to text. | ||
| * Execute from data. | ||
| * Execute from stack. | ||
| * Execute from heap. | ||
|
|
||
| Building and Running | ||
| ******************** | ||
|
|
||
| This project can be built and executed as follows: | ||
|
|
||
| .. code-block:: console | ||
|
|
||
| $ cd tests/protection | ||
| $ make BOARD=<insert your board here> | ||
|
|
||
| Connect the board to your host computer using the USB port. | ||
| Flash the generated zephyr.bin on the board. | ||
| Reset the board. | ||
|
|
||
| Sample Output | ||
| ============= | ||
|
|
||
| .. code-block:: console | ||
|
|
||
| ***** BOOTING ZEPHYR OS v1.8.99 - BUILD: Jun 19 2017 12:44:27 ***** | ||
| Running test suite test_protection | ||
| tc_start() - write_ro | ||
| trying to write to rodata at 0x00003124 | ||
| ***** BUS FAULT ***** | ||
| Executing thread ID (thread): 0x200001bc | ||
| Faulting instruction address: 0x88c | ||
| Imprecise data bus error | ||
| Caught system error -- reason 0 | ||
| =================================================================== | ||
| PASS - write_ro. | ||
| tc_start() - write_text | ||
| trying to write to text at 0x000006c0 | ||
| ***** BUS FAULT ***** | ||
| Executing thread ID (thread): 0x200001bc | ||
| Faulting instruction address: 0xd60 | ||
| Imprecise data bus error | ||
| Caught system error -- reason 0 | ||
| =================================================================== | ||
| PASS - write_text. | ||
| tc_start() - exec_data | ||
| trying to call code written to 0x2000041d | ||
| ***** BUS FAULT ***** | ||
| Executing thread ID (thread): 0x200001bc | ||
| Faulting instruction address: 0x2000041c | ||
| Imprecise data bus error | ||
| Caught system error -- reason 0 | ||
| =================================================================== | ||
| PASS - exec_data. | ||
| tc_start() - exec_stack | ||
| trying to call code written to 0x20000929 | ||
| ***** BUS FAULT ***** | ||
| Executing thread ID (thread): 0x200001bc | ||
| Faulting instruction address: 0x20000928 | ||
| Imprecise data bus error | ||
| Caught system error -- reason 0 | ||
| =================================================================== | ||
| PASS - exec_stack. | ||
| tc_start() - exec_heap | ||
| trying to call code written to 0x20000455 | ||
| ***** BUS FAULT ***** | ||
| Executing thread ID (thread): 0x200001bc | ||
| Faulting instruction address: 0x20000454 | ||
| Imprecise data bus error | ||
| Caught system error -- reason 0 | ||
| =================================================================== | ||
| PASS - exec_heap. | ||
| =================================================================== | ||
| PROJECT EXECUTION SUCCESSFUL |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| CONFIG_HEAP_MEM_POOL_SIZE=256 | ||
| CONFIG_ZTEST=y |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| include $(ZEPHYR_BASE)/tests/Makefile.test | ||
|
|
||
| ccflags-y += -I${ZEPHYR_BASE}/tests/include | ||
|
|
||
| obj-y = targets.o main.o |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,170 @@ | ||
| /* | ||
| * Parts derived from tests/kernel/fatal/src/main.c, which has the | ||
| * following copyright and license: | ||
| * | ||
| * Copyright (c) 2017 Intel Corporation | ||
| * | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
|
|
||
| #include <zephyr.h> | ||
| #include <ztest.h> | ||
| #include <kernel_structs.h> | ||
| #include <string.h> | ||
| #include <stdlib.h> | ||
|
|
||
| #include "targets.h" | ||
|
|
||
| #define INFO(fmt, ...) printk(fmt, ##__VA_ARGS__) | ||
|
|
||
| /* ARM is a special case, in that k_thread_abort() does indeed return | ||
| * instead of calling _Swap() directly. The PendSV exception is queued | ||
| * and immediately fires upon completing the exception path; the faulting | ||
| * thread is never run again. | ||
| */ | ||
| #ifndef CONFIG_ARM | ||
| FUNC_NORETURN | ||
| #endif | ||
| void _SysFatalErrorHandler(unsigned int reason, const NANO_ESF *pEsf) | ||
| { | ||
| INFO("Caught system error -- reason %d\n", reason); | ||
| ztest_test_pass(); | ||
| #ifndef CONFIG_ARM | ||
| CODE_UNREACHABLE; | ||
| #endif | ||
| } | ||
|
|
||
| #ifdef CONFIG_CPU_CORTEX_M | ||
| #include <arch/arm/cortex_m/cmsis.h> | ||
| /* Must clear LSB of function address to access as data. */ | ||
| #define FUNC_TO_PTR(x) (void *)((uintptr_t)(x) & ~0x1) | ||
| /* Must set LSB of function address to call in Thumb mode. */ | ||
| #define PTR_TO_FUNC(x) (int (*)(int))((uintptr_t)(x) | 0x1) | ||
| /* Flush preceding data writes and instruction fetches. */ | ||
| #define DO_BARRIERS() do { __DSB(); __ISB(); } while (0) | ||
| #else | ||
| #define FUNC_TO_PTR(x) (void *)(x) | ||
| #define PTR_TO_FUNC(x) (int (*)(int))(x) | ||
| #define DO_BARRIERS() do { } while (0) | ||
| #endif | ||
|
|
||
| static int __attribute__((noinline)) add_one(int i) | ||
| { | ||
| return (i + 1); | ||
| } | ||
|
|
||
| static void execute_from_buffer(u8_t *dst) | ||
| { | ||
| void *src = FUNC_TO_PTR(add_one); | ||
| int (*func)(int i) = PTR_TO_FUNC(dst); | ||
| int i = 1; | ||
|
|
||
| /* Copy add_one() code to destination buffer. */ | ||
| memcpy(dst, src, BUF_SIZE); | ||
| DO_BARRIERS(); | ||
|
|
||
| /* | ||
| * Try executing from buffer we just filled. | ||
| * Optimally, this triggers a fault. | ||
| * If not, we check to see if the function | ||
| * returned the expected result as confirmation | ||
| * that we truly executed the code we wrote. | ||
| */ | ||
| INFO("trying to call code written to %p\n", func); | ||
| i = func(i); | ||
| INFO("returned from code at %p\n", func); | ||
| if (i == 2) { | ||
| INFO("Execute from target buffer succeeded!\n"); | ||
| } else { | ||
| INFO("Did not get expected return value!\n"); | ||
| } | ||
| } | ||
|
|
||
| static void write_ro(void) | ||
| { | ||
| u32_t *ptr = (u32_t *)&rodata_var; | ||
|
|
||
| /* | ||
| * Try writing to rodata. Optimally, this triggers a fault. | ||
| * If not, we check to see if the rodata value actually changed. | ||
| */ | ||
| INFO("trying to write to rodata at %p\n", ptr); | ||
| *ptr = ~RODATA_VALUE; | ||
|
|
||
| DO_BARRIERS(); | ||
|
|
||
| if (*ptr == RODATA_VALUE) { | ||
| INFO("rodata value still the same\n"); | ||
| } else if (*ptr == ~RODATA_VALUE) { | ||
| INFO("rodata modified!\n"); | ||
| } else { | ||
| INFO("something went wrong!\n"); | ||
| } | ||
|
|
||
| zassert_unreachable("Write to rodata did not fault"); | ||
| } | ||
|
|
||
| static void write_text(void) | ||
| { | ||
| void *src = FUNC_TO_PTR(add_one); | ||
| void *dst = FUNC_TO_PTR(overwrite_target); | ||
| int i = 1; | ||
|
|
||
| /* | ||
| * Try writing to a function in the text section. | ||
| * Optimally, this triggers a fault. | ||
| * If not, we try calling the function after overwriting | ||
| * to see if it returns the expected result as | ||
| * confirmation that we truly executed the code we wrote. | ||
| */ | ||
| INFO("trying to write to text at %p\n", dst); | ||
| memcpy(dst, src, BUF_SIZE); | ||
| DO_BARRIERS(); | ||
| i = overwrite_target(i); | ||
| if (i == 2) { | ||
| INFO("Overwrite of text succeeded!\n"); | ||
| } else { | ||
| INFO("Did not get expected return value!\n"); | ||
| } | ||
|
|
||
| zassert_unreachable("Write to text did not fault"); | ||
| } | ||
|
|
||
| static void exec_data(void) | ||
| { | ||
| execute_from_buffer(data_buf); | ||
| zassert_unreachable("Execute from data did not fault"); | ||
| } | ||
|
|
||
| static void exec_stack(void) | ||
| { | ||
| u8_t stack_buf[BUF_SIZE] __aligned(sizeof(int)); | ||
|
|
||
| execute_from_buffer(stack_buf); | ||
| zassert_unreachable("Execute from stack did not fault"); | ||
| } | ||
|
|
||
| #if (CONFIG_HEAP_MEM_POOL_SIZE > 0) | ||
| static void exec_heap(void) | ||
| { | ||
| u8_t *heap_buf = k_malloc(BUF_SIZE); | ||
|
|
||
| execute_from_buffer(heap_buf); | ||
| k_free(heap_buf); | ||
| zassert_unreachable("Execute from heap did not fault"); | ||
| } | ||
| #endif | ||
|
|
||
| void test_main(void *unused1, void *unused2, void *unused3) | ||
| { | ||
| ztest_test_suite(test_protection, | ||
| ztest_unit_test(write_ro), | ||
| ztest_unit_test(write_text), | ||
| ztest_unit_test(exec_data), | ||
| ztest_unit_test(exec_stack) | ||
| #if (CONFIG_HEAP_MEM_POOL_SIZE > 0) | ||
| , ztest_unit_test(exec_heap) | ||
| #endif | ||
| ); | ||
| ztest_run_test_suite(test_protection); | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| #include <zephyr.h> | ||
| #include <misc/printk.h> | ||
|
|
||
| #include "targets.h" | ||
|
|
||
| const u32_t rodata_var = RODATA_VALUE; | ||
|
|
||
| u8_t data_buf[BUF_SIZE] __aligned(sizeof(int)); | ||
|
|
||
| int overwrite_target(int i) | ||
| { | ||
| printk("text not modified\n"); | ||
| return (i - 1); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| #ifndef _PROT_TEST_TARGETS_H_ | ||
| #define _PROT_TEST_TARGETS_H_ | ||
|
|
||
| #define RODATA_VALUE 0xF00FF00F | ||
| extern const u32_t rodata_var; | ||
|
|
||
| #define BUF_SIZE 16 | ||
| extern u8_t data_buf[BUF_SIZE]; | ||
|
|
||
| extern int overwrite_target(int i); | ||
|
|
||
| #endif |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| [test] | ||
| tags = core security ignore_faults | ||
| filter = CONFIG_CPU_HAS_MPU or CONFIG_X86_MMU |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can use your own copyright
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only relevant copyright is that of tests/kernel/fatal/src/main.c, from which I copied the _SysFatalErrorHandler() definition (and originally a few other bits, but those got replaced in converting to using ztest). No copyright for anything I wrote, since USG works are public domain by statute.