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

            Bug ID: 88780
           Summary: Wstringop-truncation
           Product: gcc
           Version: 9.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: middle-end
          Assignee: unassigned at gcc dot gnu.org
          Reporter: msebor at gcc dot gnu.org
  Target Milestone: ---

In the test case below the assignment suppression logic where we look for the
next statement to see if it assigns a nul to the destination is never entered,
resulting in a false positive.  I don't know/remember why it isn't used here,
but using it doesn't seem that it would do the right thing here anyway: it
would find the addition that computes pointer to use for the assignment:

  _3 = buf_8(D) + namelen_6;

The logic isn't robust enough to track pointer arithmetic when looking for the
assignment.  To avoid the false positive the logic needs to be enhanced to
track the destination across pointer arithmetic.

$ cat u.c && gcc -O2 -S -Wall u.c
typedef __SIZE_TYPE__ size_t;

void f (char *d, const char *s, size_t n)
{
  size_t len = __builtin_strlen (s);
  if (n < len + 1) return;

  __builtin_strncpy (d, s, len);
  d[len] = '\0';
}

u.c: In function ‘f’:
u.c:3:6: note: finish_function
    3 | void f (char *d, const char *s, size_t n)
      |      ^
u.c:8:3: warning: ‘__builtin_strncpy’ output truncated before terminating nul
copying as many bytes from a string as its length [-Wstringop-truncation]
    8 |   __builtin_strncpy (d, s, len);
      |   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
u.c:5:16: note: length computed here
    5 |   size_t len = __builtin_strlen (s);
      |                ^~~~~~~~~~~~~~~~~~~~


The test case was isolated from the following function:

/* Extract the common name of 'cert' into 'buf'. */
static int get_common_name(CERTCertificate *cert, char *buf, size_t bufsiz)
{
     /* FIXME --- truncating names with spaces */
     size_t namelen;
     char *name = CERT_GetCommonName(&cert->subject);

     if (!name) return -1;

     namelen = strlen(name);
     if (bufsiz < namelen+1) return -1;

     strncpy(buf, name, namelen);
     buf[namelen] = '\0';
     PORT_Free(name);

     return 0;
} 

and the following warning during a Fedora build with GCC 9:

In file included from /usr/include/string.h:494,
                 from /usr/include/nss3/secport.h:45,
                 from /usr/include/nss3/seccomon.h:27,
                 from /usr/include/nss3/nss.h:34,
                 from certwatch.c:77:
In function 'strncpy',
     inlined from 'get_common_name' at certwatch.c:249:5,
     inlined from 'check_cert' at certwatch.c:289:9,
     inlined from 'main' at certwatch.c:387:12:
/usr/include/bits/string_fortified.h:106:10: error: '__builtin___strncpy_chk'
output truncated before terminating nul copying as many bytes from a string as
its length [-Werror=stringop-truncation]
   106 |   return __builtin___strncpy_chk (__dest, __src, __len, __bos
(__dest));
       |         
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
certwatch.c: In function 'main':
certwatch.c:246:15: note: length computed here
   246 |     namelen = strlen(name);
       |               ^~~~~~~~~~~~
cc1: all warnings being treated as errors

Reply via email to