Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions tests/kernel/protection/Makefile
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
84 changes: 84 additions & 0 deletions tests/kernel/protection/README.rst
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
2 changes: 2 additions & 0 deletions tests/kernel/protection/prj.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
CONFIG_HEAP_MEM_POOL_SIZE=256
CONFIG_ZTEST=y
5 changes: 5 additions & 0 deletions tests/kernel/protection/src/Makefile
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
170 changes: 170 additions & 0 deletions tests/kernel/protection/src/main.c
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
Copy link
Member

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

Copy link
Contributor Author

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.

*
* 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);
}
14 changes: 14 additions & 0 deletions tests/kernel/protection/src/targets.c
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);
}
12 changes: 12 additions & 0 deletions tests/kernel/protection/src/targets.h
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
3 changes: 3 additions & 0 deletions tests/kernel/protection/testcase.ini
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
10 changes: 10 additions & 0 deletions tests/ztest/include/ztest_test.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,16 @@ void _ztest_run_test_suite(const char *name, struct unit_test *suite);
*/
void ztest_test_fail(void);

/**
* @brief Pass the currently running test.
*
* Normally a test passes just by returning without an assertion failure.
* However, if the success case for your test involves a fatal fault,
* you can call this function from _SysFatalErrorHandler to indicate that
* the test passed before aborting the thread.
*/
void ztest_test_pass(void);

/**
* @brief Do nothing, successfully.
*
Expand Down
18 changes: 18 additions & 0 deletions tests/ztest/src/ztest.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,19 @@ static void run_test_functions(struct unit_test *test)
#define FAIL_FAST 0

static jmp_buf test_fail;
static jmp_buf test_pass;
static jmp_buf stack_fail;

void ztest_test_fail(void)
{
raise(SIGABRT);
}

void ztest_test_pass(void)
{
longjmp(test_pass, 1);
}

static void handle_signal(int sig)
{
static const char *const phase_str[] = {
Expand Down Expand Up @@ -106,6 +112,11 @@ static int run_test(struct unit_test *test)
goto out;
}

if (setjmp(test_pass)) {
ret = TC_PASS;
goto out;
}

run_test_functions(test);
out:
ret |= cleanup_test(test);
Expand Down Expand Up @@ -142,6 +153,13 @@ void ztest_test_fail(void)
k_thread_abort(k_current_get());
}

void ztest_test_pass(void)
{
test_result = 0;
k_sem_give(&test_end_signal);
k_thread_abort(k_current_get());
}

static void init_testing(void)
{
k_sem_init(&test_end_signal, 0, 1);
Expand Down