strcpy()/strncpy() crashes on structure member with extra space when optimization is turned on on Unix?

前端 未结 6 1526
再見小時候
再見小時候 2021-02-06 23:34

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

6条回答
  •  南方客
    南方客 (楼主)
    2021-02-07 00:05

    This is all because of -D_FORTIFY_SOURCE=2 intentionally crashing on what it decides is unsafe.

    Some distros build gcc with -D_FORTIFY_SOURCE=2 enabled by default. Some don't. This explains all the differences between different compilers. Probably the ones that don't crash normally will if you build your code with -O3 -D_FORTIFY_SOURCE=2.

    Why does it fail only if optimization is on?

    _FORTIFY_SOURCE requires compiling with optimization (-O) to keep track of object sizes through pointer casts / assignments. See the slides from this talk for more about _FORTIFY_SOURCE.

    Why does strcpy() fail? How can it?

    gcc calls __memcpy_chk for strcpy only with -D_FORTIFY_SOURCE=2. It passes 8 as the size of the target object, because that's what it thinks you mean / what it can figure out from the source code you gave it. Same deal for strncpy calling __strncpy_chk.

    __memcpy_chk aborts on purpose. _FORTIFY_SOURCE may be going beyond things that are UB in C and disallowing things that look potentially dangerous. This gives it license to decide that your code is unsafe. (As others have pointed out, a flexible array member as the last member of your struct, and/or a union with a flexible-array member, is how you should express what you're doing in C.)


    gcc even warns that the check will always fail:

    In function 'strcpy',
        inlined from 'main' at :18:9:
    /usr/include/x86_64-linux-gnu/bits/string3.h:110:10: warning: call to __builtin___memcpy_chk will always overflow destination buffer
       return __builtin___strcpy_chk (__dest, __src, __bos (__dest));
              ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    

    (from gcc7.2 -O3 -Wall on the Godbolt compiler explorer).


    Why doesn't memcpy() fail regardless of -O level?

    IDK.

    gcc fully inlines it just an 8B load/store + a 1B load/store. (Seems like a missed optimization; it should know that malloc didn't modify it on the stack, so it could just store it from immediates again instead of reloading. (Or better keep the 8B value in a register.)

提交回复
热议问题