2
\$\begingroup\$

I have recently been working on what I believe is a buffer overrun problem in a microcontroller with software that I wrote. I'm using an Atmega328PB, so it has no such thing as a MMU or address space isolation.

Every once in a while, some bug is causing many of my in memory values to get overwritten with a bunch of garbage. I have enabled the watchdog timer and it seems that what usually happens is a total crash of the microcontroller and the watchdog takes over.

However, what seems to have happened sometimes is it overwrites a bunch of memory values and then happens to set the flag byte I have internally for "write this to the eeprom". The microcontroller keeps running happily with these "new" configuration values. Even a power cycle won't allow this to be fixed, since the "new" values are read from EEPROM.

Is there some way I can protect my microcontroller against accidental buffer overrun and underruns? I have had a few ideas such as

  1. Add a sentinel byte that should always be some number. Check this all over the place and if the number is wrong, just go into an infinite loop. The watchdog resets eventually.
  2. Store a checksum of everything that is kept in global memory. There is practically no way random memory corruption would update the checksum to be correct. This has severe performance implications as every write to a global implies updating this checksum. The checksum needs to be validated periodically
  3. Store each value with its own checksum. Each read needs to validate the checksum.

Option 1 is simple but likely not to actually work for all cases. Options 2 & 3 are likely to work but have severe performance & memory implications I think.

Is there an industry standard way to protect a microcontroller from memory corruption in the absence of an MMU?

Clarification:

None of this has anything to do with dynamic allocation or null terminated arrays. All the C-strings I use are stored in the program memory, thus corruption of them would involve and extremely unlikely set of circumstances. Almost every single variable is at a static scope or is globally shared across the program. I don't use the dynamic heap at all. I can't prove that stack corruption is occurring, but I know that my global values are being corrupted.

\$\endgroup\$

closed as too broad by Chris Stratton, Elliot Alderson, RoyC, Sparky256, StainlessSteelRat Aug 3 at 21:34

Please edit the question to limit it to a specific problem with enough detail to identify an adequate answer. Avoid asking multiple distinct questions at once. See the How to Ask page for help clarifying this question. If this question can be reworded to fit the rules in the help center, please edit the question.

  • 2
    \$\begingroup\$ Generally you can a) write a sound program b) use hardware with protection features or c) use a runtime interpreter with protection features (aka managed code) and hope the engine doesn't have its own bugs or the programmer doesn't misunderstand what protections do and do not exist. Do note that hardware protection is limited in its ability to protect a program from its own errors. \$\endgroup\$ – Chris Stratton Jul 10 at 18:04
  • 5
    \$\begingroup\$ Typically for an ATmega-type application what you need to do is understand the failures and fix their cause. \$\endgroup\$ – Chris Stratton Jul 10 at 18:07
  • 2
    \$\begingroup\$ @laptop2d that's a pretty fundamental and dangerous misunderstanding of what a buffer overrun is and how it can happen. The actual concerns unique to dynamic memory are things like fragmentation and dereferencing free'd or otherwise invalid pointers. \$\endgroup\$ – Chris Stratton Jul 10 at 18:23
  • 3
    \$\begingroup\$ @laptop2d sadly not that simple, arrays, pointers, casting types can all do it. Most likely here is writing out of bounds on an array or then just running out of stack space which can happen with 2048 bytes of memory to work with. \$\endgroup\$ – r_ahlskog Jul 10 at 18:25
  • 2
    \$\begingroup\$ Your title says protecting but you talk about methods for detecting overruns...kind of like "detecting" that you have driven off a steep cliff. \$\endgroup\$ – Elliot Alderson Jul 10 at 21:07
3
\$\begingroup\$

"Canary bytes" are a known thing. Write an easily recognizable byte pattern between data structures, and if those bytes ever change, then something has gone wrong. But it will never tell you when it went wrong or why. You just know that you have buggy code.

Much better is to write the code so that it doesn't cause buffer overflows in the first place. Check all your buffers are big enough. When reading bytes from elsewhere, only accept as many bytes as you can handle, especially if something else is telling you how many bytes to expect. Look carefully at data concatenation, usually string concatenation. Is the destination buffer always big enough to hold the result?

Stack overflow is another thing that can cause unpredictable effects. On small systems, it's likely to be either too many nested function calls, or else too many, or too large, local variables within the functions.

\$\endgroup\$

Not the answer you're looking for? Browse other questions tagged or ask your own question.