Somthing is clobbering two stack locations?

Status
Not open for further replies.

Bill Greiman

Well-known member
It appears that something in an isr is clobbering two locations near the beginning of the stack. Here is a sketch that demonstrates the possible problem. This is for beta7.
Code:
extern unsigned long _ebss;
extern unsigned long _estack;
#include <unistd.h>

void setup() {
  // fill start of stack
  uint32_t* stack = (uint32_t*)sbrk(0);
  for (size_t i = 0; i < 1000; i++) stack[i] = 0X55555555;

  // see if all is well
  bool check1 = true;  
  for (size_t i = 0; i < 1000; i++) {
    if (stack[i] != 0X55555555) check1 = false;
  }
  // wait for interrupts
  delay(1000);
  // check again
  bool check2 = true;
  for (size_t i = 0; i < 1000; i++) {
    if (stack[i] != 0X55555555) check2 = false;
  } 
  Serial.begin(9600);
  while(!Serial) {}
  delay(2000);
  Serial.print("check1 ");
  Serial.println(check1, DEC);
  Serial.print("check2 ");
  Serial.println(check2, DEC);
  // print clobbered stack locations
  for (size_t i = 0; i < 1000; i++) {
    if (stack[i] == 0X55555555) continue;
    Serial.print((int)&stack[i], HEX);
    Serial.print(' ');
    Serial.println(stack[i], HEX);
  }
  Serial.print("&_ebss  ");
  Serial.println((int)&_ebss, HEX);
  Serial.print("sbrk(0) ");
  Serial.println((int)sbrk(0), HEX);
  Serial.print("stack   ");
  Serial.println((int)stack, HEX);
}
void loop() {}
This is typical output from the sketch:
check1 1
check2 0
1FFFEC50 2580
1FFFEC54 1080000
&_ebss 1FFFEC2C
sbrk(0) 1FFFEC2C
stack 1FFFEC2C
The sketch fills the beginning of the stack with a pattern of 0X55555555. It then checks to see if all is well and saves the result in check1.

The sketch then delays for a second, rechecks the stack and stores the result in check2.

The result is that two locations are overwritten after the delay. These locations seem to always have the same values.

This is far from the part of the stack that should be used.
 
just about the same results here

I added some code to show the elapsed time in milliseconds, and the stack index where the value is written.
Code:
extern unsigned long _ebss;
extern unsigned long _estack;
#include <unistd.h>

#define TMAX 2000    // how many milliseconds to test for stack change
byte dflag[500];

void setup() {
  int j, ltime;
  ltime = 0;
  
  // fill start of stack
  uint32_t* stack = (uint32_t*)sbrk(0);
  for (size_t i = 0; i < 1000; i++) stack[i] = 0X55555555;

  // see if all is well
  bool check1 = true;  
  for (size_t i = 0; i < 1000; i++) {
    if (stack[i] != 0X55555555) check1 = false;
  }
  bool check2 = true;
  // wait for interrupts
  for (j=0;j<TMAX;j++) {
    delay(1);
    // check again
    for (size_t i = 0; i < 1000; i++) {
      if (stack[i] != 0X55555555) {
         check2 = false;
         ltime = j;
         j = TMAX; // force outer loop quit
      } 
    }
  }
  Serial.begin(9600);
  while(!Serial) {}
  delay(2000);
  Serial.print("check1 ");
  Serial.println(check1, DEC);
  Serial.print("check2 ");
  Serial.println(check2, DEC);
  Serial.print("msecs: ");
  Serial.println(ltime, DEC);
  
  // print clobbered stack locations
  for (size_t i = 0; i < 1000; i++) {
    if (stack[i] == 0X55555555) continue;
    Serial.print((int)i);
    Serial.print(": ");
    Serial.print((int)&stack[i], HEX);
    Serial.print(' ');
    Serial.println(stack[i], HEX);
  }
  Serial.print("&_ebss  ");
  Serial.println((int)&_ebss, HEX);
  Serial.print("sbrk(0) ");
  Serial.println((int)sbrk(0), HEX);
  Serial.print("stack   ");
  Serial.println((int)stack, HEX);
}

void loop() {}

