Embedded Institute
Subscribe to deep dives

Stack corruption, and not a single bug in your code: Cortex-M3 erratum 752419

cortex m3erratastackassemblyrace condition

Most stack bugs start in C. This one starts in assembly, in the one place where code loads the stack pointer straight from memory: a stack switch, a context switch, a startup path.

The erratum

Erratum 752419 (Category B, present in r1p1, r2p0 and r2p1) applies to the Cortex-M3 and the Cortex-M3 with ETM. If an interrupt is taken during the data phase of a single word load into SP (R13), the load is executed one extra time when the interrupt returns.

Three conditions are needed, all at once:

  1. A load is executed with SP as the destination.
  2. Its address has been issued to the memory system.
  3. An interrupt is taken before the data is returned and written to SP.

What goes wrong

The five affected forms are the single word LDR into SP with a post-indexed immediate, a pre-indexed immediate with write-back, an immediate offset, a plain base register, and a register offset.

For the plain forms the effect is only a repeated read. Arm says there should be no implications unless the target is Device or Strongly-Ordered memory, where a second read can return a different value and leave SP different from a single load.

The two write-back forms, post-indexed and pre-indexed with write-back, also update the base register on each execution. After the repeat, the base register is wrong, and SP is loaded from an incorrect address. Arm's description of the result is apparent stack corruption and unintended modification of memory.

The catch: a race against the load

This is a race condition between an interrupt and the data phase of your load. The interrupt has to arrive between the address being issued and the data returning. My inference, not Arm's text: that makes the window narrow, so the failure is rare, will not reproduce on demand, and will look like a random stack overflow.

What to do

Search your assembly for loads whose destination is SP: startup code, stack-switching routines, your RTOS port and any vendor library assembly.

Arm's workaround covers both problems: replace the direct load with a load into a general-purpose register, followed by a move to SP. If a repeated read is acceptable, the base-update problem can instead be avoided by loading SP without the base increment and updating the base register with a separate add or subtract.

This applies only to the revisions listed above. Check which core revision your part uses.

In production, details matter: always verify behaviors against the official documentation for your specific part, core stepping, and toolchain. Silicon integrations vary widely.

Source: Arm Cortex-M3 (AT420) and Cortex-M3 with ETM (AT425) Software Developers Errata Notice, ARM-ECM-0426803 version 4, erratum 752419. Check the current revision of the notice on Arm's site.

Share on LinkedIn

Critical failures, root-cause analyses, and quarterly curriculum drops.

Deep dives into real embedded failures, traced from symptom to cause, plus first notice when new modules open for enrollment.

Every email has an unsubscribe link.