RUST : How to check overflow and underflow in integer types
What is overflow and underflow?
Overflow occurs when an arithmetic operation attempts to create a numeric value that is outside of the range that can be represented with a given number of digits – either higher than the maximum or lower than the minimum representable value. Underflow is a condition in a computer program where the result of a calculation is a number of smaller absolute value than the computer can actually represent in memory on its central processing unit (CPU).
Simply put, overflow and underflow happen when we assign a value that is out of range of the declared data type of the variable.
If the (absolute) value is too big, we call it overflow, if the value is too small, we call it underflow.
Instead of an error in the program, it usually causes the result to be unexpected.
Data overflows have been listed as the number 8 most dangerous software error in the most recent CWE 2019 list, mostly because they often lead to buffer overflows, which are currently the number 1 most dangerous software error according to that list.
Most integer overflow and underflow conditions lead to erroneous program behavior but don’t cause any vulnerabilities. There is no error, there is no warning, you simply get a wrong result of the operation. So, they are very hard to discover and prevent.
So let’s see how to prevent these things in Rust.
Preventing Integer Overflows
Rust provides standard library integer types have checked_* methods for common operations, such as checked_add for addition, etc.
The API form of each function is variable as follows.
// Checked integer addition. Computes self + rhs,
// returning None if overflow occurred.
pub const fn checked_add(self, rhs: T) -> Option<T>
//Checked integer subtraction. Computes self - rhs,
// returning None if overflow occurred.
pub const fn checked_sub(self, rhs: T) -> Option<T>
// Checked integer multiplication. Computes self * rhs,
// returning None if overflow occurred.
pub const fn checked_mul(self, rhs: T) -> Option<T>
// Checked integer division. Computes self / rhs,
// returning None if rhs == 0.
pub fn checked_div(self, rhs: T) -> Option<T>
Looking at API form, Each function returns as an Option type. When an overflow or underflow problem occurs, it returns None and the rest return Some(T).
So, it is good to implement programming whether to proceed with the operation by checking the return or to cause panic.
Below code is a simple example. Let’s make a bug-free program by referring to the following.
let x = u32::max_value() - 2;
let y = 3;
match x.checked_add(y) {
Some(v) => {
println!("{} + {} = {}", x, y, v);
}
None => {
println!("overflow!");
}
};
// count checker
let x:u64 = u64::min_value();
if x.checked_sub(1).is_none() {
println!("This operation's result is underflow");
}
reference)