[Bug c/89161] Bogus -Wformat-overflow warning with value range known
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=89161 Martin Sebor changed: What|Removed |Added Known to fail|10.0|10.3.0, 11.2.0, 12.0 Last reconfirmed|2020-05-01 00:00:00 |2022-1-19 --- Comment #5 from Martin Sebor --- GCC 12 or 11 don't warn on the test case in comment #3 but reconfirming the warning for the test case in comment #0 with GCC 12: $ gcc -O2 -S -Wall -fdump-tree-strlen=/dev/stdout pr89161.c ;; Function main (main, funcdef_no=12, decl_uid=2421, cgraph_uid=13, symbol_order=13) (executed once) ... pr89161.c:7: sprintf: objsize = 3, fmtstr = ".%1u" Directive 1 at offset 0: ".", length = 1 Result: 1, 1, 1, 1 (1, 1, 1, 1) Directive 2 at offset 1: "%1u" pr89161.c: In function ‘main’: pr89161.c:7:24: warning: ‘%1u’ directive writing between 1 and 6 bytes into a region of size 2 [-Wformat-overflow=] 7 | sprintf(buf, ".%1u", (10 * a[0]) / a[1]); |^~~ In function ‘print’, inlined from ‘main’ at pr89161.c:17:5: pr89161.c:7:22: note: directive argument in the range [0, 327670] 7 | sprintf(buf, ".%1u", (10 * a[0]) / a[1]); | ^~ Result: 1, 6, 6, 6 (2, 7, 7, 7) Directive 3 at offset 4: "", length = 1 pr89161.c:7:9: note: ‘sprintf’ output between 3 and 8 bytes into a destination of size 3 7 | sprintf(buf, ".%1u", (10 * a[0]) / a[1]); | ^~~~
[Bug c/89161] Bogus -Wformat-overflow warning with value range known
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=89161 --- Comment #4 from Martin Sebor --- This should be resolved once the sprintf + strlen pass is converted to the new Ranger implementation sometime in the coming weeks (hopefully). In the meantime, changing the controlling expression in the if statement in the test case in comment #3 to (prec < sizeof buf - 10) should also help.
[Bug c/89161] Bogus -Wformat-overflow warning with value range known
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=89161 sisyphus359 at gmail dot com changed: What|Removed |Added CC||sisyphus359 at gmail dot com --- Comment #3 from sisyphus359 at gmail dot com --- Another demo of just how nasty this bug can be. (Apologies if this adds nothing to what has already been ascertained.) // /* overflow.c * * Build with (eg): * * gcc -o overflow overflow.c -O2 -Wall */ #include void foo(double, unsigned int); int main(void) { double d = 5.1; unsigned int precis = 15; foo(d, precis); } void foo(double dub, unsigned int prec) { char buf[127]; if( prec < sizeof(buf) && /** LINE 18 **/ sizeof(buf) - prec > 10 ){ sprintf (buf, "%.*g", prec, dub); /** LINE 21 **/ printf("%s\n", buf); } } // The warning is: overflow.c:21:19: warning: '%.*g' directive writing between 1 and 133 bytes into a region of size 127 [-Wformat-overflow=] sprintf (buf, "%.*g", prec, dub); ^~~~ overflow.c:21:18: note: assuming directive output of 132 bytes sprintf (buf, "%.*g", prec, dub); ^~ overflow.c:21:4: note: 'sprintf' output between 2 and 134 bytes into a destination of size 127 sprintf (buf, "%.*g", prec, dub); ^~~~ and I'm seeing it on Ubuntu-20.04, gcc-9.3.0 and on Windows 7, gcc-8.3.0. That's the message as seen on Windows, and it's essentially the same as appears on Ubuntu except that Ubuntu appends some additional noise: In file included from /usr/include/stdio.h:867, from overflow.c:4: /usr/include/x86_64-linux-gnu/bits/stdio2.h:36:10: note: ‘__builtin___sprintf_chk’ output between 2 and 134 bytes into a destination of size 127 36 | return __builtin___sprintf_chk (__s, __USE_FORTIFY_LEVEL - 1, | ^~ 37 | __bos (__s), __fmt, __va_arg_pack ()); | ~ A couple of things to note: 1) AFAICS, a buffer overflow cannot occur unless sizeof(buf) - prec wraps to a value greater than 10. That's why we check in advance that prec < sizeof(ebuf) at line 18. 2) If I comment out the first condition (ie line 18) then no warning is issued, even though the removal of that condition opens the door to buffer overflow occurring. Cheers, Rob
[Bug c/89161] Bogus -Wformat-overflow warning with value range known
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=89161 Martin Sebor changed: What|Removed |Added Known to fail|9.0 |10.0, 9.2.0 Last reconfirmed|2019-02-02 00:00:00 |2020-5-1 --- Comment #2 from Martin Sebor --- Reconfirming with GCC 10 with slightly different output: pr89161.c:7: sprintf: objsize = 3, fmtstr = ".%1u" Directive 1 at offset 0: ".", length = 1 Result: 1, 1, 1, 1 (1, 1, 1, 1) Directive 2 at offset 1: "%1u" pr89161.c: In function ‘main’: pr89161.c:7:24: warning: ‘%1u’ directive writing between 1 and 6 bytes into a region of size 2 [-Wformat-overflow=] 7 | sprintf(buf, ".%1u", (10 * a[0]) / a[1]); |^~~ pr89161.c:7:22: note: directive argument in the range [0, 327675] 7 | sprintf(buf, ".%1u", (10 * a[0]) / a[1]); | ^~ Result: 1, 6, 6, 6 (2, 7, 7, 7) Directive 3 at offset 4: "", length = 1 pr89161.c:7:9: note: ‘sprintf’ output between 3 and 8 bytes into a destination of size 3 7 | sprintf(buf, ".%1u", (10 * a[0]) / a[1]); | ^~~~
[Bug c/89161] Bogus -Wformat-overflow warning with value range known
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=89161 Martin Sebor changed: What|Removed |Added Keywords||diagnostic Status|UNCONFIRMED |NEW Last reconfirmed||2019-02-02 CC||msebor at gcc dot gnu.org Blocks||85741 Ever confirmed|0 |1 Known to fail||7.3.0, 8.2.0, 9.0 --- Comment #1 from Martin Sebor --- I can confirm the warning and agree that GCC could in theory figure out that the result is between 0 and 9 but it currently doesn't do that. With the optimization implemented the warning would go away. Note that there are limitations as how far GCC can go in determining the values or ranges of non-constant expressions, and warnings that rely on these abilities (i.e., flow-based warnings) are unavoidably imprecise. This is documented in the manual which says about -Wformat-overflow: Warn about calls to formatted input/output functions such as sprintf and vsprintf that might overflow the destination buffer. When the exact number of bytes written by a format directive cannot be determined at compile-time it is estimated based on heuristics that depend on the level argument and on optimization. A test case for the missing optimization is: void f (unsigned short a, unsigned short b) { if (a && a < b) { int x = (10 * a) / b; if (x < 0 || 9 < x) __builtin_abort (); } } Compiling it with the -fdump-tree-vrp=/dev/stdout option shows that range in which x is computed to be by VRP is: x_10: int [0, 655350] ... [local count: 536870913]: _4 = (int) a_8(D); _5 = _4 * 10; _6 = (int) b_9(D); x_10 = _5 / _6; if (x_10 > 9) goto ; [0.00%] else goto ; [100.00%] [count: 0]: __builtin_abort (); Referenced Bugs: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85741 [Bug 85741] [meta-bug] bogus/missing -Wformat-overflow