Home > Software engineering >  Is it safe to access a struct using pointer pointed to unaligned memory address in C/C ?
Is it safe to access a struct using pointer pointed to unaligned memory address in C/C ?

Time:12-31

struct CustomData
{
    char flag;
    int count;
    double value;
};

CustomData custom_data{};

char *buf = new char[sizeof(CustomData)   3];
memcpy(buf   3, &custom_data, sizeof(CustomData));

CustomData* ptr = (CustomData *)(buf   3);

ptr->count = 10;
ptr->value = 20.0;

I have a memory buffer composed with some header bytes and a struct. The struct is memcpy to this memory buffer. I have to modify some fields in this embeded struct, as demostrated above.

My questions are:

  1. Is this a safe access on x86-64? How about other platforms?

  2. memcpy to a local struct, modify it and copy it back is surely OK, but it seems wasteful. Is there a way to check if ptr is properly aligned for safe-access to the struct?

CodePudding user response:

Neither the C nor the C standards define the behavior of accessing memory without the alignment required for the lvalue type, nor even of converting a pointer to another pointer type when the pointer value does not have the alignment required for the destination type. The question is not whether your target architecture (x86-64 or other) supports misaligned access but whether your C or C implementation, particularly the compiler, supports it.

Looking at your sample code, I suspect it is a proxy for copying data into a buffer from a network or file or other source, then attempting to interpret part of that data as a desired type, possibly after inspecting a header in the received data to determine the type and/or the location of the subject data. If so, you should describe the original situation, because the proxy code you showed is insufficient.

When reading raw data as bytes from a network or file, it is preferable to read the data directly into the desired structure. Ideally, the communication protocol would be specified to align the data as needed so that no further manipulation is needed once the data is received into an aligned buffer. In C, unions may help deal with issues of aliasing one type of data as another.

Failing the direct-into-structure method, memcpy may be used, and it may not be as inefficient as you think because the memcpy method results in code with well-defined behavior and then the compiler often can, given sufficient visibility to the relevant parts of the code, optimize the assembly it generates so that no actual memcpy occurs.

Failing that, some compilers have various extensions to the C and C standards that let you define structure types without alignment requirements and to access memory using arbitrary types (supporting aliasing character arrays as other types).

In C at least, dynamically allocated memory can be filled with data using a character type and then accessed as another type following rules regarding “effective types.” However, certain rules about aliasing must be followed, and this does not enable accessing incorrectly aligned data (absent use of the extensions mentioned above).

CodePudding user response:

Any pointer punning is dangerous as it possible break the strict-aliasing rules.

You need to use memcpy. Most optimizing compilers know memcpy very well and usually do not emit the actual call to the memcpy:

typedef struct CustomData
{
    char flag;
    int count;
    double value;
}CustomData;


CustomData *foo(int val, double d)
{
    char *buf = new char[sizeof(CustomData)   3];

    CustomData cd;

    cd.count = val;
    cd.value = d;

    memcpy(buf   3, &cd, sizeof(cd));
    return (CustomData *)buf;
}
foo(int, double):
        push    rbp
        mov     ebp, edi
        mov     edi, 19
        push    rbx
        sal     rbp, 32
        movq    rbx, xmm0
        sub     rsp, 8
        call    operator new[](unsigned long)
        mov     QWORD PTR [rax 3], rbp
        mov     QWORD PTR [rax 11], rbx
        add     rsp, 8
        pop     rbx
        pop     rbp
        ret

CodePudding user response:

Some OS have memory alignment requirements, if I remember correctly Solaris has a requirement that memory be aligned on 8 byte boundaries. The OS will throw an error if this requirement isn't met

  • Related