Search code examples
cpointersmemory-managementvariable-assignment

Why after executing function, the address of my pointer change


Here is my code. I want to copy data from a memory region to other memory region. To avoid overlap data i make the order copy, not from the lsb , but from msb of the array like the picture order of copy

#include<stdio.h>
#include<stdint.h>
void MemCopy2(uint8_t * Src, uint8_t * Dest, uint32_t Len)
{   
    uint32_t count;

    for (count = Len; count > 0; count--)
    {
        Dest[count-1] = Src[count-1];
    }

}

int main()
{

    uint32_t A[2] = { 0xABCD1234, 0x567890AB };
    uint32_t * Ptr = (uint16_t*)&A + 1;  // CD 
    
    printf("%d \n", Ptr);
    
    MemCopy2((uint8_t*)&A, (uint8_t*)Ptr, sizeof(A));

    printf("%d \n", Ptr);

    return 0;
}

I printf before and after executing the function MemCopy2, the result is

6487570 
6444664 

which means that the value of pointer Ptr has been changed

Still this code, if I add a local variable like int i=6; before initialize array A[2] in main() , now the result is

6487554 
6487554 

which means that the value of pointer Ptr remain, Anyone know why this happen I think it because of stacking and unstacking to store local variable in register, something like that.


Solution

  • Here is the minimal example that includes the missing headers, use %p to print addresses, and resolve warnings:

    #include <stdint.h>
    #include <stdio.h>
    
    void MemCopy2(const uint8_t *Src, uint8_t *Dest, uint32_t Len) {
        for (uint32_t count = Len; count > 0; count--)
            Dest[count-1] = Src[count-1];
    
    }
    
    int main(void) {
        uint32_t A[2] = { 0xABCD1234, 0x567890AB };
        uint32_t *Ptr = (uint32_t *) ((uint16_t *) &A + 1);
        printf("%p\n", Ptr);
        MemCopy2((uint8_t*)&A, (uint8_t*)Ptr, sizeof(A));
        printf("%p\n", Ptr);
        return 0;
    }
    

    and the output is:

    0x7ffeaf38ae82
    0x7ffeaf385678
    

    The problem is that you copy sizeof(A) bytes into a non-zero offset of A which will cause a buffer overflow. As the offset 1 is sizeof(uint16_t) * 1 i.e. 2 bytes, but you only trail by 1 byte in MemCopy2(). This is undefined behavior and our systems it happens to overwrite Ptr. The technical fix is to 2 byte less, i.e.:

    Accessing your array uin32_t A[] via a uint16_t * is undefined behavior per 6.3.2.3p7. On some platforms it will cause a bus violation and crash the program. On others like amd64 it's a performance overhead.

    #include <stdint.h>
    #include <stdio.h>
    
    #define OFFSET 1
    
    void MemCopy2(const uint8_t * Src, uint8_t * Dest, uint32_t Len) {
        for (uint32_t count = Len; count > 0; count--)
            Dest[count-OFFSET] = Src[count-OFFSET];
    
    }
    
    int main(void) {
        uint32_t A[2] = { 0xABCD1234, 0x567890AB };
        uint32_t *Ptr = (uint32_t *) ((char *) A + sizeof(uint16_t) * OFFSET);
        printf("%p\n", Ptr);
        MemCopy2((uint8_t*)&A, (uint8_t*)Ptr, sizeof(A) - sizeof(uint16_t) * OFFSET);
        printf("%p\n", Ptr);
        return 0;
    }
    

    The interface is clunky, though, maybe move the offset business into MemCopy2() to avoid coupling of OFFSET in both MemCopy2() and main()?