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); ^~~~~~~~~~~~~~~~~~~~~~