From 58d374ca58acec46b6fb9d00d920578d713ef5e8 Mon Sep 17 00:00:00 2001 From: Teufelchen1 Date: Mon, 12 Jun 2023 13:08:02 +0200 Subject: [PATCH] tests/riscv: Add pmp noexecute RAM test --- Makefile.dep | 4 +++ core/lib/include/panic.h | 2 +- cpu/fe310/cpu.c | 15 +++++++++ cpu/riscv_common/irq_arch.c | 4 +++ makefiles/pseudomodules.inc.mk | 9 +++++ tests/cpu/pmp_noexec_ram/Makefile | 7 ++++ tests/cpu/pmp_noexec_ram/README.md | 11 ++++++ tests/cpu/pmp_noexec_ram/main.c | 43 ++++++++++++++++++++++++ tests/cpu/pmp_noexec_ram/tests/01-run.py | 18 ++++++++++ 9 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 tests/cpu/pmp_noexec_ram/Makefile create mode 100644 tests/cpu/pmp_noexec_ram/README.md create mode 100644 tests/cpu/pmp_noexec_ram/main.c create mode 100755 tests/cpu/pmp_noexec_ram/tests/01-run.py diff --git a/Makefile.dep b/Makefile.dep index db56c9c1f0b9..eca538a084d4 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -30,6 +30,10 @@ ifneq (,$(filter mpu_noexec_ram,$(USEMODULE))) FEATURES_REQUIRED += cortexm_mpu endif +ifneq (,$(filter pmp_noexec_ram,$(USEMODULE))) + FEATURES_REQUIRED += periph_pmp +endif + ifneq (,$(filter lwip_%,$(USEMODULE))) USEPKG += lwip endif diff --git a/core/lib/include/panic.h b/core/lib/include/panic.h index 51854b51609a..2fd453805f40 100644 --- a/core/lib/include/panic.h +++ b/core/lib/include/panic.h @@ -37,13 +37,13 @@ typedef enum { PANIC_HARD_REBOOT, PANIC_ASSERT_FAIL, PANIC_EXPECT_FAIL, + PANIC_MEM_MANAGE, /**< memory management fault */ #ifdef MODULE_CORTEXM_COMMON PANIC_NMI_HANDLER, /**< non maskable interrupt */ PANIC_HARD_FAULT, /**< hard fault */ #if defined(CPU_CORE_CORTEX_M3) || defined(CPU_CORE_CORTEX_M33) || \ defined(CPU_CORE_CORTEX_M4) || defined(CPU_CORE_CORTEX_M4F) || \ defined(CPU_CORE_CORTEX_M7) - PANIC_MEM_MANAGE, /**< memory controller interrupt */ PANIC_BUS_FAULT, /**< bus fault */ PANIC_USAGE_FAULT, /**< undefined instruction or unaligned access */ PANIC_DEBUG_MON, /**< debug interrupt */ diff --git a/cpu/fe310/cpu.c b/cpu/fe310/cpu.c index 9130f438243c..2a6a31e894d7 100644 --- a/cpu/fe310/cpu.c +++ b/cpu/fe310/cpu.c @@ -22,6 +22,9 @@ #include "kernel_init.h" #include "periph/init.h" #include "periph_conf.h" +#ifdef MODULE_PMP_NOEXEC_RAM +#include "pmp.h" +#endif #include "vendor/riscv_csr.h" @@ -108,6 +111,18 @@ void cpu_init(void) /* Common RISC-V initialization */ riscv_init(); +#ifdef MODULE_PMP_NOEXEC_RAM + /* This marks the complete RAM region from 0x80000000 to 0x80004000 as non + * executable. Using PMP entry 0. + * + * RAM starts at 2 GiB and is 16K in size + */ + write_pmpaddr(0, make_napot(0x80000000, 0x4000)); + + /* Lock & select NAPOT, only allow write and read */ + set_pmpcfg(0, PMP_L | PMP_NAPOT | PMP_W | PMP_R); +#endif + /* Initialize stdio */ early_init(); diff --git a/cpu/riscv_common/irq_arch.c b/cpu/riscv_common/irq_arch.c index 41782d58770c..5d53c0720fd5 100644 --- a/cpu/riscv_common/irq_arch.c +++ b/cpu/riscv_common/irq_arch.c @@ -134,6 +134,10 @@ static void handle_trap(uword_t mcause) write_csr(mepc, return_pc + 4); break; } + case CAUSE_FAULT_FETCH: + case CAUSE_FAULT_LOAD: + case CAUSE_FAULT_STORE: + core_panic(PANIC_MEM_MANAGE, "MEM MANAGE HANDLER"); default: #ifdef DEVELHELP printf("Unhandled trap:\n"); diff --git a/makefiles/pseudomodules.inc.mk b/makefiles/pseudomodules.inc.mk index c26a8776cdf4..055ff17a9982 100644 --- a/makefiles/pseudomodules.inc.mk +++ b/makefiles/pseudomodules.inc.mk @@ -320,6 +320,15 @@ PSEUDOMODULES += mpu_stack_guard PSEUDOMODULES += mpu_noexec_ram ## @} +## @defgroup pseudomodule_pmp_noexec_ram pmp_noexec_ram +## @{ +## @brief Mark RAM as non-executable using the PMP +## +## Mark the RAM non executable. +## This is a protection mechanism which makes exploitation of buffer overflows significantly harder. +PSEUDOMODULES += pmp_noexec_ram +## @} + ## @defgroup pseudomodule_md5sum md5sum ## @ingroup sys_shell_commands ## @{ diff --git a/tests/cpu/pmp_noexec_ram/Makefile b/tests/cpu/pmp_noexec_ram/Makefile new file mode 100644 index 000000000000..a98d3a00a305 --- /dev/null +++ b/tests/cpu/pmp_noexec_ram/Makefile @@ -0,0 +1,7 @@ +BOARD ?= hifive1b + +include ../Makefile.cpu_common + +USEMODULE += pmp_noexec_ram + +include $(RIOTBASE)/Makefile.include diff --git a/tests/cpu/pmp_noexec_ram/README.md b/tests/cpu/pmp_noexec_ram/README.md new file mode 100644 index 000000000000..a0fdba1bc69f --- /dev/null +++ b/tests/cpu/pmp_noexec_ram/README.md @@ -0,0 +1,11 @@ +# mpu_noexec_ram + +Tests for the `pmp_noexec_ram` pseudomodule. +Only supported on RISC-V devices with PMP. + +## Output + +With `USEMODULE += pmp_noexec_ram` in `Makefile` this application should +execute a kernel panic, stating `Instruction access fault` (0x01) in the +`mcause` reigster. Without this pseudomodule activated, the hard fault +will be triggered by `Illegal instruction` (0x02) instead. diff --git a/tests/cpu/pmp_noexec_ram/main.c b/tests/cpu/pmp_noexec_ram/main.c new file mode 100644 index 000000000000..8b6c705146df --- /dev/null +++ b/tests/cpu/pmp_noexec_ram/main.c @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2023 Bennet Blischke + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup tests + * @{ + * + * @file + * @brief Test application for the pmp_noexec_ram pseudo-module + * + * @author Sören Tempel + * @author Bennet Blischke + * + * @} + */ + +#include +#include + +#include "cpu.h" +#include "pmp.h" + +#define JMPBUF_SIZE 3 + +int main(void) +{ + uint32_t buf[JMPBUF_SIZE]; + + /* Fill the buffer with invalid instructions */ + for (unsigned i = 0; i < JMPBUF_SIZE; i++) { + buf[i] = UINT32_MAX; + } + + puts("Attempting to jump to stack buffer ...\n"); + __asm__ volatile ("jr %0" :: "r" ((uint8_t*)&buf)); + + return 0; +} diff --git a/tests/cpu/pmp_noexec_ram/tests/01-run.py b/tests/cpu/pmp_noexec_ram/tests/01-run.py new file mode 100755 index 000000000000..8340c1407ae3 --- /dev/null +++ b/tests/cpu/pmp_noexec_ram/tests/01-run.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 + +# Copyright (C) 2020 Sören Tempel +# +# This file is subject to the terms and conditions of the GNU Lesser +# General Public License v2.1. See the file LICENSE in the top level +# directory for more details. + +import sys +from testrunner import run + + +def testfunc(child): + child.expect_exact("MEM MANAGE HANDLER\r\n") + + +if __name__ == "__main__": + sys.exit(run(testfunc, timeout=10))