Add a stack canary, setup on boot

This commit is contained in:
Toby Jaffey 2025-12-10 01:00:36 +00:00
parent 9596838a4b
commit 9baedb42e8
21 changed files with 61 additions and 16 deletions

View file

@ -1,19 +1,24 @@
#include "uvm32_sys.h"
.equ uvm32_syscall_halt, 0x1000000
.equ uvm32_syscall_yield, 0x1000001
.equ uvm32_syscall_stackprotect, 0x1000002
.section .initial_jump , "ax", %progbits
.global _start
.align 4
_start:
la a0, _estack
li a7, uvm32_syscall_stackprotect
ecall
# sp is already setup by vm
sw ra,12(sp)
jal ra, main
#if 1
// rust will interpret the "#if 1" and "#include" as comments and ignore
// C, asm, zig will include the file below, which references a constant from uvm32_sys.h
#include "non-rust-crt0-hack.S"
#else
// only rust will see this
li a7, {UVM32_SYSCALL_HALT}
#endif
li a7, uvm32_syscall_halt
ecall
.section .data

View file

@ -78,8 +78,9 @@ SECTIONS
_sstack = .;
}
*/
/* _sstack = .;*/
.stack : ALIGN( 16 ) {
_estack = .;
}
}

View file

@ -78,6 +78,7 @@ void print_maze() {
}
}
void main(void) {
init_maze();
carve(1, 1);

View file

@ -1 +0,0 @@
li a7,UVM32_SYSCALL_HALT

View file

@ -9,7 +9,7 @@ use core::panic::PanicInfo;
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
// startup code
global_asm!(include_str!("../../crt0.S"), UVM32_SYSCALL_HALT = const UVM32_SYSCALL_HALT);
global_asm!(include_str!("../../crt0.S"));
fn syscall(id: u32, param1: u32, param2: u32) -> u32 {
let mut value;

View file

@ -1,5 +1,6 @@
// System provided UVM32_SYSCALLs, start at 0x10000000
#define UVM32_SYSCALL_HALT 0x1000000
#define UVM32_SYSCALL_YIELD 0x1000001
#define UVM32_SYSCALL_HALT 0x1000000
#define UVM32_SYSCALL_YIELD 0x1000001
#define UVM32_SYSCALL_STACKPROTECT 0x1000002
#include "uvm32_common_custom.h"

View file

@ -59,5 +59,11 @@ static uint32_t syscall(uint32_t id, uint32_t param1, uint32_t param2) {
#define yield() syscall_cast(UVM32_SYSCALL_YIELD, 0, 0)
#define printbuf(x, y) syscall_cast(UVM32_SYSCALL_PRINTBUF, x, y)
extern char _estack;
static void stackprotect(void) {
syscall_cast(UVM32_SYSCALL_STACKPROTECT, &_estack, 0);
}
#include "uvm32_common_custom.h"

View file

@ -1,5 +1,5 @@
all:
gcc -Wall -pedantic -std=c99 -O2 -DUVM32_MEMORY_SIZE=65535 -I../uvm32 -I../common -o host ../uvm32/uvm32.c host.c
gcc -Wall -Werror -pedantic -std=c99 -O2 -DUVM32_MEMORY_SIZE=65535 -I../uvm32 -I../common -o host ../uvm32/uvm32.c host.c
clean:
rm -f host

View file

@ -202,7 +202,7 @@ int main(int argc, char *argv[]) {
printf("%c", uvm32_getval(&vmst, &evt, ARG0));
break;
case UVM32_SYSCALL_PRINTHEX:
printf("%d", uvm32_getval(&vmst, &evt, ARG0));
printf("%08x", uvm32_getval(&vmst, &evt, ARG0));
break;
case UVM32_SYSCALL_MILLIS: {
clock_t now = clock() / (CLOCKS_PER_SEC / 1000);

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -10,6 +10,9 @@
// On an invalid operation, an error is set in uvm32_state_t, but a valid pointer still needs to be temporarily used
static uint32_t garbage;
// magic value for stack canary
#define STACK_CANARY_VALUE 0x42
#ifndef UVM32_MEMCPY
#define UVM32_MEMCPY uvm32_memcpy
void uvm32_memcpy(void *dst, const void *src, int len) {
@ -84,6 +87,8 @@ bool uvm32_load(uvm32_state_t *vmst, const uint8_t *rom, int len) {
}
UVM32_MEMCPY(vmst->memory, rom, len);
vmst->stack_canary = (uint8_t *)UVM32_NULL;
return true;
}
@ -135,6 +140,12 @@ void uvm32_clearError(uvm32_state_t *vmst) {
uint32_t uvm32_run(uvm32_state_t *vmst, uvm32_evt_t *evt, uint32_t instr_meter) {
uint32_t num_instr = 0;
if (vmst->stack_canary != UVM32_NULL && *vmst->stack_canary != STACK_CANARY_VALUE) {
setStatusErr(vmst, UVM32_ERR_STACKOVERFLOW);
setup_err_evt(vmst, evt);
return num_instr;
}
if (vmst->status != UVM32_STATUS_PAUSED) {
setStatusErr(vmst, UVM32_ERR_NOTREADY);
setup_err_evt(vmst, evt);
@ -163,6 +174,25 @@ uint32_t uvm32_run(uvm32_state_t *vmst, uvm32_evt_t *evt, uint32_t instr_meter)
vmst->ioevt.typ = UVM32_EVT_YIELD;
setStatus(vmst, UVM32_STATUS_PAUSED);
break;
case UVM32_SYSCALL_STACKPROTECT: {
// don't allow errant code to change it once set
if (vmst->stack_canary == (uint8_t *)UVM32_NULL) {
uint32_t param0 = vmst->core.regs[10];
uint32_t mem_offset = param0 - MINIRV32_RAM_IMAGE_OFFSET;
// check data fits in ram
if (mem_offset > UVM32_MEMORY_SIZE) {
setStatusErr(vmst, UVM32_ERR_STACKOVERFLOW);
setup_err_evt(vmst, evt);
}
// check canary is inside valid memory
if (mem_offset < UVM32_MEMORY_SIZE) {
// set canary
vmst->stack_canary = &vmst->memory[mem_offset];
*vmst->stack_canary = STACK_CANARY_VALUE;
}
}
} break;
default:
// user defined syscalls
vmst->ioevt.typ = UVM32_EVT_SYSCALL;

View file

@ -15,6 +15,7 @@
X(UVM32_ERR_INTERNAL_CORE) \
X(UVM32_ERR_INTERNAL_STATE) \
X(UVM32_ERR_ARGS) \
X(UVM32_ERR_STACKOVERFLOW) \
#define X(name) name,
typedef enum {
@ -69,6 +70,7 @@ typedef struct {
struct MiniRV32IMAState core;
uint8_t memory[UVM32_MEMORY_SIZE];
uvm32_evt_t ioevt; // for building up in callbacks
uint8_t *stack_canary;
} uvm32_state_t;
void uvm32_init(uvm32_state_t *vmst);