Can you brick your Arduino using C/C++ pointers?

I’ve been investigating C/C++ pointers :slight_smile:
I crashed the Uno board so it wouldn’t soft reset :frowning:

I let a pointer run amok like this:-

for (int i = 0; i < ENTROPY_BUFFER_SIZE; i++) {
      *entropyBufferPtr++ = analogRead(ANALOGUE_PIN) & 0xff;
}

without resetting it every time the loop was entered. It kinda ran off on it’s own. The board became totally unresponsive and had to be hard booted by toggling the power. The IDE wouldn’t recognise it at all. I wrote crap all over it’s memory map. Since the AVR chip is soft programmed without those weird +15V programming levels that old PICs needed, the question arises, can I brick the AVR with a rogue pointer? Does the boot loader have anti-stupidity protection diodes or something?

It’s certainly stopped me from investigating further till I get an answer…

You can corrupt RAM. You could even corrupt the interrupt vector table where the soft reset vector address is, so the reset button wouldn't work. But every time you turn off the power the data in the RAM disappears anyway. It will boot up fine. You can't write to EEPROM or Flash using a simple pointer.

I can't think of any way you could possibly brick the Arduino writing data through a runaway pointer. I wouldn't even worry about it.

Whew!

Although I have to admit I'm struggling to see the use of pointers in Arduinos anyway. With globally scoped major variables /structures due to the limited RAM, there doesn't seem to be much occasion for passing by reference to functions.

I'll restart my messing about with abandon. Thanks.

cossoft:
Although I have to admit I'm struggling to see the use of pointers in Arduinos anyway.

Pointers are integral to doing just about any real programming the C language. Passing large structures by value is wasteful of processor cycles and stack space, same goes for C++ objects. Additionally, the overuse of globals is a sure sign of poor coding technique.

cossoft:
I crashed the Uno board so it wouldn't soft reset :frowning:

Just push the reset button, or power-down and power-up, or load a new program to it. Or upload a new bootloader.

If an uno really does fail or becomes a brick, then make sure to have a few others (spares) waiting in the wings. Then at least you can still do something and maybe even try to fix the busted one later.

Also, if a Uno can be bricked using software, then it is likely that the very first page of an Uno tutorial would say 'don't do this ...whatever you do...'. But also, 'bricked' should really be defined as 'one way ticket'. Normally, it would mean once it's gone past a point of no return, then there really is no return - unless something major needs to be done, like chip or IC surgery or replacement. That should be the proper definition of 'brick'.

But you don't have to pass global variables :wink:

Use of global versus local is a fine balance (between coding practices and available resources) on a micro with limited memory in my opinion.

Southpark:
Just push the reset button, or power-down and power-up, or load a new program to it. Or upload a new bootloader.

Reset didn't do anything. Jimmus suggested that the interrupt vectors might have been trashed.
Wouldn't upload sketches as the IDE no longer recognised that a board was connected.

Only withdrawing power recovered it. I was concerned that I might overwrite the bootloader. I'm not sure introductory tutorials should start by mentioning bad pointer arithmetic though...

gfvalvo:
Pointers are integral to doing just about any real programming the C language ... same goes for C++ objects. Additionally, the overuse of globals is a sure sign of poor coding technique.

That's a bit harsh isn't it considering the RAM estate of an Uno? Isn't C++'s "new" banned within the IDE for expressly that reason? Remember we're talking Arduino Uno not Dell PowerEdge. The coding environment is a bit different.

cossoft:
Isn't C++'s "new" banned within the IDE for expressly that reason?

¿what?

gfvalvo:
Pointers are integral to doing just about any real programming the C language. Passing large structures by value is wasteful of processor cycles and stack space, same goes for C++ objects. Additionally, the overuse of globals is a sure sign of poor coding technique.

Use references as the normal case is a good way to go.

Globals are rightly common in microcontroller programs, don't carry over large-processor practices to a
microcontroller without thinking it through - ISRs can only access globals, for instance.

There are very limited memory resources and globals are statically allocated so you can ensure at compile
time you have enough memory. Dynamic memory allocation can blow a microcontroller out of the water
at a random time in the future - ie its a potential timebomb in an otherwise high reliability environment.

cossoft:
That's a bit harsh isn't it considering the RAM estate of an Uno? Isn't C++'s "new" banned within the IDE for expressly that reason? Remember we're talking Arduino Uno not Dell PowerEdge. The coding environment is a bit different.

No, dynamic memory allocation is perfectly legal. That being said, allocating and releasing memory with wild abandon could quite possibly get you into memory leak trouble due to fragmentation of the limited RAM. I'd say it's unnecessary in the vast majority of cases since they're single-task applications and you either have enough memory or you don't.

However, I didn't say anything about dynamic allocation, and neither did you in your original post. The example code you showed could have also blown up using array notation. You overran the buffer (or under-sized it).

There are plenty of uses for pointers even if all your memory structures are statically allocated. And, I maintain my point that C programming without pointers isn't C programming. Take for example a function that you want to pass an array to. You could use a global array, but what if you want the function to operate on different arrays in different places? Then, you have to pass the array to the function as an argument. To do this, you're going to use a pointer -- whether you know it or not.

