Ian Lance Taylor wrote: > Consider code along these lines: > > struct s { int len; char* p; }; > > inline char > bar (struct s *sp, int n) > { > if (n < 0) > abort (); > if (n > sp->len) > abort (); > return sp->p[n]; > } > > void > foo (struct s *sp, int n) > { > int len = sp->len; > int i; > int tot = 0; > for (i = 0; i <= len; ++i) > tot += bar (sp, i); > return tot; > } > > Let's assume that bar() is inlined into foo(). Now consider the > assert. If signed overflow is undefined, then we can optimize away > the "n < 0" test; it will never be true. If signed overflow is > defined, then we can not optimize that away. That is because as far > as the compiler knows, sp->len might be INT_MAX. In that case, the > loop will never terminate, and i will wrap and become negative. (The > compiler may also eliminate the "n > sp->len" test, but that does not > rely on undefined signed overflow.)
This is an excellent example of the kind of subtle vulnerabilities undefined overflow behaviour causes. Consider the case where sp->len is under the control of an attacker. Let's assume further that sp->p is dynamically allocated, and we're running in an OS configuration where malloc(INT_MAX) actually works. Now an attacker could provoke a situation where sp->len is INT_MAX, and i becomes negative. All of a sudden, if the "n < 0" test is folded away, he's left in a situation where memory is accessed that he's not supposed to. If instead of summing up the array elements the code would write them to a network socket, we'd be getting a free dump of all the heap objects in memory after sp. That might be your private key or your password, if you're unlucky. Even worse would be the case where bar would write to sp->p. Attackers writing to memory are always bad news, and if the circumstances are right, this situation could be exploitable. This is even applicable for infinite loops: changing the SEH on Windows to point to exploit code before the infinite loop finally triggers an exception is a popular exploitation technique on that platform. See how easy it is to make this kind of security mistake, even for people who are aware of the undefinedness of signed overflow? Did you notice how innocently "nothing can happen here" function bar looks? > This is a typical example of removing an if branch because signed > overflow is undefined. This kind of code is common enough. I could not have made my point any better myself. Andreas