Often in the context of secure programming, the problem of integer overflow is mentioned. Is it possible to catch this situation in C / C ++ code? After all, processors (at least x86) have the Overflow Flag among EFLAGS , it turns out that this is technically possible, and the overhead for checking the flag in the register should not be large (I think so). Either the problem in posing the question and overflow should not be caught, but not allowed to never? Does the latter mean that integer types are fundamentally not suitable for any calculations?

PS The question arose due to the fact that in the existing code (observed by me) int is widely used for arithmetic calculations and nothing protects from overflows, hypothetically they can go unnoticed (by logic). And in most cases, with a high probability everything is normal (real values ​​are small), but sometimes ...

5 answers 5

Officially, the standard language is written, if I do not confuse that overflow of signed integer types depends on the implementation (an exception can be generated, it can be ignored), and unsigned ones are ignored - the value is reduced to a representable range.

Those. it turns out something like this - C / C ++ itself has no built-in portable overflow detection tools.

But you can always use additional methods that allow you to find out by the operands whether there will be an overflow during the operation. A lot of such things are described in the book by G. Warren. Algorithmic tricks for programmers .

Well, as an illustration, how easy it is to miss this overflow - in Straustrup in Programming. Principles and practice ... there is one program in which a series was calculated for e x , or something - and it went through it. He honestly wrote that earlier, they say, I thought it was due to a loss of precision in double , and it seemed to even write in the first edition of the book, and then it came to the second edition that in fact it was an overflow of integer values ​​when calculating factorial . If even Straustrup ... :)

    The default action depends on the processor, compiler and settings. At MIPS, for example, the addition of the sign turns into the add command, which generates an exception during an overflow, and an unsigned one - an addu, which does not generate. On x86, the overflow is silently ignored. Modern type compilers believe that there should be no overflow, sometimes because of this there are interesting effects - this is the most destructive example of those that I have seen. The standard here operates according to the principle "we give you the opportunity to write as efficiently as possible, because this is C, not a learning language, and protection from curvature is your problem."

    For C ++ there is, for example, the library (template header file) SafeInt3 from Microsoft, under a free license; it can cover all the basic operations, although it does not use the specifics of processors (even x86) and therefore is ineffective in many cases - where one OF or CF suffices, it cracks a lot of complicated calculations.

    For C, you have to write everything manually, replacing the usual operations with your equivalents with functions or macros.

    UPDATE: about the new built-in functions of the latest gcc and clang have already been written. But without them you can do quite well. For example, in the case of adding two a > INT_MAX - b , a check of the form a > INT_MAX - b if b> 0, otherwise a < INT_MIN - b , is sufficient to verify the solvability of the addition.

      Though answers from links are not welcome, I found good material (including the code for checking whether overflow will (more generally, safely) add, subtract, multiply and divide integers):

      https://www.securecoding.cert.org/confluence/display/c/INT32-C.+Ensure+that+operations+on+signed+integers+do+not+result+in+overflow

      with discussions, etc.

      Update

      Since, when searching for an answer, you always want to immediately see a specific code that helps in solving a problem (rather than guessing, for example, how to write the verification condition for subtraction correctly, already knowing the correct answer for addition), here are some of the materials on this link that can be used as samples for their programs.

      Check before adding:

       #include <limits.h> void f(signed int si_a, signed int si_b) { signed int sum; if (((si_b > 0) && (si_a > (INT_MAX - si_b))) || ((si_b < 0) && (si_a < (INT_MIN - si_b)))) { /* Handle error */ } else { sum = si_a + si_b; } /* ... */ } 

      Check before subtraction:

       #include <limits.h> void func(signed int si_a, signed int si_b) { signed int diff; if ((si_b > 0 && si_a < INT_MIN + si_b) || (si_b < 0 && si_a > INT_MAX + si_b)) { /* Handle error */ } else { diff = si_a - si_b; } /* ... */ } 

      Check before multiplication:

       #include <limits.h> void func(signed int si_a, signed int si_b) { signed int result; if (si_a > 0) { /* si_a is positive */ if (si_b > 0) { /* si_a and si_b are positive */ if (si_a > (INT_MAX / si_b)) { /* Handle error */ } } else { /* si_a positive, si_b nonpositive */ if (si_b < (INT_MIN / si_a)) { /* Handle error */ } } /* si_a positive, si_b nonpositive */ } else { /* si_a is nonpositive */ if (si_b > 0) { /* si_a is nonpositive, si_b is positive */ if (si_a < (INT_MIN / si_b)) { /* Handle error */ } } else { /* si_a and si_b are nonpositive */ if ( (si_a != 0) && (si_b < (INT_MAX / si_a))) { /* Handle error */ } } /* End if si_a and si_b are nonpositive */ } /* End if si_a is nonpositive */ result = si_a * si_b; } 

      Check before dividing:
      (or calculating the remainder)

       #include <limits.h> void func(signed long s_a, signed long s_b) { signed long result; if ((s_b == 0) || ((s_a == LONG_MIN) && (s_b == -1))) { /* Handle error */ } else { result = s_a / s_b; } /* ... */ } 

      Well, if anyone is not too lazy to pull out the rest (related to the issue of the TS) code (as well as a description of all the essential points) here and arrange it carefully, you are welcome.

      • Moderators, I would love to make it general, but I don’t see how to achieve it ... - avp

      I will try to summarize: it is easier to avoid problems (having thought over arithmetic in detail) than to (detect and handle) it later (unpleasantly).

      Interception solutions either depend on the compiler or are a rejection of directly built-in types. I think a suitable solution (except for working with memory and collections that allow indexing) can be the use of floating-point numbers, since they are more close to natural arithmetic (in terms of infinity, dividing by 0).

      In the book "24 Mortal Sins of Computer Security" Howard, Leblanc, Viegie met this solution:

      • consideration of type sizes (see other answers);
      • code simplicity;
      • explicit type conversion;
      • using SafeInt (by David LeBlanc)
      • when using gcc , compile with the -ftrapv flag (when overflowing signed integer abort() is called);
      • the use of unsigned integers for working with memory and array indices (slightly eases the consequences);

        In Visual C ++, you can use functions from Intsafe.h , for example, to multiply:

         #include <stdlib.h> #include <stdio.h> #include <windows.h> #include <tchar.h> #include <Intsafe.h> int _tmain(int argc, _TCHAR* argv[]) { ULONGLONG a=100000000000, b=5000000000, c; HRESULT hr = ULongLongMult(a,b,&c); if(SUCCEEDED(hr)) printf("Result is %llu",c); else if(hr == INTSAFE_E_ARITHMETIC_OVERFLOW) printf("Overflow"); return 0; } 

        These functions are defined as inline, and their implementation depends on the architecture. ULongLongMult function:

        • On 64-bit architectures, it uses the _umul128 compiler's intrinsic function, so it should be quite effective.

        • On 32-bit architectures, a special calculation algorithm is used with a partition of numbers into 2 32-bit parts (the result is calculated using the formula ab * cd = (a*c*2^64) + (a*d*2^32) + (b*c*2^32) + (b*d) ), and overflow is detected by checking certain bits in the intermediate results.