cmemcpymemory-alignment

Direct assignment crashes but memcpy() works?


It's a problem I ran into in my work. Our embedded platform is a customized Android OS running on quad-core Cortex-A7.

Background: I'm receiving a set of parameters from outside my process, and it's fed to me through a callback that looks something like this:

void some_callback(const void * pdata, size_t n)
{
    if (*(const uint8_t*)pdata == PARAM_FP32 && n >= 5)
    {
        pdata++;
        my_variable = *(const float *)pdata;       ///< Crashes the application
    }

    /// Other processing
}

Problem: When directly assigning the type-converted byte sequence to my variable (Note: I couldn't remember where this variable is stored - be it static? global? in stack? in heap?), application crashes.

I replaced the assignment with memcpy(), and it worked fine.

void some_callback(const void * pdata, size_t n)
{
    if (*(const uint8_t*)pdata == PARAM_FP32 && n >= 5)
    {
        pdata++;
        memcpy(&my_variable, pdata, sizeof(float));       ///< Worked fine
    }

    /// Other processing
}

I asked someone in the team and got the following reply: Direct assignment failed because of data alignment issues, the bytes to be assigned are (probably) not 4-byte aligned; assigning to a 4-byte aligned variable causes a bus error.

My questions:

  1. Is my colleague correct? Is that the actual reason why assignment crashes?
  2. Why am I not experiencing this issue in MCU development - like with STM32/AVR?
  3. Is there another way of doing the data copy correctly, other than using memcpy()?

Solution

  • There are multiple problems in the posted code:

    As for your questions:

    1. Is my colleague correct? Is that the actual reason why assignment crashes?

    Your colleague is correct, bad alignment is the most probably cause for the observed behavior.

    1. Why am I not experiencing this issue in MCU development - like with STM32/AVR?

    Because these other targets happen to accept reading float values from unaligned pointers (albeit with a small performance penalty). Note that some CPUs will not crash on unaligned pointers but will not read the proper float value either. Undefined behavior is not always easy to detect.

    1. Is there another way of doing the data copy correctly, other than using memcpy()?

    Using memcpy to copy the data to the destination will ensure proper operation in all cases. Don't worry about performance: an optimizing compiler will generate the appropriate instruction(s) to copy the 4 bytes regardless of alignment, without a function call.

    Here is a modified version:

    void some_callback(const void *pdata, size_t n)
    {
        const uint8_t *p = pdata;
        if (*p == PARAM_FP32 && n >= 1 + sizeof(float)) {
            float my_variable;
            memcpy(&my_variable, p + 1, sizeof(my_variable));
            /// Further processing using my_variable
        }
    
        /// Other processing
    }
    

    Please also note another potential problem: the byte stream pointed to by pdata contains a 4 byte sequence for a float value after a PARAM_FP32 byte. You assume that these 4 bytes are in the native order for your CPU, which might not be the case if the stream was produced on a different architecture. The endianness used in byte streams exchanged between systems must be specified explicitly to allow producers and consumers to adjust the order if their native endianness (little endian or big endian) differs from the one specified for the stream.