2

Generally, How can I prevent integer overflow from happening in C programming language? I mean, Is there any functions to prevent? And finally, Is integer overflow going to get me hacked like buffer overflow or etc?

  • Go see Ghandi goes nuclear bugs in the game Civilization for a case where an integer overflow caused mayhem and fun. https://www.youtube.com/watch?v=YOg-V4OBZc0 – Michael Dorgan Jul 16 '20 at 14:05
  • You can implement functions for saturation arithmetic (similar to [this one](https://stackoverflow.com/questions/121240/how-to-do-unsigned-saturating-addition-in-c#122288) for unsigned saturating addition) and use those in any code where overflow could cause problems. – Felix G Jul 16 '20 at 14:10
  • look into bignum arithmetic libraries. they'll be a bit slower than standard C arithmetic but at least you won't get overflow. also many standard math functions (exp etc) will set errno to ERANGE in case of overflow. – ashvatthama Jul 16 '20 at 14:25
  • *Usually*, it is prevented by thinking carefully when writing code. Sometimes, this doesn't work and it happens anyway. – user253751 Jul 16 '20 at 15:10

5 Answers5

3

Assume you have two int values a, b, and you want to check that a+b doesn't produce overflow or underflow.

There are two cases: a ≥ 0 and a ≤ 0. In the first case, you cannot have underflow. You have overflow if b > INT_MAX - a. In the second case, you cannot have overflow. You can have underflow if b < INT_MIN - a. As a single expression:

a >= 0 ? b > INT_MAX - a : b < INT_MIN - a
gnasher729
  • 47,695
  • 5
  • 65
  • 91
1

Check before operation if the result will overflow.

gcc provides some helper built-in functions.

Built-in Function: bool __builtin_add_overflow (type1 a, type2 b, type3 *res)
Built-in Function: bool __builtin_sadd_overflow (int a, int b, int *res)
Built-in Function: bool __builtin_saddl_overflow (long int a, long int b, long int *res)
Built-in Function: bool __builtin_saddll_overflow (long long int a, long long int b, long long int *res)
Built-in Function: bool __builtin_uadd_overflow (unsigned int a, unsigned int b, unsigned int *res)
Built-in Function: bool __builtin_uaddl_overflow (unsigned long int a, unsigned long int b, unsigned long int *res)
Built-in Function: bool __builtin_uaddll_overflow (unsigned long long int a, unsigned long long int b, unsigned long long int *res)

    These built-in functions promote the first two operands into infinite precision signed type and perform addition on those promoted operands. The result is then cast to the type the third pointer argument points to and stored there. If the stored result is equal to the infinite precision result, the built-in functions return false, otherwise they return true. As the addition is performed in infinite signed precision, these built-in functions have fully defined behavior for all argument values.

    The first built-in function allows arbitrary integral types for operands and the result type must be pointer to some integral type other than enumerated or boolean type, the rest of the built-in functions have explicit integer types.

    The compiler will attempt to use hardware instructions to implement these built-in functions where possible, like conditional jump on overflow after addition, conditional jump on carry etc.

Built-in Function: bool __builtin_sub_overflow (type1 a, type2 b, type3 *res)
Built-in Function: bool __builtin_ssub_overflow (int a, int b, int *res)
Built-in Function: bool __builtin_ssubl_overflow (long int a, long int b, long int *res)
Built-in Function: bool __builtin_ssubll_overflow (long long int a, long long int b, long long int *res)
Built-in Function: bool __builtin_usub_overflow (unsigned int a, unsigned int b, unsigned int *res)
Built-in Function: bool __builtin_usubl_overflow (unsigned long int a, unsigned long int b, unsigned long int *res)
Built-in Function: bool __builtin_usubll_overflow (unsigned long long int a, unsigned long long int b, unsigned long long int *res)

    These built-in functions are similar to the add overflow checking built-in functions above, except they perform subtraction, subtract the second argument from the first one, instead of addition.

Built-in Function: bool __builtin_mul_overflow (type1 a, type2 b, type3 *res)
Built-in Function: bool __builtin_smul_overflow (int a, int b, int *res)
Built-in Function: bool __builtin_smull_overflow (long int a, long int b, long int *res)
Built-in Function: bool __builtin_smulll_overflow (long long int a, long long int b, long long int *res)
Built-in Function: bool __builtin_umul_overflow (unsigned int a, unsigned int b, unsigned int *res)
Built-in Function: bool __builtin_umull_overflow (unsigned long int a, unsigned long int b, unsigned long int *res)
Built-in Function: bool __builtin_umulll_overflow (unsigned long long int a, unsigned long long int b, unsigned long long int *res)

    These built-in functions are similar to the add overflow checking built-in functions above, except they perform multiplication, instead of addition.

The following built-in functions allow checking if simple arithmetic operation would overflow.

Built-in Function: bool __builtin_add_overflow_p (type1 a, type2 b, type3 c)
Built-in Function: bool __builtin_sub_overflow_p (type1 a, type2 b, type3 c)
Built-in Function: bool __builtin_mul_overflow_p (type1 a, type2 b, type3 c)

Integer overflow itself is an UB and it may lead to many problems. But it depends of your code. If it is not the part if the pointer arithmetic or array indexing you will not be "hacked"

0___________
  • 34,740
  • 4
  • 19
  • 48
  • Is there any performance difference in using the generic `type1, type2, type3` versions versus the ones with explicit argument types? – Emile Cormier Apr 13 '21 at 21:44
  • 1
    @EmileCormier It will generally generate code as effective for the particular pair types as authors of the gcc port were able to archive. – 0___________ Apr 13 '21 at 21:57
1

Whenever you declare an integer variable:

  1. Actually consider how large/small a number it will ever contain.
  2. Actually consider if it needs to be signed or unsigned. Unsigned is usually less problematic.
  3. Pick the smallest type of intn_t or uintn_t types from stdint.h that will satisfy the above (or the ...fast_t etc flavours if you wish).
  4. If needed, come up with integer constants that contain the maximum and/or minimum value the variable will hold and check against those whenever you do arithmetic.

That is, don't just aimlessly spam int all over your code without a thought.

Signed types can be problematic for other reasons than overflow too, namely whenever you need to do bitwise arithmetic. To avoid over/underflow and accidental signed bitwise arithmetic, you also need to know of the various implicit integer type promotion rules.


Is integer overflow going to get me hacked like buffer overflow or etc?

Not really, but any bug can of course be exploited if someone is aware of it - as you can see in almost every single computer game.

Lundin
  • 155,020
  • 33
  • 213
  • 341
0

You cannot prevent integer-overflow completely. If it happens, it happens. You need to be carefully at coding in the first place.

But you can try to check before the assignment if an overflow can occur.

In the case of the assignment to an object of type int or long int, a portable approach to check for integer-overflow would be to assign the value first to an object of type long long int.

Then compare the stored value if is greater than INT_MAX or less than INT_MIN in the case of int or greater than LONG_MAX or less than LONG_MIN in the case of long int.

If it is, you know it is out of range and you can prevent the integer-overflow that way.

You can encapsulate this technique into an own custom functions if you want to use functions instead, f.e.:

#include <limits.h>

// For int.

_Bool INT_OF_CHECK (long long int n)
{
    return ( n > INT_MAX || n < INT_MIN ) ? 1 : 0;  
}

// For long int.

_Bool LINT_OF_CHECK (long long int n)
{
    return ( n > LONG_MAX || n < LONG_MIN ) ? 1 : 0;  
}

The function returns 1 if the value does not fit into the range, 0 if it does.

Unfortunately this checking method doesn't work for assignments to long long int itself, but for int as well as long int it might help.

It also has the downside that it doesn't cover the case, when the value to be assigned is even greater or less than inside of the range an long long int can hold, whereas an overflow occurs here itself, but it is just a possible approach.

The macros you can find in the header limits.h.

  • These functions don't make much sense. For example, in the case of `INT_OF_CHECK`, what if a value that's greater than LLONG_MAX or smaller than LLONG_MIN is assigned to an `int`? Besides, integer overflow often happens when doing arithmetic (e.g. `x * y`) in practice rather than with assignments. And unsigned integers don't overflow at all. – P.P Jul 16 '20 at 16:22
  • @P.P "*For example, in the case of `INT_OF_CHECK`, what if a value that's greater than `LLONG_MAX` or smaller than `LLONG_MIN` is assigned to an int?*" - Yes, but I already admitted that in the last paragraph. – RobertS supports Monica Cellio Jul 16 '20 at 16:43
  • "*Besides, integer overflow often happens when doing arithmetic (e.g. `x * y`) in practice rather than with assignments.*" - Correct, but that is just one way to proof if an overflow *can* happen. But one can also use arithmetic expressions as function argument and check. It doesn't cover all cases, as I already said, but it is better than nothing and one can use these functions to check if an overflow can happen (requirement is that the value is maximum `LLONG_MAX` and minimum `LLONG_MIN`). – RobertS supports Monica Cellio Jul 16 '20 at 16:44
  • Removed the `unsigned` approaches. – RobertS supports Monica Cellio Jul 16 '20 at 16:44
0

If you want to guarantee that you won't have overflows but are fine with a bit of a performance cost, look into arbitrary-precision arithmetic libraries (for example tiny-bignum-c, or GNU Multiple Precision Arithmetic).

Since C99: standard library math functions report overflow by setting errno = ERANGE, so you can check for that (remember to set errno = 0 before calling said math function). math.h defines HUGE_VAL and related macros which you can check against for overflow, and for floating point errors there's the fenv family of functions, see the manuals about math_error(7) and fenv(3).

ashvatthama
  • 113
  • 4
  • Standard library math functions by and large provide *floating-point* computations. They are not particularly relevant to the question of *integer* overflow. – John Bollinger Jul 16 '20 at 15:08
  • @JohnBollinger yes, but I thought if OP was asking about preventing integer overflow, they would also be interested in floating-point overflow. – ashvatthama Jul 16 '20 at 15:10