https://gcc.gnu.org/bugzilla/show_bug.cgi?id=96509
Bug ID: 96509 Summary: out of bounds access makes a bounded loop infinite with inlining Product: gcc Version: 11.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: --- The one-byte buffer overflow turns the bounded loop in the test case below into an infinite one. GCC does issue a warning which is enabled by default, but the transformation is still obviously problematic due to its security implications. $ cat x.c && gcc -O2 -S -fdump-tree-evrp=/dev/stdout x.c struct { char a[4], b[4]; } x; static inline void f (const char *s, int n) { for (int i = 0; i != n; ++i) // warning (good)... x.a[i] = s[i]; } void g (const char *s) { f (s, 5); // ...but loop doesn't terminate } ;; Function f (f, funcdef_no=0, decl_uid=1936, cgraph_uid=1, symbol_order=1) ;; 2 loops found ;; ;; Loop 0 ;; header 0, latch 1 ;; depth 0, outer -1 ;; nodes: 0 1 2 3 4 5 ;; ;; Loop 1 ;; header 4, latch 3 ;; depth 1, outer 0 ;; nodes: 4 3 ;; 2 succs { 4 } ;; 3 succs { 4 } ;; 4 succs { 3 5 } ;; 5 succs { 1 } Value ranges after Early VRP: _1: sizetype [0, 4] _2: const char * VARYING _3: char VARYING i_4: int [0, 4] n_8(D): int VARYING s_9(D): const char * VARYING i_11: int [1, 5] f (const char * s, int n) { int i; sizetype _1; const char * _2; char _3; <bb 2> : goto <bb 4>; [INV] <bb 3> : _1 = (sizetype) i_4; _2 = s_9(D) + _1; _3 = *_2; x.a[i_4] = _3; i_11 = i_4 + 1; <bb 4> : # i_4 = PHI <0(2), i_11(3)> if (i_4 != n_8(D)) goto <bb 3>; [INV] else goto <bb 5>; [INV] <bb 5> : return; } ;; Function g (g, funcdef_no=1, decl_uid=1943, cgraph_uid=2, symbol_order=2) ;; 2 loops found ;; ;; Loop 0 ;; header 0, latch 1 ;; depth 0, outer -1 ;; nodes: 0 1 2 3 4 5 ;; ;; Loop 1 ;; header 4, latch 3 ;; depth 1, outer 0 ;; nodes: 4 3 ;; 2 succs { 4 } ;; 3 succs { 4 } ;; 4 succs { 3 5 } ;; 5 succs { 1 } x.c: In function āgā: x.c:6:12: warning: iteration 4 invokes undefined behavior [-Waggressive-loop-optimizations] 6 | x.a[i] = s[i]; | ~~~~~~~^~~~~~ x.c:5:3: note: within this loop 5 | for (int i = 0; i != n; ++i) // warning (good)... | ^~~ Value ranges after Early VRP: s_2(D): const char * VARYING i_4: int [0, 4] _5: sizetype [0, 4] _6: const char * VARYING _7: char VARYING i_8: int [1, 5] Removing basic block 5 Merging blocks 4 and 3 g (const char * s) { int i; sizetype _5; const char * _6; char _7; <bb 2> : <bb 3> : # i_4 = PHI <0(2), i_8(3)> _5 = (sizetype) i_4; _6 = s_2(D) + _5; _7 = *_6; x.a[i_4] = _7; i_8 = i_4 + 1; goto <bb 3>; [100.00%] }