Bug 77690 - -Wformat-length %s false positive after strlen check
Summary: -Wformat-length %s false positive after strlen check
Status: RESOLVED INVALID
Alias: None
Product: gcc
Classification: Unclassified
Component: c (show other bugs)
Version: 7.0
: P3 normal
Target Milestone: ---
Assignee: Martin Sebor
URL:
Keywords: diagnostic
Depends on:
Blocks:
 
Reported: 2016-09-22 11:03 UTC by nsz
Modified: 2018-05-17 10:58 UTC (History)
1 user (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail: 7.0
Last reconfirmed: 2016-09-23 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description nsz 2016-09-22 11:03:16 UTC
there is no obvious way to silence the sprintf %s warning.
(this currently breaks the gdb build)

$ cat a.c
int sprintf (char*, const char*, ...);

struct { char buf[12]; } s;

void f(void)
{
  char foo[7];
  if (__builtin_strlen(s.buf) > 3) // this check does not help
    return;
  sprintf(foo, "zz%s", s.buf);
}

$ gcc -Wall -c a.c
a.c: In function 'f':
a.c:10:19: warning: '%s' directive writing between 0 and 11 bytes into a region of size 5 [-Wformat-length=]
   sprintf(foo, "zz%s", s.buf);
                   ^~   ~
a.c:10:3: note: format output between 3 and 14 bytes into a destination of size 7
   sprintf(foo, "zz%s", s.buf);
   ^~~~~~~~~~~~~~~~~~~~~~~~~~~
Comment 1 Martin Sebor 2016-09-23 03:02:03 UTC
Unfortunately, even though it seems like the if statement with the strlen call should help, the length range information isn't available to the warning pass.  The warning can, however, be avoided by constraining the length of the string argument output by specifying the precision in the directive like so:

  sprintf(foo, "zz%.4s", s.buf);

Please let me know if this isn't sufficient to resolve the problem report.
Comment 2 nsz 2016-09-23 10:54:02 UTC
(In reply to Martin Sebor from comment #1)
>   sprintf(foo, "zz%.4s", s.buf);
> 
> Please let me know if this isn't sufficient to resolve the problem report.

in my case truncation is fatal error so using precision is not useful (other than suppressing the warning) and has a runtime cost (extra arg passing for %.*s).
Comment 3 nsz 2016-09-23 12:45:48 UTC
(In reply to nsz from comment #2)
> in my case truncation is fatal error so using precision is not useful (other
> than suppressing the warning) and has a runtime cost (extra arg passing for
> %.*s).

nevermind, i'll just use snprintf.
Comment 4 Martin Sebor 2016-09-23 15:32:14 UTC
Note that with snprintf GCC issues

  warning: ‘%s’ directive output may be truncated writing between 1 and 11 bytes into a region of size 5

This is also by design though I'm on the fence about warning at the same level or under the same option as for sprintf.

FWIW, I wouldn't expect the parsing of the precision within the format string to have a noticeable performance impact on the overall cost of the call to the function.  If you prefer to avoid it, you can specify via an argument to the '*':

  sprintf(foo, "zz%.*s", 4, s.buf);

I'm changing the resolution of this bug to invalid since we haven't actually fixed anything, but if none of the solutions above is sufficient please feel free to reopen the bug and let us know what would be (keeping in mind that the ideal solution of tracking the actual string length or its range or determining it from the strlen call may not be feasible in the near term).
Comment 5 nsz 2016-09-23 15:35:43 UTC
(In reply to Martin Sebor from comment #4)
> Note that with snprintf GCC issues
> 
>   warning: ‘%s’ directive output may be truncated writing between 1 and 11
> bytes into a region of size 5
> 
> This is also by design though I'm on the fence about warning at the same
> level or under the same option as for sprintf.
> 

i opened another bug about snprintf
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=77708

i think warning for snprintf is almost surely a false positive, that is hard to suppress (and the suppression code is not idiomatic).