Add stack overgrow detection

If the stack ever grows too large (and starts overwriting variables
in .bss) the firmware jumps into the bootloader. This is better than
just continuing to run with strange side effects.
This commit is contained in:
Raphael Assenat 2017-01-08 15:14:55 -05:00
parent 2eaafb7786
commit b22985712f
6 changed files with 85 additions and 3 deletions

View File

@ -7,7 +7,7 @@ include Makefile.inc
PROGNAME=gcn64usb PROGNAME=gcn64usb
OBJDIR=objs-$(PROGNAME) OBJDIR=objs-$(PROGNAME)
CPU=atmega32u2 CPU=atmega32u2
CFLAGS=-Wall -mmcu=$(CPU) -DF_CPU=16000000L -Os -DUART1_STDOUT -DVERSIONSTR=$(VERSIONSTR) -DVERSIONSTR_SHORT=$(VERSIONSTR_SHORT) CFLAGS=-Wall -mmcu=$(CPU) -DF_CPU=16000000L -Os -DUART1_STDOUT -DVERSIONSTR=$(VERSIONSTR) -DVERSIONSTR_SHORT=$(VERSIONSTR_SHORT) -std=gnu99
LDFLAGS=-mmcu=$(CPU) -Wl,-Map=$(PROGNAME).map LDFLAGS=-mmcu=$(CPU) -Wl,-Map=$(PROGNAME).map
HEXFILE=$(PROGNAME).hex HEXFILE=$(PROGNAME).hex

View File

@ -1,3 +1,3 @@
OBJS=main.o usb.o usbpad.o mappings.o gcn64_protocol.o n64.o gamecube.o usart1.o bootloader.o eeprom.o config.o hiddata.o usbstrings.o intervaltimer.o version.o gcn64txrx0.o gcn64txrx1.o gcn64txrx2.o gcn64txrx3.o gamepads.o OBJS=main.o usb.o usbpad.o mappings.o gcn64_protocol.o n64.o gamecube.o usart1.o bootloader.o eeprom.o config.o hiddata.o usbstrings.o intervaltimer.o version.o gcn64txrx0.o gcn64txrx1.o gcn64txrx2.o gcn64txrx3.o gamepads.o stkchk.o
VERSIONSTR=\"3.4.0\" VERSIONSTR=\"3.4.0\"
VERSIONSTR_SHORT=\"3.4\" VERSIONSTR_SHORT=\"3.4\"

View File

@ -7,7 +7,7 @@ include Makefile.inc
PROGNAME=gcn64usb-stk500 PROGNAME=gcn64usb-stk500
OBJDIR=objs-$(PROGNAME) OBJDIR=objs-$(PROGNAME)
CPU=at90usb1287 CPU=at90usb1287
CFLAGS=-Wall -mmcu=$(CPU) -DF_CPU=16000000L -Os -DUART1_STDOUT -DSTK525 -DVERSIONSTR=$(VERSIONSTR) -DVERSIONSTR_SHORT=$(VERSIONSTR_SHORT) CFLAGS=-Wall -mmcu=$(CPU) -DF_CPU=16000000L -Os -DUART1_STDOUT -DSTK525 -DVERSIONSTR=$(VERSIONSTR) -DVERSIONSTR_SHORT=$(VERSIONSTR_SHORT) -std=gnu99
LDFLAGS=-mmcu=$(CPU) -Wl,-Map=$(PROGNAME).map LDFLAGS=-mmcu=$(CPU) -Wl,-Map=$(PROGNAME).map
HEXFILE=$(PROGNAME).hex HEXFILE=$(PROGNAME).hex

6
main.c
View File

@ -37,6 +37,7 @@
#include "usbstrings.h" #include "usbstrings.h"
#include "intervaltimer.h" #include "intervaltimer.h"
#include "requests.h" #include "requests.h"
#include "stkchk.h"
#define MAX_PLAYERS 2 #define MAX_PLAYERS 2
@ -444,6 +445,7 @@ int main(void)
usart1_init(); usart1_init();
eeprom_init(); eeprom_init();
intervaltimer_init(); intervaltimer_init();
stkchk_init();
switch (g_eeprom_data.cfg.mode) switch (g_eeprom_data.cfg.mode)
{ {
@ -511,6 +513,10 @@ int main(void)
{ {
static char last_v[MAX_PLAYERS] = { }; static char last_v[MAX_PLAYERS] = { };
if (stkchk_verify()) {
enterBootLoader();
}
usb_doTasks(); usb_doTasks();
hiddata_doTask(&hiddata_ops); hiddata_doTask(&hiddata_ops);

60
stkchk.c Normal file
View File

@ -0,0 +1,60 @@
#include <stdio.h>
#include <stdint.h>
#include <avr/io.h>
#include <util/atomic.h>
#include "stkchk.h"
extern uint16_t __stack;
extern uint16_t _end;
/** Write a canary at the end of the stack. */
void stkchk_init(void)
{
*((&_end)-1) = 0xDEAD;
}
/** Check if the canary is still alive.
*
* Call this perdiocally to check if the
* stack grew too large.
**/
char stkchk_verify(void)
{
if (*((&_end)-1) != 0xDEAD) {
return -1;
}
return 0;
}
/* In order to get an approximate idea of how
* much stack you are using, this can be called
* at strategic places where you know the
* call stack is deep or where large automatic
* buffers are used.
*/
#ifdef STKCHK_WITH_STATUS_CHECK
void stkchk(const char *fname)
{
static int max_usage = 0;
uint16_t end = ((uint16_t)&_end);
uint16_t s_top = ((uint16_t)&__stack);
uint16_t s_cur = SPL | SPH<<8;
uint16_t used, s_size;
uint8_t grew = 0;
ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
{
used = s_top - s_cur;
s_size = s_top - end;
if (used > max_usage) {
max_usage = used;
grew = 1;
}
}
if (grew) {
printf("[stkchk] %s: %d/%d\r\n", fname, used, s_size);
}
}
#endif

16
stkchk.h Normal file
View File

@ -0,0 +1,16 @@
#ifndef _stkchk_h__
#define _stkchk_h__
#undef STKCHK_WITH_STATUS_CHECK
#ifdef STKCHK_WITH_STATUS_CHECK
#define stkcheck() stkchk(__FUNCTION__);
void stkchk(const char *label);
#else
#define stkcheck()
#endif
void stkchk_init(void);
char stkchk_verify(void);
#endif // _stkchk_h__