Add stack validity check and raise an error when it happens.

The backtrace cannot be given because it relies on the validity
of the qstr data structures on the heap which may have been
corrupted.

In fact, it still can crash hard when the bytecode itself is
overwritten. To fix, we'd need a way to skip gathering the
backtrace completely.

This also increases the default stack size on M4s so it can
accomodate the stack needed by ASF4s nvm API.
This commit is contained in:
Scott Shawcroft 2018-10-22 17:57:28 -07:00
parent 4b5edd3c03
commit 7ad2e6ace3
No known key found for this signature in database
GPG Key ID: FD0EDC4B6C53CA59
9 changed files with 62 additions and 9 deletions

4
main.c
View File

@ -220,8 +220,8 @@ bool run_code_py(safe_mode_t safe_mode) {
rgb_status_animation_t animation;
prep_rgb_status_animation(&result, found_main, safe_mode, &animation);
while (true) {
#ifdef MICROPY_VM_HOOK_LOOP
MICROPY_VM_HOOK_LOOP
#ifdef CIRCUITPY_SUPERVISOR_BACKGROUND
CIRCUITPY_SUPERVISOR_BACKGROUND
#endif
if (reload_requested) {
return true;

View File

@ -29,11 +29,15 @@
#include "tick.h"
#include "supervisor/usb.h"
#include "py/runtime.h"
#include "shared-module/displayio/__init__.h"
#include "shared-module/network/__init__.h"
#include "supervisor/shared/stack.h"
volatile uint64_t last_finished_tick = 0;
bool stack_ok_so_far = true;
void run_background_tasks(void) {
#if (defined(SAMD21) && defined(PIN_PA02)) || defined(SAMD51)
audio_dma_background();
@ -41,6 +45,7 @@ void run_background_tasks(void) {
#ifdef CIRCUITPY_DISPLAYIO
displayio_refresh_display();
#endif
#if MICROPY_PY_NETWORK
network_module_background();
#endif
@ -49,6 +54,12 @@ void run_background_tasks(void) {
last_finished_tick = ticks_ms;
}
void run_background_vm_tasks(void) {
assert_heap_ok();
run_background_tasks();
assert_heap_ok();
}
bool background_tasks_ok(void) {
return ticks_ms - last_finished_tick < 1000;
}

View File

@ -30,6 +30,7 @@
#include <stdbool.h>
void run_background_tasks(void);
void run_background_vm_tasks(void);
bool background_tasks_ok(void);
#endif // MICROPY_INCLUDED_ATMEL_SAMD_BACKGROUND_H

View File

@ -28,6 +28,8 @@
#include "hal_flash.h"
#include "supervisor/shared/stack.h"
#include <stdint.h>
#include <string.h>
@ -42,6 +44,7 @@ bool common_hal_nvm_bytearray_set_bytes(nvm_bytearray_obj_t *self,
struct flash_descriptor desc;
desc.dev.hw = NVMCTRL;
flash_write(&desc, (uint32_t) self->start_address + start_index, values, len);
assert_heap_ok();
return true;
}

View File

@ -182,7 +182,7 @@ typedef long mp_off_t;
#define MICROPY_PY_SYS_PLATFORM "MicroChip SAMD51"
#define PORT_HEAP_SIZE (0x20000) // 128KiB
#define SPI_FLASH_MAX_BAUDRATE 24000000
#define CIRCUITPY_DEFAULT_STACK_SIZE 8192
#define CIRCUITPY_DEFAULT_STACK_SIZE 0x6000
#define MICROPY_CPYTHON_COMPAT (1)
#define MICROPY_MODULE_WEAK_LINKS (1)
#define MICROPY_PY_BUILTINS_NOTIMPLEMENTED (1)
@ -432,10 +432,6 @@ extern const struct _mp_obj_module_t wiznet_module;
#define MP_STATE_PORT MP_STATE_VM
void run_background_tasks(void);
#define MICROPY_VM_HOOK_LOOP run_background_tasks();
#define MICROPY_VM_HOOK_RETURN run_background_tasks();
#include "peripherals/samd/dma.h"
#include "supervisor/flash_root_pointers.h"
@ -454,6 +450,11 @@ void run_background_tasks(void);
mp_obj_t gamepad_singleton; \
NETWORK_ROOT_POINTERS \
void run_background_tasks(void);
void run_background_vm_tasks(void);
#define MICROPY_VM_HOOK_LOOP run_background_vm_tasks();
#define MICROPY_VM_HOOK_RETURN run_background_vm_tasks();
#define CIRCUITPY_SUPERVISOR_BACKGROUND run_background_tasks();
#define CIRCUITPY_AUTORELOAD_DELAY_MS 500
#define CIRCUITPY_BOOT_OUTPUT_FILE "/boot_out.txt"

View File

@ -80,7 +80,7 @@ extern volatile bool mp_msc_enabled;
#define TRACE_BUFFER_SIZE (1 << (TRACE_BUFFER_MAGNITUDE_PACKETS + 1))
// Size in bytes. 4 bytes per uint32_t.
#define TRACE_BUFFER_SIZE_BYTES (TRACE_BUFFER_SIZE << 2)
__attribute__((__aligned__(TRACE_BUFFER_SIZE_BYTES))) uint32_t mtb[TRACE_BUFFER_SIZE];
__attribute__((__aligned__(TRACE_BUFFER_SIZE_BYTES))) uint32_t mtb[TRACE_BUFFER_SIZE] = {0};
#endif
safe_mode_t port_init(void) {
@ -285,6 +285,19 @@ void reset_to_bootloader(void) {
*/
__attribute__((used)) void HardFault_Handler(void)
{
#ifdef ENABLE_MICRO_TRACE_BUFFER
// Turn off the micro trace buffer so we don't fill it up in the infinite
// loop below.
REG_MTB_MASTER = 0x00000000 + 6;
#endif
#ifdef CIRCUITPY_CANARY_WORD
// If the canary is intact, then kill it and reset so we have a chance to
// read our files.
if (_ezero == CIRCUITPY_CANARY_WORD) {
_ezero = CIRCUITPY_SAFE_RESTART_WORD;
NVIC_SystemReset();
}
#endif
while (true) {
asm("");
}

View File

@ -33,10 +33,12 @@
#include "py/objtype.h"
#include "py/objint.h"
#include "py/objstr.h"
#include "py/qstr.h"
#include "py/runtime.h"
#include "py/stackctrl.h"
#include "py/stream.h" // for mp_obj_print
#include "supervisor/shared/stack.h"
#include "supervisor/shared/translate.h"
mp_obj_type_t *mp_obj_get_type(mp_const_obj_t o_in) {
@ -81,7 +83,7 @@ void mp_obj_print(mp_obj_t o_in, mp_print_kind_t kind) {
// helper function to print an exception with traceback
void mp_obj_print_exception(const mp_print_t *print, mp_obj_t exc) {
if (mp_obj_is_exception_instance(exc)) {
if (mp_obj_is_exception_instance(exc) && stack_ok()) {
size_t n, *values;
mp_obj_exception_get_traceback(exc, &n, &values);
if (n > 0) {

View File

@ -27,6 +27,7 @@
#include "stack.h"
#include "py/mpconfig.h"
#include "py/runtime.h"
#include "supervisor/cpu.h"
extern uint32_t _estack;
@ -37,6 +38,8 @@ supervisor_allocation* stack_alloc = NULL;
#define EXCEPTION_STACK_SIZE 1024
#define STACK_CANARY_VALUE 0x017829ef
void allocate_stack(void) {
mp_uint_t regs[10];
mp_uint_t sp = cpu_get_regs_and_sp(regs);
@ -50,6 +53,19 @@ void allocate_stack(void) {
} else {
current_stack_size = next_stack_size;
}
*stack_alloc->ptr = STACK_CANARY_VALUE;
}
inline bool stack_ok(void) {
return *stack_alloc->ptr == STACK_CANARY_VALUE;
}
inline void assert_heap_ok(void) {
if (!stack_ok()) {
asm("nop");
while(true) {}
mp_raise_RuntimeError(translate("Stack clobbered heap."));
}
}
void stack_init(void) {
@ -58,6 +74,7 @@ void stack_init(void) {
void stack_resize(void) {
if (next_stack_size == current_stack_size) {
*stack_alloc->ptr = STACK_CANARY_VALUE;
return;
}
free_memory(stack_alloc);

View File

@ -37,5 +37,10 @@ void stack_init(void);
void stack_resize(void);
void set_next_stack_size(uint32_t size);
uint32_t get_current_stack_size(void);
bool stack_ok(void);
// Use this after any calls into a library which may use a lot of stack. This will raise a Python
// exception when the stack has likely overwritten a portio of the heap.
void assert_heap_ok(void);
#endif // MICROPY_INCLUDED_SUPERVISOR_STACK_H