Search code examples
caliasstrict-aliasingpointer-aliasing

Are there any strict aliasing rule violations that are not detected by -Wstrict-aliasing?


I created a function that copies strings quickly by copying 8 bytes at a time, similar to the built-in memcpy. In the following function, the char pointer type is cast to an unsigned long long pointer type and referenced. However, I later wondered if this might violate the strict aliasing rule. I compiled it with the cc -O2 -Wstrict-aliasing command, and there were no warnings, and the program worked correctly. Isn't this a violation of the strict aliasing rule, or is it a violation of the strict aliasing rule that cannot be detected with the -Wstrict-aliasing option?

char    *my_strncpy(char *dst, char *src, size_t n) {
    char    *first_dst;
    char    *last_src;

    first_dst = dst;
    last_src = src + n - 1;
    while (src + 7 <= last_src)
    {
        *(unsigned long long*)dst = *(unsigned long long*)src;
        dst += 8;
        src += 8;
    }
    while (src <= last_src)
    {
        *dst = *src;
        dst++;
        src++;
    }
    *dst = '\0';
    return (first_dst);
}

int main(void)
{
    char    dst[100];
    char    src[100] = "012345678901234567890123456789";

    my_strncpy(dst, src, 20);
    printf("%s\n", dst);
    return (0);
}

Solution

  • Are there any strict aliasing rule violations that are not detected by -Wstrict-aliasing?

    Yes, of course. First, the documentation says there are:

    … The warning does not catch all cases,…

    Second, it is impossible for the compiler to detect cases it cannot see: If you define an object as long in one translation unit and use it as double in another translation unit, the compiler cannot see when compiling the latter that the type is wrong.

    In the following function, the char pointer type is cast to an unsigned long long pointer type and referenced.

    The char pointer type is irrelevant. What matters is the effective type of the memory it points to. The aliasing rules are not defined in terms of the types of pointers used prior to the last pointer type. (The last pointer type matters because, when you apply * or […] to dereference it, it sets the lvalue type, which is part of the aliasing rules.)

    If the memory was originally defined with some type (versus dynamically allocated with malloc), then that type is its effective type, and the fact it was passed via char * does not change how it may or may not be aliased.

    However, I later wondered if this might violate the strict aliasing rule.

    Yes, it does, unless the char * points to memory with an effective type that may be aliased by unsigned long long.