Does integer overflow cause undefined behavior because of memory corruption?

前端 未结 6 753
Happy的楠姐
Happy的楠姐 2020-12-25 10:53

I recently read that signed integer overflow in C and C++ causes undefined behavior:

If during the evaluation of an expression, the result is not math

6条回答
  •  我在风中等你
    2020-12-25 11:44

    The authors of the Standard left integer overflow undefined because some hardware platforms might trap in ways whose consequences could be unpredictable (possibly including random code execution and consequent memory corruption). Although two's-complement hardware with predictable silent-wraparound overflow handling was pretty much established as a standard by the time the C89 Standard was published (of the many reprogrammable-microcomputer architectures I've examined, zero use anything else) the authors of the Standard didn't want to prevent anyone from producing C implementations on older machines.

    On implementations which implemented commonplace two's-complement silent-wraparound semantics, code like

    int test(int x)
    {
      int temp = (x==INT_MAX);
      if (x+1 <= 23) temp+=2;
      return temp;
    }
    

    would, 100% reliably, return 3 when passed a value of INT_MAX, since adding 1 to INT_MAX would yield INT_MIN, which is of course less than 23.

    In the 1990s, compilers used the fact that integer overflow was undefined behavior, rather than being defined as two's-complement wrapping, to enable various optimizations which meant that the exact results of computations that overflowed would not be predictable, but aspects of behavior that didn't depend upon the exact results would stay on the rails. A 1990s compiler given the above code might likely treat it as though adding 1 to INT_MAX yielded a value numerically one larger than INT_MAX, thus causing the function to return 1 rather than 3, or it might behave like the older compilers, yielding 3. Note that in the above code, such treatment could save an instruction on many platforms, since (x+1 <= 23) would be equivalent to (x <= 22). A compiler may not be consistent in its choice of 1 or 3, but the generated code would not do anything other than yield one of those values.

    Since then, however, it has become more fashionable for compilers to use the Standard's failure to impose any requirements on program behavior in case of integer overflow (a failure motivated by the existence of hardware where the consequences might be genuinely unpredictable) to justify having compilers launch code completely off the rails in case of overflow. A modern compiler could notice that the program will invoke Undefined Behavior if x==INT_MAX, and thus conclude that the function will never be passed that value. If the function is never passed that value, the comparison with INT_MAX can be omitted. If the above function were called from another translation unit with x==INT_MAX, it might thus return 0 or 2; if called from within the same translation unit, the effect might be even more bizarre since a compiler would extend its inferences about x back to the caller.

    With regard to whether overflow would cause memory corruption, on some old hardware it might have. On older compilers running on modern hardware, it won't. On hyper-modern compilers, overflow negates the fabric of time and causality, so all bets are off. The overflow in the evaluation of x+1 could effectively corrupt the value of x that had been seen by the earlier comparison against INT_MAX, making it behave as though the value of x in memory had been corrupted. Further, such compiler behavior will often remove conditional logic that would have prevented other kinds of memory corruption, thus allowing arbitrary memory corruption to occur.

提交回复
热议问题