Globals certainly have their place. That's why I said the overuse of them is a poor practice. But, unless doing away with one results in awkward, inefficient code structure, I'll attempt to limit scope as much as possible by using static locals and passing pointers if the data structures are large.

gfvalvo:
Globals certainly have their place. That's why I said the overuse of them is a poor practice. But, unless doing away with one results in awkward, inefficient code structure, I'll attempt to limit scope as much as possible by using static locals and passing pointers if the data structures are large.

This is nice advice and often overlooked in the pure beginner space; though beat into the minds of first year CS students; what with all those ramblings about the heap and stack and whatnot. :wink:

I see (with kids that I work with playing with Arduino) that projects seem to start off small and morph as their skills improve and their ideas become more bold. Having the fundamentals in place early on is a great way of avoiding collisions later on. Plus it makes it easier to pull out bits of what you've/they've done for other projects.

cossoft:
Reset didn't do anything. Jimmus suggested that the interrupt vectors might have been trashed.

That doesn't even make sense. Even if interrupts got disabled by some rogue function call turning them all off, RESET is nonmaskable (fancy way of saying you can't disable it) on basically every processor ever.

Maybe it latched up somehow? Or the power regulator overheated for some reason. I don't think there's anything you can do in the software that would cause RESET to not work, that has to be a hardware fault of some kind.

BulldogLowell:
I see (with kids that I work with playing with Arduino) that projects seem to start off small and morph as their skills improve and their ideas become more bold. Having the fundamentals in place early on is a great way of avoiding collisions later on. Plus it makes it easier to pull out bits of what you've/they've done for other projects.

It's also necessary to be willing to rewrite a project from scratch (sometimes multiple times) as your skills improve and you learn new techniques. Reworking existing code can only get you so far, and if you keep that up for long enough then what might have started as a simple and workable bit of code mutates into a Frankenstein monstrosity.

That doesn't even make sense.

Turns out you are absolutely correct. Somehow I figured the interrupt vector table would be copied into RAM, so you could intercept interrupts by inserting your own ISR address into the table at run time. Turns out the interrupt vector table is burned into flash, and you can't take over interrupts that simply.

Even attachInterrupt() doesn't work by actually putting the address of your interrupt service routine into the interrupt vector table -- it is called indirectly by a wrapper ISR. It's even less efficient than I thought!

But I don't know how you could hang the Arduino hard enough to require a power cycle. I have read about other people experiencing this, and they say it has to do with "noise", whatever that means. Still, cycling the power has always fixed it.

Jimmus:
I have read about other people experiencing this, and they say it has to do with “noise”, whatever that means.

Well I swear by the lives of all my gold fish that it happened. Also, I’m not a beginner with either electronics or programming (just with C pointers). (Un)fortunately my code has evolved and I can no longer replicate the behaviour. Important factoid - the code writes somewhat random data that changes with each run so the bytes written are entirely non deterministic. The upshot is that it is impossible to know what was written, but I could replicate it on demand at the time. I remember that there were several (10?) attempts in the IDE console at trying to unsuccessfully communicate with the board.

So inverting the problem, how would you make the Uno apparently unresponsive to a RESET press using only pointers spewing quasi-random data? What about the watchdog?

Note: If the Watchdog is accidentally enabled, for example by a runaway pointer or brown-out condition, the
device will be reset and the Watchdog Timer will stay enabled. If the code is not set up to handle the Watchdog,
this might lead to an eternal loop of time-out resets. To avoid this situation, the application software should
always clear the Watchdog System Reset Flag (WDRF) and the WDE control bit in the initialization routine,
even if the Watchdog is not in use. - Page 52, Mega datasheet.

Now from Table 11.2 we can set the time out period to << 1s. That’s longer that a programming attempt takes and would result in a non communicative board. All it should take is some crap into bits WDP[3:0] and then switch on the Watchdog Timer Control Register. What do you think?

Just guessing. I might try to write come code that exploits this watchdog feature to see if I can lock up the processor on demand. Got to go to bed now though…

The bootloaders should contain code to correctly handle watchdog resets. This is not the case with some old bootloaders and the current bootloaders included with the IDE for the Nano and Pro Mini (I believe any that use the "ATmegaBOOT" bootloader). The optiboot bootloader (used for Uno) and the current version of the mega bootloader correctly handle watchdog resets. I believe old versions of the mega bootloader did not.

What is meant by "brick?"

What is meant by "brick?"

That's when you turn a working device into one that works like a brick (ie, does nothing.)

sthudium:
What is meant by "brick?"

See this explanation. I'm thinking that you might be able to randomly configure the watchdog timer to reset every 16ms so that you can't even reprogram it. Not sure though...

@pert

Yes. I found a blog entry by Simon Tushev saying exactly that. With great shame I have to admit that the Uno in question is a knock off, so I can't tell what's on it. Perhaps I've got the old version susceptible to a bad dog.