https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84468

            Bug ID: 84468
           Summary: [gcc 8] Inconsistent -Wstringop-truncation warnings
                    with -O2
           Product: gcc
           Version: 8.0.1
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: romain.geissler at amadeus dot com
  Target Milestone: ---

Hi,

I can see some strange behavior for the warning -W=stringop-truncation with -O2
and current master, depending on how I enclose my code in if/else blocks or
not, despite that logically speaking, the end result is always the same.

See this code:

EOF
#include <string.h>

class A 
{   
    public:
        A();
        A(const char* iCString);

        A& operator=(const A& iA);

    private:
        void setCString(const char* iCString);

        // Uncommenting the attribute silences the warning
        // but actually we do have a "string", not a "nonstring".
        /* [[gnu::nonstring]] */ char _cstring[3 + 1];
};      

A::A()
{   
    _cstring[0] = '\0';
}

A::A(const char* iCString)
{
    setCString(iCString);
}

A& A::operator=(const A& iA)
{
    if (this != &iA) // Commenting this line silences the warning.
    {
        setCString(iA._cstring);
    }

    return *this;
}

void A::setCString(const char* iCString)
{   
    // This version produces warning.
    if (iCString)
    {   
        strncpy(_cstring, iCString, 3);
    }

    _cstring[3] = '\0';

    /*
    // This version doesn't produce any warning, but has the same logic as
above.
    if (iCString)
    {   
        strncpy(_cstring, iCString, 3);
        _cstring[3] = '\0';
    }
    else
    {
        _cstring[3] = '\0';
    }
    */

    /*
    // This version also produces a warning, despite having the same logic as
above,
    // just with an extra assigment to _cstring[3] when iCString is not null.
    if (iCString)
    {
        strncpy(_cstring, iCString, 3);
        _cstring[3] = '\0';
    }
    _cstring[3] = '\0';
    */
}

When compiled with -Wall -Werror -Wextra -std=gnu++17 -O2 and current master. I
get:
error: ‘char* strncpy(char*, const char*, size_t)’ output may be truncated
copying 3 bytes from a string of length 3 [-Werror=stringop-truncation]

First what I find strange is the warning itself. gcc is clever enough to see
that source is 3 bytes long, that we copy in a buffer that is 4 bytes long, so
why would it complain about a truncation anyway ? Or maybe I misunderstood the
wording of the warning itself ?

Second, why are the different variant of A::setCString I commented in the code
working differently wrt the warning, while logically speaking they do have the
same behavior. If we need to write our if/else block in a given way to make gcc
understand it is safe, this starts to be complex for developers.

Final question: do you think it would somehow make sense to introduce a
"string" attribute, that would be the contrary of the newly introduced
"nonstring". "nonstring" means that maybe the string is not null terminate.
"string" would mean that for sure the array is always null terminated, and thus
doing things like strncpy(_cstring, anotherA._cstring, 4) (note that I used 4
and not 3 for the total size), would always work ? "nonstring" correctly
silences the warning, but has the exact opposite semantic meaning than I have
in this particular case.

Cheers,
Romain

Reply via email to