Skip to content

Commit

Permalink
Merge #52
Browse files Browse the repository at this point in the history
52: Compartment switch via memory stored data r=ltratt a=0152la



Co-authored-by: Andrei Lascu <[email protected]>
  • Loading branch information
bors[bot] and 0152la authored Dec 2, 2021
2 parents e326b7d + fcd4f7a commit 9f7c157
Show file tree
Hide file tree
Showing 3 changed files with 284 additions and 0 deletions.
136 changes: 136 additions & 0 deletions hybrid/compartment_examples/comp_setup/main.c
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);
}
6 changes: 6 additions & 0 deletions hybrid/compartment_examples/comp_setup/main.h
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
142 changes: 142 additions & 0 deletions hybrid/compartment_examples/comp_setup/shared.S
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

0 comments on commit 9f7c157

Please sign in to comment.