diff --git a/Makefile.host b/Makefile.host index 347253c8..f92339cd 100644 --- a/Makefile.host +++ b/Makefile.host @@ -1,3 +1,4 @@ DEFAULT_COMPONENTS = $(filter test_meterfs_%,$(ALL_COMPONENTS)) DEFAULT_COMPONENTS += $(SAMPLE_TESTS) DEFAULT_COMPONENTS += $(LIBC_UNIT_TESTS) +DEFAULT_COMPONENTS += test-mprotect diff --git a/mem/Makefile b/mem/Makefile index 32c08178..5469d241 100644 --- a/mem/Makefile +++ b/mem/Makefile @@ -1,10 +1,25 @@ # -# Makefile for libphoenix tests +# Makefile for mem tests # # Copyright 2019 Phoenix Systems # # %LICENSE% # +# LEGACY $(eval $(call add_test, test_mmap)) $(eval $(call add_test, test_malloc)) + +# UNITY + +NAME := test-mprotect +LOCAL_SRCS := test_mprotect.c +DEP_LIBS := unity + +include $(binary.mk) + +NAME := test-mprotect-fault +LOCAL_SRCS := test_mprotect_fault.c +DEP_LIBS := unity + +include $(binary.mk) \ No newline at end of file diff --git a/mem/fault_harness.py b/mem/fault_harness.py new file mode 100644 index 00000000..8ab01c92 --- /dev/null +++ b/mem/fault_harness.py @@ -0,0 +1,9 @@ +from trunner.ctx import TestContext +from trunner.dut import Dut +from trunner.types import TestResult, Status + + +def harness(dut: Dut, ctx: TestContext, result: TestResult): + dut.expect("Exception", timeout=30) + + return TestResult(status=Status.OK) diff --git a/mem/test.yaml b/mem/test.yaml new file mode 100644 index 00000000..101a5baa --- /dev/null +++ b/mem/test.yaml @@ -0,0 +1,19 @@ +test: + tests: + - name: mprotect-fault + harness: fault_harness.py + execute: test-mprotect-fault + targets: + # mprotect: is noop on NOMMU architecture + exclude: [armv7m7-imxrt106x-evk, armv7m7-imxrt117x-evk, armv7m4-stm32l4x6-nucleo] + + - name: mprotect + type: unity + execute: test-mprotect + # FIXME: this reboot is placed here as after mprotect-fault is run target need to be rebooter. + # Currently rebooting after the test is not implemented in the runner. + reboot: True + targets: + # mprotect: is noop on NOMMU architecture + exclude: [armv7m7-imxrt106x-evk, armv7m7-imxrt117x-evk, armv7m4-stm32l4x6-nucleo] + diff --git a/mem/test_mprotect.c b/mem/test_mprotect.c new file mode 100644 index 00000000..1543ca3a --- /dev/null +++ b/mem/test_mprotect.c @@ -0,0 +1,181 @@ +/* + * Phoenix-RTOS + * + * phoenix-rtos-test + * + * mprotect syscall tests + * + * Copyright 2023 Phoenix Systems + * Author: Hubert Badocha + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#include +#include +#include +#include + +#include "unity_fixture.h" + +static long page_size; + +#define PAGES 4 + +TEST_GROUP(test_mprotect); + + +TEST_SETUP(test_mprotect) +{ + page_size = sysconf(_SC_PAGESIZE); +} + + +TEST_TEAR_DOWN(test_mprotect) +{ +} + + +TEST(test_mprotect, test_mprotect_singlecore) +{ + unsigned char *area = mmap(0, page_size * PAGES, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + TEST_ASSERT_NOT_NULL(area); + + for (int page = 0; page < PAGES; page++) { + area[page * page_size] = 0x42; + } + + TEST_ASSERT_EQUAL(0, mprotect(area, page_size * PAGES, PROT_READ)); + + for (int page = 0; page < PAGES; page++) { + TEST_ASSERT_EQUAL(0x42, area[page * page_size]); + } + + TEST_ASSERT_EQUAL(0, mprotect(area, page_size * PAGES, PROT_READ | PROT_WRITE)); + + for (int page = 0; page < PAGES; page++) { + area[(page * page_size) + 0x6] = 0x9; + TEST_ASSERT_EQUAL(0x9, area[(page * page_size) + 0x6]); + } + + TEST_ASSERT_EQUAL(0, munmap(area, page_size * PAGES)); +} + + +struct mprotect_data { + pthread_cond_t cond; + pthread_mutex_t mutex; + enum { PROTECT, + USE, + EXIT } state; + int prot; + int res; + unsigned char *area; + size_t size; +}; + + +static void *mprotect_protect_thread(void *arg) +{ + struct mprotect_data *data = arg; + + TEST_ASSERT_EQUAL(0, pthread_mutex_lock(&data->mutex)); + + while (data->state != EXIT) { + if (data->state == PROTECT) { + data->res = mprotect(data->area, data->size, data->prot); + data->state = USE; + pthread_cond_signal(&data->cond); + } + TEST_ASSERT_EQUAL(0, pthread_cond_wait(&data->cond, &data->mutex)); + } + + TEST_ASSERT_EQUAL(0, pthread_mutex_unlock(&data->mutex)); + + return NULL; +} + + +static int mprotect_in_thread(struct mprotect_data *data, int prot) +{ + data->prot = prot; + data->state = PROTECT; + + TEST_ASSERT_EQUAL(0, pthread_cond_signal(&data->cond)); + + do { + TEST_ASSERT_EQUAL(0, pthread_cond_wait(&data->cond, &data->mutex)); + } while (data->state != USE); + + return data->res; +} + + +TEST(test_mprotect, test_mprotect_multicore) +{ + pthread_t thread; + struct mprotect_data data = { + .state = PROTECT, + .size = page_size * PAGES, + .prot = PROT_NONE, + }; + data.area = mmap(0, page_size * PAGES, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + TEST_ASSERT_NOT_NULL(data.area); + TEST_ASSERT_EQUAL(0, pthread_cond_init(&data.cond, NULL)); + TEST_ASSERT_EQUAL(0, pthread_mutex_init(&data.mutex, NULL)); + + TEST_ASSERT_EQUAL(0, pthread_create(&thread, NULL, mprotect_protect_thread, &data)); + + TEST_ASSERT_EQUAL(0, pthread_mutex_lock(&data.mutex)); + + for (int page = 0; page < PAGES; page++) { + data.area[page * page_size] = 0x42; + } + + TEST_ASSERT_EQUAL(0, mprotect_in_thread(&data, PROT_READ)); + + for (int page = 0; page < PAGES; page++) { + TEST_ASSERT_EQUAL(0x42, data.area[page * page_size]); + } + + TEST_ASSERT_EQUAL(0, mprotect_in_thread(&data, PROT_READ | PROT_WRITE)); + + for (int page = 0; page < PAGES; page++) { + data.area[(page * page_size) + 0x6] = 0x9; + TEST_ASSERT_EQUAL(0x9, data.area[(page * page_size) + 0x6]); + } + + data.state = EXIT; + TEST_ASSERT_EQUAL(0, pthread_cond_signal(&data.cond)); + TEST_ASSERT_EQUAL(0, pthread_mutex_unlock(&data.mutex)); + + + TEST_ASSERT_EQUAL(0, pthread_join(thread, NULL)); + + TEST_ASSERT_EQUAL(0, munmap(data.area, data.size)); + TEST_ASSERT_EQUAL(0, pthread_cond_destroy(&data.cond)); + TEST_ASSERT_EQUAL(0, pthread_mutex_destroy(&data.mutex)); +} + + +TEST_GROUP_RUNNER(test_mprotect) +{ + RUN_TEST_CASE(test_mprotect, test_mprotect_singlecore); + RUN_TEST_CASE(test_mprotect, test_mprotect_multicore); +} + + +static void runner(void) +{ + RUN_TEST_GROUP(test_mprotect); +} + + +int main(int argc, char *argv[]) +{ + UnityMain(argc, (const char **)argv, runner); + + return 0; +} \ No newline at end of file diff --git a/mem/test_mprotect_fault.c b/mem/test_mprotect_fault.c new file mode 100644 index 00000000..fb0ded20 --- /dev/null +++ b/mem/test_mprotect_fault.c @@ -0,0 +1,68 @@ +/* + * Phoenix-RTOS + * + * phoenix-rtos-test + * + * mprotect syscall tests + * + * Copyright 2023 Phoenix Systems + * Author: Hubert Badocha + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#include +#include +#include +#include + +#include "unity_fixture.h" + + +TEST_GROUP(test_mprotect_fault); + + +TEST_SETUP(test_mprotect_fault) +{ +} + + +TEST_TEAR_DOWN(test_mprotect_fault) +{ +} + + +TEST(test_mprotect_fault, unit) +{ + long pageSize = sysconf(_SC_PAGESIZE); + long totalSize = 4 * pageSize; + volatile unsigned char *area = mmap(NULL, totalSize, PROT_READ | PROT_WRITE, MAP_ANONYMOUS, -1, 0); + TEST_ASSERT_NOT_NULL(area); + + TEST_ASSERT_EQUAL(0, mprotect(area, totalSize, PROT_READ)); + + area[0] = 0x42; /* Should generate fault. */ + TEST_ASSERT_EQUAL(0x42, area[0]); /* Here to make sure compiler doesn't optimize away write. */ +} + + +TEST_GROUP_RUNNER(test_mprotect_fault) +{ + RUN_TEST_CASE(test_mprotect_fault, unit); +} + + +static void runner(void) +{ + RUN_TEST_GROUP(test_mprotect_fault); +} + + +int main(int argc, char *argv[]) +{ + UnityMain(argc, (const char **)argv, runner); + + return 0; +} \ No newline at end of file