Skip to content

Commit 7ad2e6a

Browse files
committed
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.
1 parent 4b5edd3 commit 7ad2e6a

9 files changed

Lines changed: 62 additions & 9 deletions

File tree

main.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -220,8 +220,8 @@ bool run_code_py(safe_mode_t safe_mode) {
220220
rgb_status_animation_t animation;
221221
prep_rgb_status_animation(&result, found_main, safe_mode, &animation);
222222
while (true) {
223-
#ifdef MICROPY_VM_HOOK_LOOP
224-
MICROPY_VM_HOOK_LOOP
223+
#ifdef CIRCUITPY_SUPERVISOR_BACKGROUND
224+
CIRCUITPY_SUPERVISOR_BACKGROUND
225225
#endif
226226
if (reload_requested) {
227227
return true;

ports/atmel-samd/background.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,18 +29,23 @@
2929
#include "tick.h"
3030
#include "supervisor/usb.h"
3131

32+
#include "py/runtime.h"
3233
#include "shared-module/displayio/__init__.h"
3334
#include "shared-module/network/__init__.h"
35+
#include "supervisor/shared/stack.h"
3436

3537
volatile uint64_t last_finished_tick = 0;
3638

39+
bool stack_ok_so_far = true;
40+
3741
void run_background_tasks(void) {
3842
#if (defined(SAMD21) && defined(PIN_PA02)) || defined(SAMD51)
3943
audio_dma_background();
4044
#endif
4145
#ifdef CIRCUITPY_DISPLAYIO
4246
displayio_refresh_display();
4347
#endif
48+
4449
#if MICROPY_PY_NETWORK
4550
network_module_background();
4651
#endif
@@ -49,6 +54,12 @@ void run_background_tasks(void) {
4954
last_finished_tick = ticks_ms;
5055
}
5156

57+
void run_background_vm_tasks(void) {
58+
assert_heap_ok();
59+
run_background_tasks();
60+
assert_heap_ok();
61+
}
62+
5263
bool background_tasks_ok(void) {
5364
return ticks_ms - last_finished_tick < 1000;
5465
}

ports/atmel-samd/background.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include <stdbool.h>
3131

3232
void run_background_tasks(void);
33+
void run_background_vm_tasks(void);
3334
bool background_tasks_ok(void);
3435

3536
#endif // MICROPY_INCLUDED_ATMEL_SAMD_BACKGROUND_H

ports/atmel-samd/common-hal/nvm/ByteArray.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828

2929
#include "hal_flash.h"
3030

31+
#include "supervisor/shared/stack.h"
32+
3133
#include <stdint.h>
3234
#include <string.h>
3335

@@ -42,6 +44,7 @@ bool common_hal_nvm_bytearray_set_bytes(nvm_bytearray_obj_t *self,
4244
struct flash_descriptor desc;
4345
desc.dev.hw = NVMCTRL;
4446
flash_write(&desc, (uint32_t) self->start_address + start_index, values, len);
47+
assert_heap_ok();
4548
return true;
4649
}
4750

ports/atmel-samd/mpconfigport.h

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ typedef long mp_off_t;
182182
#define MICROPY_PY_SYS_PLATFORM "MicroChip SAMD51"
183183
#define PORT_HEAP_SIZE (0x20000) // 128KiB
184184
#define SPI_FLASH_MAX_BAUDRATE 24000000
185-
#define CIRCUITPY_DEFAULT_STACK_SIZE 8192
185+
#define CIRCUITPY_DEFAULT_STACK_SIZE 0x6000
186186
#define MICROPY_CPYTHON_COMPAT (1)
187187
#define MICROPY_MODULE_WEAK_LINKS (1)
188188
#define MICROPY_PY_BUILTINS_NOTIMPLEMENTED (1)
@@ -432,10 +432,6 @@ extern const struct _mp_obj_module_t wiznet_module;
432432

433433
#define MP_STATE_PORT MP_STATE_VM
434434

435-
void run_background_tasks(void);
436-
#define MICROPY_VM_HOOK_LOOP run_background_tasks();
437-
#define MICROPY_VM_HOOK_RETURN run_background_tasks();
438-
439435
#include "peripherals/samd/dma.h"
440436

441437
#include "supervisor/flash_root_pointers.h"
@@ -454,6 +450,11 @@ void run_background_tasks(void);
454450
mp_obj_t gamepad_singleton; \
455451
NETWORK_ROOT_POINTERS \
456452

453+
void run_background_tasks(void);
454+
void run_background_vm_tasks(void);
455+
#define MICROPY_VM_HOOK_LOOP run_background_vm_tasks();
456+
#define MICROPY_VM_HOOK_RETURN run_background_vm_tasks();
457+
#define CIRCUITPY_SUPERVISOR_BACKGROUND run_background_tasks();
457458

458459
#define CIRCUITPY_AUTORELOAD_DELAY_MS 500
459460
#define CIRCUITPY_BOOT_OUTPUT_FILE "/boot_out.txt"

ports/atmel-samd/supervisor/port.c

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ extern volatile bool mp_msc_enabled;
8080
#define TRACE_BUFFER_SIZE (1 << (TRACE_BUFFER_MAGNITUDE_PACKETS + 1))
8181
// Size in bytes. 4 bytes per uint32_t.
8282
#define TRACE_BUFFER_SIZE_BYTES (TRACE_BUFFER_SIZE << 2)
83-
__attribute__((__aligned__(TRACE_BUFFER_SIZE_BYTES))) uint32_t mtb[TRACE_BUFFER_SIZE];
83+
__attribute__((__aligned__(TRACE_BUFFER_SIZE_BYTES))) uint32_t mtb[TRACE_BUFFER_SIZE] = {0};
8484
#endif
8585

8686
safe_mode_t port_init(void) {
@@ -285,6 +285,19 @@ void reset_to_bootloader(void) {
285285
*/
286286
__attribute__((used)) void HardFault_Handler(void)
287287
{
288+
#ifdef ENABLE_MICRO_TRACE_BUFFER
289+
// Turn off the micro trace buffer so we don't fill it up in the infinite
290+
// loop below.
291+
REG_MTB_MASTER = 0x00000000 + 6;
292+
#endif
293+
#ifdef CIRCUITPY_CANARY_WORD
294+
// If the canary is intact, then kill it and reset so we have a chance to
295+
// read our files.
296+
if (_ezero == CIRCUITPY_CANARY_WORD) {
297+
_ezero = CIRCUITPY_SAFE_RESTART_WORD;
298+
NVIC_SystemReset();
299+
}
300+
#endif
288301
while (true) {
289302
asm("");
290303
}

py/obj.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,12 @@
3333
#include "py/objtype.h"
3434
#include "py/objint.h"
3535
#include "py/objstr.h"
36+
#include "py/qstr.h"
3637
#include "py/runtime.h"
3738
#include "py/stackctrl.h"
3839
#include "py/stream.h" // for mp_obj_print
3940

41+
#include "supervisor/shared/stack.h"
4042
#include "supervisor/shared/translate.h"
4143

4244
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) {
8183

8284
// helper function to print an exception with traceback
8385
void mp_obj_print_exception(const mp_print_t *print, mp_obj_t exc) {
84-
if (mp_obj_is_exception_instance(exc)) {
86+
if (mp_obj_is_exception_instance(exc) && stack_ok()) {
8587
size_t n, *values;
8688
mp_obj_exception_get_traceback(exc, &n, &values);
8789
if (n > 0) {

supervisor/shared/stack.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "stack.h"
2828

2929
#include "py/mpconfig.h"
30+
#include "py/runtime.h"
3031
#include "supervisor/cpu.h"
3132

3233
extern uint32_t _estack;
@@ -37,6 +38,8 @@ supervisor_allocation* stack_alloc = NULL;
3738

3839
#define EXCEPTION_STACK_SIZE 1024
3940

41+
#define STACK_CANARY_VALUE 0x017829ef
42+
4043
void allocate_stack(void) {
4144
mp_uint_t regs[10];
4245
mp_uint_t sp = cpu_get_regs_and_sp(regs);
@@ -50,6 +53,19 @@ void allocate_stack(void) {
5053
} else {
5154
current_stack_size = next_stack_size;
5255
}
56+
*stack_alloc->ptr = STACK_CANARY_VALUE;
57+
}
58+
59+
inline bool stack_ok(void) {
60+
return *stack_alloc->ptr == STACK_CANARY_VALUE;
61+
}
62+
63+
inline void assert_heap_ok(void) {
64+
if (!stack_ok()) {
65+
asm("nop");
66+
while(true) {}
67+
mp_raise_RuntimeError(translate("Stack clobbered heap."));
68+
}
5369
}
5470

5571
void stack_init(void) {
@@ -58,6 +74,7 @@ void stack_init(void) {
5874

5975
void stack_resize(void) {
6076
if (next_stack_size == current_stack_size) {
77+
*stack_alloc->ptr = STACK_CANARY_VALUE;
6178
return;
6279
}
6380
free_memory(stack_alloc);

supervisor/shared/stack.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,5 +37,10 @@ void stack_init(void);
3737
void stack_resize(void);
3838
void set_next_stack_size(uint32_t size);
3939
uint32_t get_current_stack_size(void);
40+
bool stack_ok(void);
41+
42+
// Use this after any calls into a library which may use a lot of stack. This will raise a Python
43+
// exception when the stack has likely overwritten a portio of the heap.
44+
void assert_heap_ok(void);
4045

4146
#endif // MICROPY_INCLUDED_SUPERVISOR_STACK_H

0 commit comments

Comments
 (0)