When writing a project, I ran into a strange issue.
This is the minimal code I managed to write to recreate the issue. I am intentionally storing an actual string in the place of something else, with enough space allocated.
// #include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stddef.h> // For offsetof()
typedef struct _pack{
    // The type of `c` doesn't matter as long as it's inside of a struct.
    int64_t c;
} pack;
int main(){
    pack *p;
    char str[9] = "aaaaaaaa"; // Input
    size_t len = offsetof(pack, c) + (strlen(str) + 1);
    p = malloc(len);
    // Version 1: crash
        strcpy((char*)&(p->c), str);
    // Version 2: crash
        strncpy((char*)&(p->c), str, strlen(str)+1);
    // Version 3: works!
        memcpy((char*)&(p->c), str, strlen(str)+1);
    // puts((char*)&(p->c));
    free(p);
  return 0;
}
The above code is confusing me:
- With gcc/clang -O0, bothstrcpy()andmemcpy()works on Linux/WSL, and theputs()below gives whatever I entered.
- With clang -O0on OSX, the code crashes withstrcpy().
- With gcc/clang -O2or-O3on Ubuntu/Fedora/WSL, the code crashes (!!) atstrcpy(), whilememcpy()works well.
- With gcc.exeon Windows, the code works well whatever the optimization level is.
Also I found some other traits of the code:
- (It looks like) the minimum input to reproduce the crash is 9 bytes (including zero terminator), or - 1+sizeof(p->c). With that length (or longer) a crash is guaranteed (Dear me ...).
- Even if I allocate extra space (up to 1MB) in - malloc(), it doesn't help. The above behaviors don't change at all.
- strncpy()behaves exactly the same, even with the correct length supplied to its 3rd argument.
- The pointer does not seem to matter. If structure member - char *cis changed into- long long c(or- int64_t), the behavior remains the same. (Update: changed already).
- The crash message doesn't look regular. A lot of extra info is given along. 
I tried all these compilers and they made no difference:
- GCC 5.4.0 (Ubuntu/Fedora/OS X/WSL, all are 64-bit)
- GCC 6.3.0 (Ubuntu only)
- GCC 7.2.0 (Android, norepro???) (This is the GCC from C4droid)
- Clang 5.0.0 (Ubuntu/OS X)
- MinGW GCC 6.3.0 (Windows 7/10, both x64)
Additionally, this custom string copy function, which looks exactly like the standard one, works well with any compiler configuration mentioned above:
char* my_strcpy(char *d, const char* s){
    char *r = d;
    while (*s){
        *(d++) = *(s++);
    }
    *d = '\0';
    return r;
}
Questions:
- Why does strcpy()fail? How can it?
- Why does it fail only if optimization is on?
- Why doesn't memcpy()fail regardless of-Olevel??
*If you want to discuss about struct member access violation, pleast head over here.
Part of objdump -d's output of a crashing executable (on WSL):
P.S. Initially I want to write a structure, the last item of which is a pointer to a dynamically allocated space (for a string). When I write the struct to file, I can't write the pointer. I must write the actual string. So I came up with this solution: force store a string in the place of a pointer.
Also please don't complain about gets(). I don't use it in my project, but the example code above only.


 
     
     
     
     
     
     
    