diff --git a/doc/README.md b/doc/README.md index 22b3e9f..f8a3c22 100644 --- a/doc/README.md +++ b/doc/README.md @@ -183,6 +183,8 @@ The uvm32 memory size is set at compile time with `-DUVM32_MEMORY_SIZE=X` (in by Define `UVM32_ERROR_STRINGS` to add an `errstr` field to `uvm32_evt_err_t` giving a printable error string. +Define `UVM32_STACK_PROTECTION` to enable a basic stack canary, to cause an early crash when the stack grows too large. Without this, the VM will normally crash (safely) in some other way which is less easily detected. + ## Debugging Binaries can be disassembled with diff --git a/precompiled/self.bin b/precompiled/self.bin index 20484c7..d25ca78 100755 Binary files a/precompiled/self.bin and b/precompiled/self.bin differ diff --git a/uvm32/uvm32.c b/uvm32/uvm32.c index c1ccba0..536a7b7 100644 --- a/uvm32/uvm32.c +++ b/uvm32/uvm32.c @@ -16,8 +16,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 +#ifdef UVM32_STACK_PROTECTION +#define STACK_CANARY_VALUE 0x42 // magic value for stack canary +#endif #ifndef UVM32_MEMCPY #define UVM32_MEMCPY uvm32_memcpy @@ -101,8 +102,9 @@ bool uvm32_load(uvm32_state_t *vmst, const uint8_t *rom, int len) { } UVM32_MEMCPY(vmst->_memory, rom, len); +#ifdef UVM32_STACK_PROTECTION vmst->_stack_canary = (uint8_t *)NULL; - +#endif return true; } @@ -207,11 +209,13 @@ uint32_t uvm32_run(uvm32_state_t *vmst, uvm32_evt_t *evt, uint32_t instr_meter) orig_instr_meter = min_instrs; } +#ifdef UVM32_STACK_PROTECTION if (vmst->_stack_canary != NULL && *vmst->_stack_canary != STACK_CANARY_VALUE) { setStatusErr(vmst, UVM32_ERR_INTERNAL_CORE); setup_err_evt(vmst, evt); return orig_instr_meter - instr_meter; } +#endif if (vmst->_status != UVM32_STATUS_PAUSED) { setStatusErr(vmst, UVM32_ERR_NOTREADY); @@ -243,6 +247,7 @@ uint32_t uvm32_run(uvm32_state_t *vmst, uvm32_evt_t *evt, uint32_t instr_meter) setStatus(vmst, UVM32_STATUS_ENDED); break; case UVM32_SYSCALL_STACKPROTECT: { +#ifdef UVM32_STACK_PROTECTION // don't allow errant code to change it once set if (vmst->_stack_canary == (uint8_t *)NULL) { uint32_t param0 = vmst->_core.regs[10]; // a0 @@ -263,6 +268,7 @@ uint32_t uvm32_run(uvm32_state_t *vmst, uvm32_evt_t *evt, uint32_t instr_meter) *vmst->_stack_canary = STACK_CANARY_VALUE; } } +#endif } break; default: // user defined syscalls diff --git a/uvm32/uvm32.h b/uvm32/uvm32.h index 1027686..51a207e 100644 --- a/uvm32/uvm32.h +++ b/uvm32/uvm32.h @@ -118,7 +118,9 @@ typedef struct { struct MiniRV32IMAState _core; /*! CPU registers */ uint8_t _memory[UVM32_MEMORY_SIZE]; /*! Memory */ uvm32_evt_t _ioevt; /*! Event to be returned on next pause */ +#ifdef UVM32_STACK_PROTECTION uint8_t *_stack_canary; /*! Location of stack canary */ +#endif uint8_t *_extram; /*! External RAM pointer, or NULL */ uint32_t _extramLen; /*! Length of external RAM */ bool _extramDirty; /*! Flag to indicate VM code has modified extram since last run */