This is the mail archive of the gcc-bugs@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[Bug middle-end/77696] Confusing wording for -Wformat-length=


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

Martin Sebor <msebor at gcc dot gnu.org> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|UNCONFIRMED                 |NEW
   Last reconfirmed|                            |2016-09-23
           Assignee|unassigned at gcc dot gnu.org      |msebor at gcc dot gnu.org
     Ever confirmed|0                           |1
           Severity|normal                      |enhancement

--- Comment #1 from Martin Sebor <msebor at gcc dot gnu.org> ---
Thanks for the feedback.  I took the liberty to change the classification of
this bug to enhancement.  If you feel like it should be a defect instead please
change it back.

The message

  "writing format character ‘!’ at offset 3 past the end of the destination"

says that the '!' is three bytes from the beginning of the format string, and
the function (whose name should probably be mentioned) is writing it somewhere
past the end of the destination sequence (often but not necessarily always
exactly just past the end).  Mentioning the offset is important in case there
are multiple exclamation points in the format string.  Note that the wording is
meant to be similar or analogous to:

  "writing a terminating nul past the end of the destination"

so it were to change so I think should the other.


When considering changes here I think it would be useful to take a look at all
the warnings issued by the pass and the convention they follow.  The pass
checks two sets of functions and issues two broad classes of warnings:

1) bounded functions (like snprintf) along with truncation warnings, and
2) unbounded functions (line sprintf) along with "writing past the end"
warnings.

For individual directives, the pass also distinguishes two general situations,
and uses two kinds of wordings in the warnings to help users tell them apart:

1) a definite problem denoted by the words "truncated writing X bytes" or
"writing X bytes into a region" of a given size, and
2) possible problem indicated by the phrase "may be truncated writing between X
and Y bytes into a region" or "writing between X and Y bytes into a region" of
a given size.

Finally, similarly to individual directives but for format string characters
that aren't part of a directive, the pass again distinguishes two general
situations, and emits two kinds of wordings in the warnings to tell one from
the other

1) a definite premature truncation or buffer overflow: "output truncated before
the last character" and "writing a character/nul past the end," and
2) a possible premature truncation or buffer overflow: "output may be truncated
before the last character" and "may write a character/nul past the end."

With this background, it might also be helpful to look at some examples.  I
quickly put together the test program below.  It doesn't cover all the possible
permutations (and the output may change based on the warning level and based on
optimization), but it should be a starting point for a more comprehensive
survey.  With a more complete picture we should be able to make a more informed
decision about the new wording of all the kinds of diagnostic (it could also
help find bugs or inconsistencies in the implementation).

$ cat zzz.c && /build/gcc-trunk-svn/gcc/xgcc -B /build/gcc-trunk-svn/gcc -O2 -S
-Wall -Wformat -Wextra -Wpedantic -Wformat-length=2 zzz.c
char d[4];

typedef __SIZE_TYPE__ size_t;

extern int sprintf (char*, const char*, ...);
extern int snprintf (char*, size_t, const char*, ...);


void f (int i)
{
  // bounded, definite truncation in a directve
  snprintf (d, sizeof d, "%i", 1235);

  // bounded, definite truncation copying format string
  snprintf (d, sizeof d, "%iAB", 123);

  // unbounded, definite overflow in a directve
  sprintf (d, "%i", 1235);

  // unbounded, definite overflow copying format string
  sprintf (d, "%iAB", 123);

  // bounded, possible truncation a directve
  snprintf (d, sizeof d, "%i", i);

  // bounded, possible overflow copying format string
  snprintf (d, sizeof d, "%iAB", i);

  // unbounded, possible overflow in a directve
  sprintf (d, "%i", i);

  // unbounded, possible overflow copying format string
  sprintf (d, "%iAB", 123);

  // unbounded, possible overflow copying format string
  const char *s = i ? "123" : "1234";
  sprintf (d, "%sAB", s);
}

zzz.c: In function ‘f’:
zzz.c:12:26: warning: output truncated before the last format character
[-Wformat-length=]
   snprintf (d, sizeof d, "%i", 1235);
                          ^~~~
zzz.c:12:3: note: format output 5 bytes into a destination of size 4
   snprintf (d, sizeof d, "%i", 1235);
   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
zzz.c:15:30: warning: output truncated at format character ‘B’ at offset 3
[-Wformat-length=]
   snprintf (d, sizeof d, "%iAB", 123);
                              ^
zzz.c:15:3: note: format output 6 bytes into a destination of size 4
   snprintf (d, sizeof d, "%iAB", 123);
   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
zzz.c:18:15: warning: writing a terminating nul past the end of the destination
[-Wformat-length=]
   sprintf (d, "%i", 1235);
               ^~~~
zzz.c:18:3: note: format output 5 bytes into a destination of size 4
   sprintf (d, "%i", 1235);
   ^~~~~~~~~~~~~~~~~~~~~~~
zzz.c:21:19: warning: writing format character ‘B’ at offset 3 past the end of
the destination [-Wformat-length=]
   sprintf (d, "%iAB", 123);
                   ^
zzz.c:21:3: note: format output 6 bytes into a destination of size 4
   sprintf (d, "%iAB", 123);
   ^~~~~~~~~~~~~~~~~~~~~~~~
zzz.c:24:27: warning: ‘%i’ directive output may be truncated writing between 1
and 11 bytes into a region of size 4 [-Wformat-length=]
   snprintf (d, sizeof d, "%i", i);
                           ^~
zzz.c:24:26: note: using the range [‘1’, ‘-2147483648’] for directive argument
   snprintf (d, sizeof d, "%i", i);
                          ^~~~
zzz.c:24:3: note: format output between 2 and 12 bytes into a destination of
size 4
   snprintf (d, sizeof d, "%i", i);
   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
zzz.c:27:27: warning: ‘%i’ directive output may be truncated writing between 1
and 11 bytes into a region of size 4 [-Wformat-length=]
   snprintf (d, sizeof d, "%iAB", i);
                           ^~
zzz.c:27:26: note: using the range [‘1’, ‘-2147483648’] for directive argument
   snprintf (d, sizeof d, "%iAB", i);
                          ^~~~~~
zzz.c:27:3: note: format output between 4 and 14 bytes into a destination of
size 4
   snprintf (d, sizeof d, "%iAB", i);
   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
zzz.c:30:16: warning: ‘%i’ directive writing between 1 and 11 bytes into a
region of size 4 [-Wformat-length=]
   sprintf (d, "%i", i);
                ^~
zzz.c:30:15: note: using the range [‘1’, ‘-2147483648’] for directive argument
   sprintf (d, "%i", i);
               ^~~~
zzz.c:30:3: note: format output between 2 and 12 bytes into a destination of
size 4
   sprintf (d, "%i", i);
   ^~~~~~~~~~~~~~~~~~~~
zzz.c:33:19: warning: writing format character ‘B’ at offset 3 past the end of
the destination [-Wformat-length=]
   sprintf (d, "%iAB", 123);
                   ^
zzz.c:33:3: note: format output 6 bytes into a destination of size 4
   sprintf (d, "%iAB", 123);
   ^~~~~~~~~~~~~~~~~~~~~~~~
zzz.c:37:19: warning: may write format character ‘B’ at offset 3 past the end
of the destination [-Wformat-length=]
   sprintf (d, "%sAB", s);
                   ^
zzz.c:37:3: note: format output between 6 and 7 bytes into a destination of
size 4
   sprintf (d, "%sAB", s);
   ^~~~~~~~~~~~~~~~~~~~~~

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]