Search code examples
carmembeddedstm32cortex-m

STM32 call to memcpy causes hardfault (the call to memcpy itself, not the execution of memcpy)


Situation: I am working with a crypto library called embedded disco, I have a demo working on my PC but when porting it over to the MCU I get a hard fault when executing a library procedure. In the faulting code, the library is trying to simply copy the content of one strobe_s struct into another strobe_s. This is done twice: once for s1 and once for s2. For s1, the library simply assigns the dest. struct to the source struct. For s2 however, such an assign gave a hard fault. As the Cortex-M ISA requires aligned memory accesses, I reckoned that replacing the assignment with a memcpy should fix the problem. Nevertheless, simply stepping into memcpy using the debugger results in a hard fault! I.e. I have a breakpoint at the line with the memcpy and when stepping inside the fault handler is called! I have used memcpy to fix misaligned memory accesses in other parts of the code just fine...

MCU: STM32L552ZET6QU

Faulting code:

The code below is my modification of the original library code where the assignment to *s2 was replaced by a memcpy. The original code from the library's github was:

  // s1 = our current strobe state
  *s1 = ss->strobe;
  if (!half_duplex) {
    // s2 = s1
    *s2 = ss->strobe;
  }

My modified version:

  // s1 = our current strobe state
  *s1 = ss->strobe;
  if (!half_duplex) {
    // s2 = s1
    // WARNING: The below code will give a HARD FAULT ON THE STM32L552ZE!
    // *s2 = ss->strobe;
    // Fix I tried: Use memcpy instead!
    memcpy((void*) s2, (void*)(&(ss -> strobe)), sizeof(strobe_s));
  }

Parameter values of memcpy:

Right before the execution of memcpy, the debugger shows me the following values for the variables in question:

Expr.   Type                Value
----------------------------------------------------
s1      strobe_s *          0x800c374   
s2      strobe_s *          0x800a497 <_fflush_r+66>    
ss      symmetricState *    0x2002f988  
&s1     strobe_s **         0x2002f690  
&s2     strobe_s **         0x2002f68c  
&ss     symmetricState **   0x2002f694

Typedefs:

typedef struct symmetricState_ {
  strobe_s strobe;
  bool isKeyed;
} symmetricState;

/** Keccak's domain: 25 words of size b/25, or b/8 bytes. */
typedef union {
  kword_t w[25];
  uint8_t b[25 * sizeof(kword_t) / sizeof(uint8_t)];
} kdomain_s;

/** The main strobe state object. */
typedef struct strobe_s_ {
  kdomain_s state;
  uint8_t position;
  uint8_t pos_begin;
  uint8_t flags;
  uint8_t initiator;
  uint8_t initialized;  // strobe is initialized if this value is set to 111.
                        // This is because we cannot assume that a boolean would
                        // be set to false initially (C stuff). A uint8_t is a
                        // short value but here we do not care about security
                        // much, rather catching bugs early in a development
                        // environement.
} strobe_s;

Questions:

  1. How is it possible that just the call to memcpy without actually executing a single instruction within memcpy gives a hard fault?
  2. How can I fix this?

Solution

  • Here:

    s2      strobe_s *          0x800a497 <_fflush_r+66>   
    

    s2is a flash (read-only) address. Copying to read-only memory is both semantically erroneous and may trigger an MPU fault if the region were set to read-only.

    It is not clear to me how the original code worked or indeed how:

     *s1 = ss->strobe;
    

    is not causing a problem too however. Certainly it won't work as intended even if there were no exception.