-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
52: Compartment switch via memory stored data r=ltratt a=0152la Co-authored-by: Andrei Lascu <[email protected]>
- Loading branch information
Showing
3 changed files
with
284 additions
and
0 deletions.
There are no files selected for viewing
This file contains 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,136 @@ | ||
/* This example builds on previous ones, particularly improving the | ||
* `switch_compartment` (`shared.s:17`) function. This is a step towards | ||
* inter-compartment transition without the need of a more-privileged entity | ||
* controlling the switch (but needing to set everything up before control is | ||
* relinquished to a compartment). | ||
* | ||
* Some specific improvements over previous examples: | ||
* - the compartment switching function now reads information from memory | ||
* rather than from the stack. This allows compartments to call this function | ||
* from a constrained environment, without leaking capabilities beyond a few | ||
* prepared ones; | ||
* - we showcase the use of the compartment ID (`CID_EL0`) register to control | ||
* which compartment we are switching into. This is more of an example, as it | ||
* would entail compartments are aware of the ID of the callee. | ||
*/ | ||
|
||
#include "../../../include/common.h" | ||
#include "../../include/utils.h" | ||
|
||
#include <assert.h> | ||
#include <stddef.h> | ||
#include <stdint.h> | ||
#include <stdlib.h> | ||
#include <string.h> | ||
|
||
#if !defined(__CHERI_CAPABILITY_WIDTH__) || defined(__CHERI_PURE_CAPABILITY__) | ||
#error "This example only works on CHERI hybrid mode" | ||
#endif | ||
|
||
/******************************************************************************* | ||
* Assembly Functions & Consts | ||
******************************************************************************/ | ||
|
||
extern int switch_compartment(); | ||
extern void comp_f_fn(); | ||
|
||
/******************************************************************************* | ||
* Types & Consts | ||
******************************************************************************/ | ||
|
||
const size_t comp_stack_size = 2000; | ||
const size_t total_comp_size = 5000; | ||
size_t id = 0; | ||
|
||
/* Abstract representation of a compartment. Within 80 bytes we represent: | ||
* - an id (8B) | ||
* - address to the start of the compartment's stack (8B) | ||
* the size of the stack (8B) | ||
* - address to the start of the compartment's heap (8B) | ||
* the size of the heap (8B) | ||
* - alignment padding (8B) | ||
* - the ddc corresponding to the compartment (16B) | ||
* - the function within the compartment to be executed upon switching (16B) | ||
*/ | ||
struct comp | ||
{ | ||
size_t id; | ||
void *stack_addr; | ||
size_t stack_len; | ||
void *heap_addr; | ||
size_t heap_len; | ||
void *__capability ddc; | ||
void *__capability comp_fn; | ||
}; | ||
|
||
// ASM offsets, included here for validation | ||
#include "main.h" | ||
|
||
static_assert(COMP_SIZE == sizeof(struct comp), "Invalid `COMP_SIZE` provided"); | ||
static_assert(COMP_OFFSET_STK_ADDR == offsetof(struct comp, stack_addr), | ||
"Invalid `COMP_OFFSET_STK_ADDR` provided."); | ||
static_assert(COMP_OFFSET_STK_LEN == offsetof(struct comp, stack_len), | ||
"Invalid `COMP_OFFSET_STK_LEN` provided."); | ||
static_assert(COMP_OFFSET_DDC == offsetof(struct comp, ddc), "Invalid `COMP_OFFSET_DDC` provided."); | ||
static_assert(COMP_OFFSET_PCC == offsetof(struct comp, comp_fn), | ||
"Invalid `COMP_OFFSET_PCC` provided."); | ||
|
||
struct comp comps[COMP_COUNT]; | ||
|
||
/******************************************************************************* | ||
* Privileged Functions | ||
******************************************************************************/ | ||
|
||
/* This function stores the compartment data in memory, so that it can be | ||
* accessed by the compartments as needed. | ||
*/ | ||
void executive_switch(struct comp c) | ||
{ | ||
void *comps_addr = &comps; | ||
asm("mov x19, %0\n\t" | ||
"mov x0, #0\n\t" | ||
"msr CID_EL0, c0" | ||
: | ||
: "r"(comps_addr)); | ||
|
||
switch_compartment(); | ||
} | ||
|
||
void add_comp(uint8_t *_start_addr, void (*_comp_fn)()) | ||
{ | ||
assert(id < COMP_COUNT); | ||
|
||
struct comp new_comp; | ||
new_comp.id = id; | ||
|
||
new_comp.stack_addr = (void *) _start_addr; | ||
new_comp.stack_len = comp_stack_size; | ||
new_comp.heap_addr = (void *) (_start_addr + comp_stack_size); | ||
new_comp.heap_len = total_comp_size - comp_stack_size; | ||
|
||
void *__capability comp_ddc = (void *__capability) _start_addr; | ||
comp_ddc = cheri_bounds_set(comp_ddc, total_comp_size); | ||
new_comp.ddc = comp_ddc; | ||
|
||
void *__capability comp_fn = (void *__capability) _comp_fn; | ||
comp_fn = cheri_bounds_set(comp_fn, 40); | ||
new_comp.comp_fn = comp_fn; | ||
|
||
comps[id] = new_comp; | ||
++id; | ||
} | ||
|
||
/******************************************************************************* | ||
* Main | ||
******************************************************************************/ | ||
|
||
int main() | ||
{ | ||
uint8_t *comp_f = malloc(total_comp_size); | ||
add_comp(comp_f, comp_f_fn); | ||
|
||
executive_switch(comps[0]); | ||
|
||
// Check compartment did indeed execute | ||
assert(comp_f[4000] == 42); | ||
} |
This file contains 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,6 @@ | ||
#define COMP_COUNT 1 | ||
#define COMP_SIZE 80 | ||
#define COMP_OFFSET_STK_ADDR 8 | ||
#define COMP_OFFSET_STK_LEN 16 | ||
#define COMP_OFFSET_DDC 48 | ||
#define COMP_OFFSET_PCC 64 |
This file contains 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,142 @@ | ||
// Copyright (c) 2021 The CapableVMs "CHERI Examples" Contributors. | ||
// SPDX-License-Identifier: MIT OR Apache-2.0 | ||
|
||
#include "main.h" | ||
|
||
.global comp_f_fn | ||
|
||
.text | ||
.balign 4 | ||
|
||
/* This function has been updated with a few features: | ||
* - it identifies which compartment to switch to based on the value within the | ||
* `CID_EL0` register; | ||
* - it reads compartment data from memory, rather than it having to be passed | ||
* to it via registers - this includes DDC and PCC of compartment to switch to. | ||
*/ | ||
.global switch_compartment | ||
.type switch_compartment, "function" | ||
switch_compartment: | ||
|
||
// Get compartment to switch to data | ||
mrs c10, CID_EL0 | ||
mov x11, #COMP_SIZE | ||
mul x10, x10, x11 | ||
|
||
// Setup SP | ||
mov x12, sp | ||
add x11, x10, #COMP_OFFSET_STK_ADDR | ||
ldr x11, [x19, x11] | ||
add x13, x10, #COMP_OFFSET_STK_LEN | ||
ldr x13, [x19, x13] | ||
add sp, x11, x13 | ||
|
||
// Derive a new clr to restore PCC, and store it. | ||
cvtp c11, lr | ||
|
||
// Save old DDC, old SP, old LR on stack | ||
mrs c10, DDC | ||
stp c10, c11, [sp, #-48]! | ||
str x12, [sp, #32] | ||
|
||
// Stack layout at this point: | ||
// | ||
// `stack + size` -> ________________________ | ||
// sp + 40 -> [ <alignment pad> ] ^ | ||
// 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 | ||
|
||
// Load PCC, including function we are jumping to within compartment | ||
add x11, x10, #COMP_OFFSET_PCC | ||
ldr c0, [x19, x11] | ||
|
||
// Load DDC | ||
add x11, x10, #COMP_OFFSET_DDC | ||
ldr c11, [x19, x11] | ||
msr DDC, c11 | ||
|
||
bl clean+4 | ||
|
||
// Jump to the function within the compartment we are switching to | ||
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 | ||
|
||
/* The function in this compartment just writes to some memory within its | ||
* bounds, to ensure it is properly called. | ||
*/ | ||
.type comp_f_fn, "function" | ||
comp_f_fn: | ||
mrs c10, DDC | ||
mov x11, 42 | ||
str x11, [x10, #4000] | ||
|
||
ret clr | ||
|
||
|
||
// Inner helper for cleaning capabilities from registers, either side of an | ||
// AAPCS64 function call where some level of distrust exists between caller | ||
// and callee. | ||
// | ||
// Depending on the trust model, this might not be required, but the process | ||
// is included here for demonstration purposes. Note that if data needs to | ||
// be scrubbed as well as capabilities, then NEON registers also need to be | ||
// cleaned. | ||
// | ||
// Callers should enter at an appropriate offset so that live registers | ||
// holding arguments and return values (c0-c7) are preserved. | ||
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 | ||
|