From 9c4277c68012aeb328c80e1198064d0dc0240d49 Mon Sep 17 00:00:00 2001 From: Andrei Lascu Date: Thu, 27 Jan 2022 14:06:29 +0000 Subject: [PATCH 1/5] Add basic infrastructure, and initial work * usual `src`, `include`, `tests` setup * use in-built cmake option for testing * sketch out `init_compartments` and `add_compartment` functions --- .gitignore | 6 ++ CMakeLists.txt | 24 ++++++++ include/comps_offsets.h | 4 ++ src/CMakeLists.txt | 4 ++ src/manager.S | 117 +++++++++++++++++++++++++++++++++++ src/switcher.S | 131 ++++++++++++++++++++++++++++++++++++++++ tests/CMakeLists.txt | 10 +++ tests/run_test.sh | 17 ++++++ tests/simple_add.c | 66 ++++++++++++++++++++ tests/simple_init.c | 49 +++++++++++++++ 10 files changed, 428 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 include/comps_offsets.h create mode 100644 src/CMakeLists.txt create mode 100644 src/manager.S create mode 100644 src/switcher.S create mode 100644 tests/CMakeLists.txt create mode 100755 tests/run_test.sh create mode 100644 tests/simple_add.c create mode 100644 tests/simple_init.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8972cb6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +build/ + +**/*.swp + +build.sh +up.sh diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..d73a9a9 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,24 @@ +cmake_minimum_required(VERSION 3.16) +project(CheriMorelloCompartments LANGUAGES C ASM) + +set(INCLUDE_DIR ${CMAKE_SOURCE_DIR}/include) +set(SRC_DIR ${CMAKE_SOURCE_DIR}/src) +set(TEST_DIR ${CMAKE_SOURCE_DIR}/tests) + +# Setup +include_directories("${INCLUDE_DIR}") + +find_package(Clang REQUIRED) +find_package(LLVM REQUIRED CONFIG) + +# Building +add_subdirectory(${SRC_DIR}) + +# Testing +if (CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) + include(CTest) +endif() + +if (CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME AND BUILD_TESTING) + add_subdirectory(${TEST_DIR}) +endif() diff --git a/include/comps_offsets.h b/include/comps_offsets.h new file mode 100644 index 0000000..a8f4965 --- /dev/null +++ b/include/comps_offsets.h @@ -0,0 +1,4 @@ +#define COMP_SIZE 48 +#define COMP_OFFSET_PCC 0 +#define COMP_OFFSET_DDC 16 +#define COMP_OFFSET_STK_ADDR 32 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..f14286d --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,4 @@ +add_library(morello-compartments STATIC + ${SRC_DIR}/manager.S + ${SRC_DIR}/switcher.S + ) diff --git a/src/manager.S b/src/manager.S new file mode 100644 index 0000000..d7c35bd --- /dev/null +++ b/src/manager.S @@ -0,0 +1,117 @@ +.data + +comps_cnt: .dword 0 +comps_addr: .dword 0 +switcher_caps: .dword 0 + +.text +.balign 4 + +#include "comps_offsets.h" + +// Variables +.global comps_cnt +.global comps_addr +.global switcher_caps + +// Functions +.global asm_call_wrapper +.global init_compartments +.global add_compartment +.global del_compartment + +/** + * Wrapper to store/restore state when coming from C + * + * @param x0 ASM function to call + * @param x1-x6 parameters to pass to function in c0 + */ +.type asm_call_wrapper, "function" +asm_call_wrapper: + // Save `x0` so we can temporarily use it + cvtp c0, x0 + str c0, [sp] + + // Derive `clr` (in case asm function does something weird with `PCC`) + cvtp c0, lr + swp c0, c0, [sp] + sub sp, sp, #16 + + blr c0 + ldr clr, [sp, #16]! + ret clr + +/** + * Sets up memory for compartments + * + * @param c1 Start address of switcher memory region + * @param c2 Size of switcher memory region + * @param c3 Address of `switch_compartment` + */ +.type init_compartments, "function" +init_compartments: + + // Derive DDC + cvtp c1, x1 + scbnds c1, c1, x2 + + // Derive PCC + cvtp c2, x3 + mov x3, #320 // TODO dynamic value + scbndse c2, c2, x3 + + // Store (DDC, PCC) at `[switcher_caps]` + ldr x4, switcher_caps + stp c1, c2, [x4] + + ret + +/** + * Function to add information for a compartment + * + * @param c1 Start address + * @param c2 Size of memory region + * @param c3 Function address + */ +.type add_compartment, "function" +add_compartment: + + // Derive compartment PCC + cvtp c3, x3 + mov x4, #320 // TODO dynamic value + scbndse c3, c3, x4 + + // Derive compartment DDC + cvtp c1, x1 + scbnds c2, c1, x2 + + // Store new PCC and DDC + ldr x0, comps_addr + ldr x1, comps_cnt + mov x3, #COMP_SIZE + madd x0, x1, x2, x0 + stp c3, c2, [x0] + + // Increment counter + adr x3, comps_cnt + ldr x4, [x3] + add x4, x4, #1 + str x4, [x3] + + // Update switcher DDC + adr x2, switcher_caps + ldr c0, [x2] + gclen x1, c0 + add x1, x1, #32 + scbndse c0, c0, x1 + str c0, [x2] + + ret + +/** + * Function to delete an existing compartment data + * + * @param c0 ID of compartment to be deleted + */ +.type del_compartment, "function" +del_compartment: diff --git a/src/switcher.S b/src/switcher.S new file mode 100644 index 0000000..22cbdcd --- /dev/null +++ b/src/switcher.S @@ -0,0 +1,131 @@ +#include "comps_offsets.h" + +.text +.balign 4 + +.global switcher_entry +.global switch_compartment + +/** + * Entry point from user code to switcher function + * + * @param c0 DDC of switcher, containing compartment information + */ +.type switcher_entry, "function" +switcher_entry: + mov c29, c0 + mov x0, #0 + cvtp clr, lr + b switch_compartment + ret clr + +/** Code to perform actual switch + * + * @param x0 ID (in `comps` array) of compartment to switch to + */ +.type switch_compartment, "function" +switch_compartment: + // Store entering compartment's DDC, and move to memory containing + // compartment info + mrs c2, DDC + mov x10, x0 + + // Expect switcher DDC in c29 + msr DDC, c29 + + // Get compartment to switch to data + mov x11, #COMP_SIZE + mul x10, x10, x11 + + // Load PCC, including function we are jumping to within compartment + add x11, x10, #COMP_OFFSET_PCC + ldr c0, [x29, x11] + + // Load DDC + add x11, x10, #COMP_OFFSET_DDC + ldr c1, [x29, x11] + + // Setup SP + mov x12, sp + add x11, x10, #COMP_OFFSET_STK_ADDR + ldr x11, [x29, x11] + mov sp, x11 + + // Install compartment DDC + msr DDC, c1 + + // Save old DDC (c2), old SP (x12), old CLR (clr) on stack + stp c2, clr, [sp, #-48]! + str x12, [sp, #32] + + // Stack layout at this point: + // + // `stack + size` -> ________________________ + // sp + 40 -> [ ] ^ + // sp + 32 -> [ old SP ] | + // sp + 24 -> [ old CLR (hi64) ] | + // sp + 16 -> [ old CLR (lo64) ] | + // sp + 8 -> [ old DDC (high 64) ] | DDC bounds + // sp + 0 -> [ old DDC (low 64) ] | + // : : + // `stack` -> ________________________v + + // Clean all registers, except register used to call function within + // compartment we are transitioning to + bl clean+4 + + // Jump to the function within the compartment we are switching to (this + // also sets PCC) + blr c0 + + // Clean capabilities left in the return value. + mov w0, w0 + bl clean + + // Restore the caller's context and compartment. + ldp c10, clr, [sp] + ldr x12, [sp, #32] + msr DDC, c10 + mov x10, #0 + mov sp, x12 + + ret clr + +clean: + mov x0, #0 + mov x1, #0 + mov x2, #0 + mov x3, #0 + mov x4, #0 + mov x5, #0 + mov x6, #0 + mov x7, #0 + mov x8, #0 + mov x9, #0 + mov x10, #0 + mov x11, #0 + mov x12, #0 + mov x13, #0 + mov x14, #0 + mov x15, #0 + mov x16, #0 + mov x17, #0 + // x18 is the "platform register" (for some platforms). If so, it needs to + // be preserved, but here we assume that only the lower 64 bits are + // required. + mov x18, x18 + // x19-x29 are callee-saved, but only the lower 64 bits. + mov x19, x19 + mov x20, x20 + mov x21, x21 + mov x22, x22 + mov x23, x23 + mov x24, x24 + mov x25, x25 + mov x26, x26 + mov x27, x27 + mov x28, x28 + mov x29, x29 // FP + // We need LR (x30) to return. The call to this helper already cleaned it. + // Don't replace SP; this needs special handling by the caller anyway. + ret diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..17bc8e6 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,10 @@ +function(new_proj_test test_name) + add_executable(${test_name} + ${test_name}.c) + target_link_libraries(${test_name} morello-compartments) + add_test(NAME ${test_name} + COMMAND ${CMAKE_SOURCE_DIR}/tests/run_test.sh $) +endfunction() + +new_proj_test(simple_init) +new_proj_test(simple_add) diff --git a/tests/run_test.sh b/tests/run_test.sh new file mode 100755 index 0000000..1a774df --- /dev/null +++ b/tests/run_test.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +set -x +set -e + +if [ $# -ne 1 ] +then + echo "Expected one parameter: path to executable." + exit 1 +fi + +CHERIBSD_PORT=10086 +CHERIBSD_USER=root +CHERIBSD_HOST=localhost + +scp -P $CHERIBSD_PORT $1 $CHERIBSD_USER@$CHERIBSD_HOST: +ssh -p $CHERIBSD_PORT $CHERIBSD_USER@$CHERIBSD_HOST -t ./$(basename $1) diff --git a/tests/simple_add.c b/tests/simple_add.c new file mode 100644 index 0000000..83118b6 --- /dev/null +++ b/tests/simple_add.c @@ -0,0 +1,66 @@ +#include "assert.h" +#include "stdint.h" +#include "stdlib.h" + +#include "cheriintrin.h" + +#include "comps_offsets.h" + +static_assert(COMP_SIZE == sizeof(void* __capability) * 3, "Invalid `COMP_SIZE` provided"); +static_assert(COMP_OFFSET_DDC == sizeof(void* __capability) * 1, "Invalid `COMP_OFFSET_DDC` provided."); +static_assert(COMP_OFFSET_STK_ADDR == sizeof(void* __capability) * 2, "Invalid `COMP_OFFSET_STK_LEN` provided."); + +/******************************************************************************* + * Globals and constants + ******************************************************************************/ + +const size_t max_comp_cnt = 2; +const size_t switcher_mem_max_size = max_comp_cnt * COMP_SIZE; + +/******************************************************************************* + * Extern functions + ******************************************************************************/ + +extern void asm_call_wrapper(void*, ...); +extern void init_compartments(void*, size_t, void*); +extern void add_compartment(void*, size_t, void*); +extern int switch_compartment(); + +extern void* comps_addr; +extern void* switcher_caps; + +/******************************************************************************* + * Main + ******************************************************************************/ + +int comp_f_fn(); + +int +main() +{ + switcher_caps = malloc(sizeof(void* __capability) * 2); + comps_addr = malloc(COMP_SIZE * max_comp_cnt); + + void* switcher_start = malloc(switcher_mem_max_size); + void* switch_comp_addr = switch_compartment; + + asm_call_wrapper(init_compartments, + switcher_start, switcher_mem_max_size, switch_comp_addr); + + const size_t comp_f_size = 1000; + uintptr_t comp_f_start = (uintptr_t) malloc(comp_f_size); + asm_call_wrapper(add_compartment, + comp_f_start, comp_f_size, comp_f_fn); + + return 0; +} + +/******************************************************************************* + * Compartments + ******************************************************************************/ + +int +comp_f_fn() +{ + return 42; +} diff --git a/tests/simple_init.c b/tests/simple_init.c new file mode 100644 index 0000000..f9a00d3 --- /dev/null +++ b/tests/simple_init.c @@ -0,0 +1,49 @@ +#include "assert.h" +#include "stdint.h" +#include "stdlib.h" + +#include "cheriintrin.h" + +#include "comps_offsets.h" + +static_assert(COMP_SIZE == sizeof(void* __capability) * 3, "Invalid `COMP_SIZE` provided"); +static_assert(COMP_OFFSET_DDC == sizeof(void* __capability) * 1, "Invalid `COMP_OFFSET_DDC` provided."); +static_assert(COMP_OFFSET_STK_ADDR == sizeof(void* __capability) * 2, "Invalid `COMP_OFFSET_STK_LEN` provided."); + +/******************************************************************************* + * Globals and constants + ******************************************************************************/ + +const size_t max_comp_cnt = 2; +const size_t switcher_mem_max_size = max_comp_cnt * COMP_SIZE; + +/******************************************************************************* + * Extern functions + ******************************************************************************/ + +extern void asm_call_wrapper(void*, ...); +extern void init_compartments(uint8_t*, size_t, uintptr_t); +extern int switch_compartment(); + +extern void* comps_addr; +extern void* switcher_caps; + +/******************************************************************************* + * Main + ******************************************************************************/ + +int +main() +{ + switcher_caps = malloc(sizeof(void* __capability) * 2); + comps_addr = malloc(COMP_SIZE * max_comp_cnt); + + uint8_t* switcher_start = malloc(switcher_mem_max_size); + uintptr_t switch_comp_addr = (uintptr_t) switch_compartment; + + // init_compartments(); + + asm_call_wrapper(init_compartments, + switcher_start, switcher_mem_max_size, switch_comp_addr); + return 0; +} From 4c26fa27a5f3b4f0ef743a04294a5f53aede26c4 Mon Sep 17 00:00:00 2001 From: Andrei Lascu Date: Tue, 8 Feb 2022 14:43:32 +0000 Subject: [PATCH 2/5] Update `init_compartments` and `simple_init` As discussed, we move towards a more abstract model, including memory management in the assembly layer. This updates the initialiation function to not require any user input, and setup memory appropriately, for future use. Add checks to `simple_init` to sanity check functionality. --- include/comps_offsets.h | 2 ++ src/manager.S | 66 +++++++++++++++++++++++++++++++---------- src/switcher.S | 3 ++ tests/simple_init.c | 43 +++++++++++++-------------- 4 files changed, 76 insertions(+), 38 deletions(-) diff --git a/include/comps_offsets.h b/include/comps_offsets.h index a8f4965..16c1ee1 100644 --- a/include/comps_offsets.h +++ b/include/comps_offsets.h @@ -2,3 +2,5 @@ #define COMP_OFFSET_PCC 0 #define COMP_OFFSET_DDC 16 #define COMP_OFFSET_STK_ADDR 32 + +#define MAX_COMP_COUNT 2 diff --git a/src/manager.S b/src/manager.S index d7c35bd..5eb6b19 100644 --- a/src/manager.S +++ b/src/manager.S @@ -44,25 +44,59 @@ asm_call_wrapper: /** * Sets up memory for compartments * - * @param c1 Start address of switcher memory region - * @param c2 Size of switcher memory region - * @param c3 Address of `switch_compartment` + * Initializes required memory for compartments. This involves allocating a + * memory region for use by switcher code, to contain required capabilities, + * and deriving appropriate PCC/DDC values containing the executable switcher + * code, and the aforementioned memory region, respectively. + * + * @return Pointer to newly allocated memory region */ .type init_compartments, "function" init_compartments: - - // Derive DDC - cvtp c1, x1 - scbnds c1, c1, x2 - - // Derive PCC - cvtp c2, x3 - mov x3, #320 // TODO dynamic value - scbndse c2, c2, x3 - - // Store (DDC, PCC) at `[switcher_caps]` - ldr x4, switcher_caps - stp c1, c2, [x4] + // Compute size of required memory, equivalent to `length` parameter of + // `mmap` + mov x0, #COMP_SIZE + mov x1, #MAX_COMP_COUNT + mov x2, #32 // size of the 2 switcher capabilities + madd x1, x0, x1, x2 + + // Store length and `lr` on stack, as we'll need them later + stp x1, lr, [sp, #-16]! + + // Allocate memory for switcher + mov x0, xzr // address + // length - already stored in `x1` + mov w2, #3 // prot == PROT_READ | PROT_WRITE + mov w3, #4098 // flags == MAP_PRIVATE | MAP_ANONYMOUS + mov w4, #-1 // fd + mov w5, wzr // offset + bl mmap + + // Restore length and `lr` + ldp x1, lr, [sp], #16 + + // Save pointer to new allocated memory in `switcher_caps` + adr x2, switcher_caps + str x0, [x2] + + // Derive DDC for switcher + cvtp c2, x0 + scbnds c2, c2, x1 + + // Derive PCC for `switch_compartments` and friends + adr x3, switcher_entry + adr x4, switch_compartment_end + sub x4, x4, x3 + cvtp c3, x3 + scbndse c3, c3, x4 + + // Store (DDC, PCC) at `switcher_caps` + ldr x1, switcher_caps + stp c2, c3, [x1], #32 + + // Save start address for compartment capabilities in `comps_addr` + adr x2, comps_addr + str x1, [x2] ret diff --git a/src/switcher.S b/src/switcher.S index 22cbdcd..7a83c6f 100644 --- a/src/switcher.S +++ b/src/switcher.S @@ -5,6 +5,7 @@ .global switcher_entry .global switch_compartment +.global switch_compartment_end /** * Entry point from user code to switcher function @@ -129,3 +130,5 @@ clean: // We need LR (x30) to return. The call to this helper already cleaned it. // Don't replace SP; this needs special handling by the caller anyway. ret + +switch_compartment_end: diff --git a/tests/simple_init.c b/tests/simple_init.c index f9a00d3..a07f5f8 100644 --- a/tests/simple_init.c +++ b/tests/simple_init.c @@ -1,6 +1,7 @@ -#include "assert.h" -#include "stdint.h" -#include "stdlib.h" +#include +#include +#include +#include #include "cheriintrin.h" @@ -10,23 +11,15 @@ static_assert(COMP_SIZE == sizeof(void* __capability) * 3, "Invalid `COMP_SIZE` static_assert(COMP_OFFSET_DDC == sizeof(void* __capability) * 1, "Invalid `COMP_OFFSET_DDC` provided."); static_assert(COMP_OFFSET_STK_ADDR == sizeof(void* __capability) * 2, "Invalid `COMP_OFFSET_STK_LEN` provided."); -/******************************************************************************* - * Globals and constants - ******************************************************************************/ - -const size_t max_comp_cnt = 2; -const size_t switcher_mem_max_size = max_comp_cnt * COMP_SIZE; - /******************************************************************************* * Extern functions ******************************************************************************/ -extern void asm_call_wrapper(void*, ...); -extern void init_compartments(uint8_t*, size_t, uintptr_t); -extern int switch_compartment(); +extern void* init_compartments(); +extern void* __capability * switcher_caps; -extern void* comps_addr; -extern void* switcher_caps; +extern void switcher_entry(); +extern void switch_compartment_end(); /******************************************************************************* * Main @@ -35,15 +28,21 @@ extern void* switcher_caps; int main() { - switcher_caps = malloc(sizeof(void* __capability) * 2); - comps_addr = malloc(COMP_SIZE * max_comp_cnt); + void* inner_addr = init_compartments(); + + assert(inner_addr != MAP_FAILED); + assert(switcher_caps == inner_addr); - uint8_t* switcher_start = malloc(switcher_mem_max_size); - uintptr_t switch_comp_addr = (uintptr_t) switch_compartment; + void* __capability switcher_ddc = switcher_caps[0]; + assert(cheri_is_valid(switcher_ddc)); + assert(cheri_length_get(switcher_ddc) == + COMP_SIZE * MAX_COMP_COUNT + 2 * sizeof(void* __capability)); - // init_compartments(); + void* __capability switcher_pcc = switcher_caps[1]; + assert(cheri_is_valid(switcher_pcc)); + assert(cheri_address_get(switcher_pcc) == (unsigned long) switcher_entry); + assert(cheri_address_get(switcher_pcc) + cheri_length_get(switcher_pcc) == + (unsigned long) switch_compartment_end); - asm_call_wrapper(init_compartments, - switcher_start, switcher_mem_max_size, switch_comp_addr); return 0; } From 9f4758ce96a5dd2858263bc93675bcc825b58857 Mon Sep 17 00:00:00 2001 From: Andrei Lascu Date: Wed, 9 Feb 2022 13:59:22 +0000 Subject: [PATCH 3/5] Finalize `simple_add` test --- src/manager.S | 86 ++++++++++++++++++++++++++++------------------ tests/simple_add.c | 45 ++++++++++-------------- 2 files changed, 71 insertions(+), 60 deletions(-) diff --git a/src/manager.S b/src/manager.S index 5eb6b19..f44baa0 100644 --- a/src/manager.S +++ b/src/manager.S @@ -103,44 +103,62 @@ init_compartments: /** * Function to add information for a compartment * - * @param c1 Start address - * @param c2 Size of memory region - * @param c3 Function address + * @param x0 Compartment memory size + * @param x1 Compartment executable function + * + * @return Pointer to newly allocated memory region */ .type add_compartment, "function" add_compartment: + // Store inputs and `lr` so we can call `mmap` + stp x0, x1, [sp, #-32]! + str lr, [sp, #16] + mov x1, x0 + + // Allocate memory for new compartment + mov x0, xzr // address + // length - already stored in `x1` + mov w2, #3 // prot == PROT_READ | PROT_WRITE + mov w3, #4098 // flags == MAP_PRIVATE | MAP_ANONYMOUS + mov w4, #-1 // fd + mov w5, wzr // offset + bl mmap + + // Restore memory size and function address + ldp x1, x2, [sp], #32 + ldr lr, [sp, #-16] - // Derive compartment PCC - cvtp c3, x3 - mov x4, #320 // TODO dynamic value - scbndse c3, c3, x4 - - // Derive compartment DDC - cvtp c1, x1 - scbnds c2, c1, x2 - - // Store new PCC and DDC - ldr x0, comps_addr - ldr x1, comps_cnt - mov x3, #COMP_SIZE - madd x0, x1, x2, x0 - stp c3, c2, [x0] - - // Increment counter - adr x3, comps_cnt - ldr x4, [x3] - add x4, x4, #1 - str x4, [x3] - - // Update switcher DDC - adr x2, switcher_caps - ldr c0, [x2] - gclen x1, c0 - add x1, x1, #32 - scbndse c0, c0, x1 - str c0, [x2] - - ret + // Derive compartment DDC + cvtp c0, x0 + scbnds c0, c0, x1 + + // Derive compartment PCC + cvtp c1, x2 + mov x2, #320 // TODO dynamic value + scbndse c1, c1, x2 + + // Store new PCC and DDC + ldr x2, comps_addr + ldr x3, comps_cnt + mov x4, #COMP_SIZE + madd x2, x3, x4, x2 + stp c0, c1, [x2] + + // Increment counter + adr x3, comps_cnt + ldr x4, [x3] + add x4, x4, #1 + str x4, [x3] + + // Update switcher DDC + //adr x2, switcher_caps + //ldr c0, [x2] + //gclen x1, c0 + //add x1, x1, #32 + //scbndse c0, c0, x1 + //str c0, [x2] + + ret /** * Function to delete an existing compartment data diff --git a/tests/simple_add.c b/tests/simple_add.c index 83118b6..82d0044 100644 --- a/tests/simple_add.c +++ b/tests/simple_add.c @@ -1,6 +1,7 @@ -#include "assert.h" -#include "stdint.h" -#include "stdlib.h" +#include +#include +#include +#include #include "cheriintrin.h" @@ -10,24 +11,15 @@ static_assert(COMP_SIZE == sizeof(void* __capability) * 3, "Invalid `COMP_SIZE` static_assert(COMP_OFFSET_DDC == sizeof(void* __capability) * 1, "Invalid `COMP_OFFSET_DDC` provided."); static_assert(COMP_OFFSET_STK_ADDR == sizeof(void* __capability) * 2, "Invalid `COMP_OFFSET_STK_LEN` provided."); -/******************************************************************************* - * Globals and constants - ******************************************************************************/ - -const size_t max_comp_cnt = 2; -const size_t switcher_mem_max_size = max_comp_cnt * COMP_SIZE; - /******************************************************************************* * Extern functions ******************************************************************************/ -extern void asm_call_wrapper(void*, ...); -extern void init_compartments(void*, size_t, void*); -extern void add_compartment(void*, size_t, void*); -extern int switch_compartment(); +extern void* __capability * comps_addr; +extern size_t comps_cnt; -extern void* comps_addr; -extern void* switcher_caps; +extern void* init_compartments(); +extern void* add_compartment(size_t, void*); /******************************************************************************* * Main @@ -38,19 +30,20 @@ int comp_f_fn(); int main() { - switcher_caps = malloc(sizeof(void* __capability) * 2); - comps_addr = malloc(COMP_SIZE * max_comp_cnt); + init_compartments(); + + size_t comp_size = 2000; + assert(add_compartment(comp_size, comp_f_fn) != MAP_FAILED); - void* switcher_start = malloc(switcher_mem_max_size); - void* switch_comp_addr = switch_compartment; + assert(comps_cnt == 1); - asm_call_wrapper(init_compartments, - switcher_start, switcher_mem_max_size, switch_comp_addr); + void* __capability comp_ddc = comps_addr[0]; + assert(cheri_is_valid(comp_ddc)); + assert(cheri_length_get(comp_ddc) == comp_size); - const size_t comp_f_size = 1000; - uintptr_t comp_f_start = (uintptr_t) malloc(comp_f_size); - asm_call_wrapper(add_compartment, - comp_f_start, comp_f_size, comp_f_fn); + void* __capability comp_pcc = comps_addr[1]; + assert(cheri_is_valid(comp_pcc)); + assert(cheri_address_get(comp_pcc) == (unsigned long) comp_f_fn); return 0; } From 6a2c5436a62665c78af2d496473819920089c2ff Mon Sep 17 00:00:00 2001 From: Andrei Lascu Date: Wed, 9 Feb 2022 15:58:49 +0000 Subject: [PATCH 4/5] Add bors + buildbot support, and legal stuff --- .buildbot.sh | 40 +++++++++++++++++++++++++++++++++++++ .run_cheri_qemu_and_test.py | 34 +++++++++++++++++++++++++++++++ COPYRIGHT | 17 ++++++++++++++++ LICENSE-APACHE | 10 ++++++++++ LICENSE-MIT | 17 ++++++++++++++++ bors.toml | 10 ++++++++++ tests/run_test.sh | 4 ++-- 7 files changed, 130 insertions(+), 2 deletions(-) create mode 100644 .buildbot.sh create mode 100644 .run_cheri_qemu_and_test.py create mode 100644 COPYRIGHT create mode 100644 LICENSE-APACHE create mode 100644 LICENSE-MIT create mode 100644 bors.toml diff --git a/.buildbot.sh b/.buildbot.sh new file mode 100644 index 0000000..33a79dd --- /dev/null +++ b/.buildbot.sh @@ -0,0 +1,40 @@ +#!/bin/bash + +set -e + +build_dir="$(pwd)/build" +src_dir="$(pwd)" +cheri_dir="/home/buildbot/cheri/output" + +# Build project locally +export CC=$cheri_dir/morello-sdk/bin/clang +export CFLAGS="--config cheribsd-morello-hybrid.cfg" +export ASMFLAGS="--config cheribsd-morello-hybrid.cfg" + +cmake \ + -G Ninja \ + -DCMAKE_BUILD_TYPE=DEBUG \ + -DLLVM_DIR=$cheri_dir/morello-sdk/lib/cmake/llvm \ + -DClang_DIR=$cheri_dir/morello-sdk/lib/cmake/clang \ + -B $build_dir \ + -S $src_dir +cmake --build $build_dir + +# Set arguments for Morello hybrid instance +export SSHPORT=10086 +export PYTHONPATH="/home/buildbot/build/test-scripts" + +args=( + --architecture morello-hybrid + # Qemu System to use + --qemu-cmd $cheri_dir/morello-sdk/bin/qemu-system-morello + --bios edk2-aarch64-code.fd + --disk-image $cheri_dir/cheribsd-morello-hybrid.img + # Required build-dir in CheriBSD + --build-dir . + --ssh-port $SSHPORT + --ssh-key $HOME/.ssh/id_ed25519.pub + ) + +# Spawn CHERI QEMU instance, then execute `ctest` +python3 $src_dir/.run_cheri_qemu_and_test.py "${args[@]}" diff --git a/.run_cheri_qemu_and_test.py b/.run_cheri_qemu_and_test.py new file mode 100644 index 0000000..e56a2bc --- /dev/null +++ b/.run_cheri_qemu_and_test.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 + +# Adapted from +# https://github.com/capablevms/cheri-examples/blob/master/tests/run_cheri_examples.py + +import argparse +import importlib.util +import os +import subprocess +import sys + +from pathlib import Path + +# Emulate `sys.path` from path of module `run_tests_common` (found via +# environment variable `PYTHONPATH`), as required by the CHERI testing +# infrastructure which we are using to simplify booting a QEMU instance +test_scripts_dir = str(Path(importlib.util.find_spec("run_tests_common").origin).parent.absolute()) +sys.path = sys.path[sys.path.index(test_scripts_dir):] + +from run_tests_common import boot_cheribsd, run_tests_main + +def run_tests(qemu: boot_cheribsd.QemuCheriBSDInstance, args: argparse.Namespace) -> bool: + if args.sysroot_dir is not None: + boot_cheribsd.set_ld_library_path_with_sysroot(qemu) + boot_cheribsd.info("Running tests for cheri-morello-compartmentalisation") + + # Run command on host to test the executed client + os.chdir(f"{args.build_dir}/build") + subprocess.run(["/usr/bin/ctest", "-V"], check=True) + return True + +if __name__ == '__main__': + # This call has the side-effect of booting a QEMU instance + run_tests_main(test_function=run_tests, need_ssh=True, should_mount_builddir=False) diff --git a/COPYRIGHT b/COPYRIGHT new file mode 100644 index 0000000..8b2ad70 --- /dev/null +++ b/COPYRIGHT @@ -0,0 +1,17 @@ +Except as otherwise noted (below and/or in individual files), this project is +licensed under the Apache License, Version 2.0 + or the MIT license +, at your option. + +Copyright is retained by contributors and/or the organisations they +represent(ed) -- this project does not require copyright assignment. Please see +version control history for a full list of contributors. Note that some files +may include explicit copyright and/or licensing notices. + +The following contributors wish to explicitly make it known that the copyright +of their contributions is retained by an organisation: + + Jacob Bramley : + copyright retained by Arm Limited + Laurence Tratt : + copyright retained by King's College London diff --git a/LICENSE-APACHE b/LICENSE-APACHE new file mode 100644 index 0000000..3d127b7 --- /dev/null +++ b/LICENSE-APACHE @@ -0,0 +1,10 @@ +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the +License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. diff --git a/LICENSE-MIT b/LICENSE-MIT new file mode 100644 index 0000000..410e04e --- /dev/null +++ b/LICENSE-MIT @@ -0,0 +1,17 @@ +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/bors.toml b/bors.toml new file mode 100644 index 0000000..db1bdaa --- /dev/null +++ b/bors.toml @@ -0,0 +1,10 @@ +# Sourced from +# https://github.com/capablevms/cheri-examples/blob/master/bors.toml +status = ["buildbot/capablevms-test-script"] + +timeout_sec = 600 # 10 minutes + +# Have bors delete auto-merged branches +delete_merged_branches = true + +cut_body_after = "" diff --git a/tests/run_test.sh b/tests/run_test.sh index 1a774df..06a8039 100755 --- a/tests/run_test.sh +++ b/tests/run_test.sh @@ -13,5 +13,5 @@ CHERIBSD_PORT=10086 CHERIBSD_USER=root CHERIBSD_HOST=localhost -scp -P $CHERIBSD_PORT $1 $CHERIBSD_USER@$CHERIBSD_HOST: -ssh -p $CHERIBSD_PORT $CHERIBSD_USER@$CHERIBSD_HOST -t ./$(basename $1) +scp -o "StrictHostKeyChecking no" -P $CHERIBSD_PORT $1 $CHERIBSD_USER@$CHERIBSD_HOST: +ssh -o "StrictHostKeyChecking no" -p $CHERIBSD_PORT $CHERIBSD_USER@$CHERIBSD_HOST -t ./$(basename $1) From c804e0963660c996f7d85a4c5cb2eb449196f585 Mon Sep 17 00:00:00 2001 From: Andrei Lascu Date: Fri, 11 Feb 2022 11:30:13 +0000 Subject: [PATCH 5/5] Polish pass over `init` and `add` implementations * Add checks to ensure we don't add more compartments than we allocated space for * Small optimisations and unneeded code removal Remove comment Simplify Replace multiple instruction with a single one Remove unimplemented stub Optimization Better register usage Fix Fix Forgot to update checked register Removed unused function Remove an unneeded call Remove unneeded instruction --- include/comps_offsets.h | 4 +-- src/manager.S | 58 +++++++---------------------------------- src/switcher.S | 4 +-- tests/simple_add.c | 3 ++- tests/simple_init.c | 3 ++- 5 files changed, 17 insertions(+), 55 deletions(-) diff --git a/include/comps_offsets.h b/include/comps_offsets.h index 16c1ee1..f1edbfe 100644 --- a/include/comps_offsets.h +++ b/include/comps_offsets.h @@ -1,6 +1,6 @@ #define COMP_SIZE 48 -#define COMP_OFFSET_PCC 0 -#define COMP_OFFSET_DDC 16 +#define COMP_OFFSET_DDC 0 +#define COMP_OFFSET_PCC 16 #define COMP_OFFSET_STK_ADDR 32 #define MAX_COMP_COUNT 2 diff --git a/src/manager.S b/src/manager.S index f44baa0..f6915fd 100644 --- a/src/manager.S +++ b/src/manager.S @@ -15,32 +15,10 @@ switcher_caps: .dword 0 .global switcher_caps // Functions -.global asm_call_wrapper .global init_compartments .global add_compartment .global del_compartment -/** - * Wrapper to store/restore state when coming from C - * - * @param x0 ASM function to call - * @param x1-x6 parameters to pass to function in c0 - */ -.type asm_call_wrapper, "function" -asm_call_wrapper: - // Save `x0` so we can temporarily use it - cvtp c0, x0 - str c0, [sp] - - // Derive `clr` (in case asm function does something weird with `PCC`) - cvtp c0, lr - swp c0, c0, [sp] - sub sp, sp, #16 - - blr c0 - ldr clr, [sp, #16]! - ret clr - /** * Sets up memory for compartments * @@ -55,10 +33,7 @@ asm_call_wrapper: init_compartments: // Compute size of required memory, equivalent to `length` parameter of // `mmap` - mov x0, #COMP_SIZE - mov x1, #MAX_COMP_COUNT - mov x2, #32 // size of the 2 switcher capabilities - madd x1, x0, x1, x2 + mov x1, #(32 + (COMP_SIZE * MAX_COMP_COUNT)) // Store length and `lr` on stack, as we'll need them later stp x1, lr, [sp, #-16]! @@ -90,8 +65,8 @@ init_compartments: cvtp c3, x3 scbndse c3, c3, x4 - // Store (DDC, PCC) at `switcher_caps` - ldr x1, switcher_caps + // Store (DDC, PCC) at `switcher_caps` (saved in `x0`) + mov x1, x0 stp c2, c3, [x1], #32 // Save start address for compartment capabilities in `comps_addr` @@ -145,25 +120,12 @@ add_compartment: stp c0, c1, [x2] // Increment counter - adr x3, comps_cnt - ldr x4, [x3] - add x4, x4, #1 - str x4, [x3] - - // Update switcher DDC - //adr x2, switcher_caps - //ldr c0, [x2] - //gclen x1, c0 - //add x1, x1, #32 - //scbndse c0, c0, x1 - //str c0, [x2] + adr x4, comps_cnt + add x3, x3, #1 + str x3, [x4] - ret + // Check we have not exceeded max number of compartments + cmp x3, #MAX_COMP_COUNT + b.gt abort -/** - * Function to delete an existing compartment data - * - * @param c0 ID of compartment to be deleted - */ -.type del_compartment, "function" -del_compartment: + ret diff --git a/src/switcher.S b/src/switcher.S index 7a83c6f..4058b05 100644 --- a/src/switcher.S +++ b/src/switcher.S @@ -17,8 +17,7 @@ switcher_entry: mov c29, c0 mov x0, #0 cvtp clr, lr - b switch_compartment - ret clr + // fall through into switch_compartment /** Code to perform actual switch * @@ -80,7 +79,6 @@ switch_compartment: blr c0 // Clean capabilities left in the return value. - mov w0, w0 bl clean // Restore the caller's context and compartment. diff --git a/tests/simple_add.c b/tests/simple_add.c index 82d0044..af334a7 100644 --- a/tests/simple_add.c +++ b/tests/simple_add.c @@ -8,7 +8,8 @@ #include "comps_offsets.h" static_assert(COMP_SIZE == sizeof(void* __capability) * 3, "Invalid `COMP_SIZE` provided"); -static_assert(COMP_OFFSET_DDC == sizeof(void* __capability) * 1, "Invalid `COMP_OFFSET_DDC` provided."); +static_assert(COMP_OFFSET_DDC == 0, "Invalid `COMP_OFFSET_DDC` provided."); +static_assert(COMP_OFFSET_PCC == sizeof(void* __capability) * 1, "Invalid `COMP_OFFSET_PCC` provided."); static_assert(COMP_OFFSET_STK_ADDR == sizeof(void* __capability) * 2, "Invalid `COMP_OFFSET_STK_LEN` provided."); /******************************************************************************* diff --git a/tests/simple_init.c b/tests/simple_init.c index a07f5f8..d897c68 100644 --- a/tests/simple_init.c +++ b/tests/simple_init.c @@ -8,7 +8,8 @@ #include "comps_offsets.h" static_assert(COMP_SIZE == sizeof(void* __capability) * 3, "Invalid `COMP_SIZE` provided"); -static_assert(COMP_OFFSET_DDC == sizeof(void* __capability) * 1, "Invalid `COMP_OFFSET_DDC` provided."); +static_assert(COMP_OFFSET_DDC == 0, "Invalid `COMP_OFFSET_DDC` provided."); +static_assert(COMP_OFFSET_PCC == sizeof(void* __capability) * 1, "Invalid `COMP_OFFSET_PCC` provided."); static_assert(COMP_OFFSET_STK_ADDR == sizeof(void* __capability) * 2, "Invalid `COMP_OFFSET_STK_LEN` provided."); /*******************************************************************************