Example results. Stack values are constant but "msecs" varies: 434, 613, 701, 361, etc.
Code:
check1 1
check2 0
msecs: 434
9: 1FFFEC50 1C200
10: 1FFFEC54 1080000
&_ebss  1FFFEC2C
sbrk(0) 1FFFEC2C
stack   1FFFEC2C

Also interesting: running at 24 MHz, "msecs" reported value is about half, and running at 96 MHz it is about double, the numbers I get at 48 MHz (shown above).
 
Last edited:
Sorry I should have posted this in the bugs section.

Looks more serious than I first realized. The problem is near the start of the heap so String, SD.h, and anything else that uses malloc() will fail.

This sketch
Code:
void setup() {
  uint32_t* heap = (uint32_t*)malloc(400);
  for (size_t i = 0; i < 100; i++) heap[i] = 0X55555555;
  Serial.begin(9600);
  while (!Serial) {}
  delay(2000);
  for (size_t i = 0; i < 100; i++) {
    if (heap[i] == 0X55555555) continue;
    Serial.print((int)&heap[i], HEX);
    Serial.print(' ');
    Serial.println(heap[i], HEX);
  }
}
void loop() {}
Prints this
1FFFF094 2580
1FFFF098 1080000
 
Last edited:
USB vs UART makes a difference

Note: only one byte in one heap location gets clobbered instead of two uint32 words, if you use Serial1 (UART) instead of Serial (USB):

Code:
check1 1
check2 0
msecs: 325
10: 1FFFEC54 555555
&_ebss  1FFFEC2C
sbrk(0) 1FFFEC2C
stack   1FFFEC2C

Code:
extern unsigned long _ebss;
extern unsigned long _estack;
#include <unistd.h>

#define TMAX 2000    // how many milliseconds to wait for stack to change
byte dflag[500];

void setup() {
  int j, ltime;
  ltime = 0;
  
  // fill start of stack
  uint32_t* stack = (uint32_t*)sbrk(0);
  for (size_t i = 0; i < 1000; i++) stack[i] = 0X55555555;

  // see if all is well
  bool check1 = true;  
  for (size_t i = 0; i < 1000; i++) {
    if (stack[i] != 0X55555555) check1 = false;
  }
  bool check2 = true;
  // wait for interrupts
  for (j=0;j<TMAX;j++) {
    delay(1);
    // check again
    for (size_t i = 0; i < 1000; i++) {
      if (stack[i] != 0X55555555) {
         check2 = false;
         ltime = j;
         j = TMAX; // force outer loop quit
      } 
    }
  }
  Serial1.begin(9600);
//  while(!Serial) {}
  delay(2000);
  Serial1.print("check1 ");
  Serial1.println(check1, DEC);
  Serial1.print("check2 ");
  Serial1.println(check2, DEC);
  Serial1.print("msecs: ");
  Serial1.println(ltime, DEC);
  
  // print clobbered stack locations
  for (size_t i = 0; i < 1000; i++) {
    if (stack[i] == 0X55555555) continue;
    Serial1.print((int)i);
    Serial1.print(": ");
    Serial1.print((int)&stack[i], HEX);
    Serial1.print(' ');
    Serial1.println(stack[i], HEX);
  }
  Serial1.print("&_ebss  ");
  Serial1.println((int)&_ebss, HEX);
  Serial1.print("sbrk(0) ");
  Serial1.println((int)sbrk(0), HEX);
  Serial1.print("stack   ");
  Serial1.println((int)stack, HEX);
}

void loop() {}
 
Last edited:
Aha! And if you disconnect USB entirely, running from separate power to the "Vin" pin, the problem goes away.

Code:
check1 1
check2 1
msecs: 0
&_ebss  1FFFEC2C
sbrk(0) 1FFFEC2C
stack   1FFFEC2C

Teensy3_FTDI.JPG
 
Last edited:
Yikes. I can't look at this until tomorrow, but it sounds pretty serious. Will investigate as soon as possible.
 
JBeale,

Great work! Looks like this bug is going to be squashed.

I would like to suggest that the Teensy 3.0 ResetHandler should initialize the area from _ebss to _estack with the 0X55 pattern in every byte.

A pattern like this in unused memory helps find bugs and simple programs can analyze stack/heap usage.
 
Status
Not open for further replies.
Back
Top