https://gcc.gnu.org/bugzilla/show_bug.cgi?id=78969
Bug ID: 78969 Summary: bogus snprintf truncation warning due to missing range info Product: gcc Version: 7.0 Status: UNCONFIRMED Severity: normal Priority: P3 Component: tree-optimization Assignee: unassigned at gcc dot gnu.org Reporter: msebor at gcc dot gnu.org Target Milestone: --- In both functions in the following test case the %3u argument to snprintf is in the range [0, 999] and so the directive writes exactly 3 bytes into the destination buffer of size 4. As the VRP dump shows, GCC exposes that range to the -Wformat-length checker via the get_range_info() function in the call in function f() allowing it to avoid a warning. But in the call in function g(), even though the same range is also known, it's not made available for the argument to the directive, resulting in a false positive. $ cat t.c && gcc -O2 -S -Wall -Wformat-length=2 -fdump-tree-vrp=/dev/stdout t.c void f (unsigned j, char *p) { if (j > 999) j = 0; __builtin_snprintf (p, 4, "%3u", j); } void g (unsigned j, char *p) { if (j > 999) return; __builtin_snprintf (p, 4, "%3u", j); } ;; Function f (f, funcdef_no=0, decl_uid=1796, cgraph_uid=0, symbol_order=0) ;; 1 loops found ;; ;; Loop 0 ;; header 0, latch 1 ;; depth 0, outer -1 ;; nodes: 0 1 2 3 4 ;; 2 succs { 3 4 } ;; 3 succs { 4 } ;; 4 succs { 1 } SSA replacement table N_i -> { O_1 ... O_j } means that N_i replaces O_1, ..., O_j j_6 -> { j_2(D) } j_7 -> { j_2(D) } Incremental SSA update started at block: 2 Number of blocks in CFG: 6 Number of blocks to update: 4 ( 67%) Value ranges after VRP: j_1: [0, 999] EQUIVALENCES: { } (0 elements) j_2(D): VARYING j_6: [1000, +INF] EQUIVALENCES: { j_2(D) } (1 elements) j_7: [0, 999] EQUIVALENCES: { j_2(D) } (1 elements) Removing basic block 3 f (unsigned int j, char * p) { <bb 2> [100.00%]: if (j_2(D) > 999) goto <bb 4>; [54.00%] else goto <bb 3>; [46.00%] <bb 3> [46.00%]: <bb 4> [100.00%]: # j_1 = PHI <j_2(D)(3), 0(2)> __builtin_snprintf (p_4(D), 4, "%3u", j_1); return; } ;; Function f (f, funcdef_no=0, decl_uid=1796, cgraph_uid=0, symbol_order=0) ;; 1 loops found ;; ;; Loop 0 ;; header 0, latch 1 ;; depth 0, outer -1 ;; nodes: 0 1 2 3 4 ;; 2 succs { 3 4 } ;; 3 succs { 4 } ;; 4 succs { 1 } SSA replacement table N_i -> { O_1 ... O_j } means that N_i replaces O_1, ..., O_j j_6 -> { j_2(D) } j_7 -> { j_2(D) } Incremental SSA update started at block: 2 Number of blocks in CFG: 6 Number of blocks to update: 4 ( 67%) Value ranges after VRP: j_1: [0, 999] EQUIVALENCES: { } (0 elements) j_2(D): VARYING j_6: [1000, +INF] EQUIVALENCES: { j_2(D) } (1 elements) j_7: [0, 999] EQUIVALENCES: { j_2(D) } (1 elements) Removing basic block 3 f (unsigned int j, char * p) { <bb 2> [100.00%]: if (j_2(D) > 999) goto <bb 4>; [54.00%] else goto <bb 3>; [46.00%] <bb 3> [46.00%]: <bb 4> [100.00%]: # j_1 = PHI <j_2(D)(3), 0(2)> __builtin_snprintf (p_4(D), 4, "%3u", j_1); return; } ;; Function g (g, funcdef_no=1, decl_uid=1800, cgraph_uid=1, symbol_order=1) ;; 1 loops found ;; ;; Loop 0 ;; header 0, latch 1 ;; depth 0, outer -1 ;; nodes: 0 1 2 3 4 ;; 2 succs { 4 3 } ;; 3 succs { 4 } ;; 4 succs { 1 } SSA replacement table N_i -> { O_1 ... O_j } means that N_i replaces O_1, ..., O_j j_6 -> { j_2(D) } Incremental SSA update started at block: 2 Number of blocks in CFG: 5 Number of blocks to update: 2 ( 40%) Value ranges after VRP: .MEM_1: VARYING j_2(D): VARYING j_6: [0, 999] EQUIVALENCES: { j_2(D) } (1 elements) g (unsigned int j, char * p) { <bb 2> [100.00%]: if (j_2(D) > 999) goto <bb 4>; [51.01%] else goto <bb 3>; [48.99%] <bb 3> [48.99%]: __builtin_snprintf (p_4(D), 4, "%3u", j_2(D)); <bb 4> [100.00%]: return; } t.c: In function āgā: t.c:14:30: warning: ā%3uā directive output may be truncated writing between 3 and 10 bytes into a region of size 4 [-Wformat-length=] __builtin_snprintf (p, 4, "%3u", j); ^~~ t.c:14:29: note: using the range [1, 4294967295] for directive argument __builtin_snprintf (p, 4, "%3u", j); ^~~~~ t.c:14:3: note: format output between 4 and 11 bytes into a destination of size 4 __builtin_snprintf (p, 4, "%3u", j); ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ;; Function g (g, funcdef_no=1, decl_uid=1800, cgraph_uid=1, symbol_order=1) ;; 1 loops found ;; ;; Loop 0 ;; header 0, latch 1 ;; depth 0, outer -1 ;; nodes: 0 1 2 3 4 ;; 2 succs { 4 3 } ;; 3 succs { 4 } ;; 4 succs { 1 } SSA replacement table N_i -> { O_1 ... O_j } means that N_i replaces O_1, ..., O_j j_6 -> { j_2(D) } Incremental SSA update started at block: 2 Number of blocks in CFG: 5 Number of blocks to update: 2 ( 40%) Value ranges after VRP: .MEM_1: VARYING j_2(D): VARYING j_6: [0, 999] EQUIVALENCES: { j_2(D) } (1 elements) g (unsigned int j, char * p) { <bb 2> [100.00%]: if (j_2(D) > 999) goto <bb 4>; [51.01%] else goto <bb 3>; [48.99%] <bb 3> [48.99%]: __builtin_snprintf (p_4(D), 4, "%3u", j_2(D)); <bb 4> [100.00%]: return; }