Skip to content

Commit

Permalink
Do over/underflow checks for addition (SafeMath)
Browse files Browse the repository at this point in the history
Related to #153
  • Loading branch information
cburgdorf committed Feb 22, 2021
1 parent f2c2204 commit 2f96c9d
Show file tree
Hide file tree
Showing 6 changed files with 96 additions and 1 deletion.
5 changes: 4 additions & 1 deletion compiler/src/yul/mappers/expressions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,10 @@ pub fn expr_bin_operation(
.typ;

return match op.node {
fe::BinOperator::Add => Ok(expression! { add([yul_left], [yul_right]) }),
fe::BinOperator::Add => match typ.is_signed_integer() {
true => Ok(expression! { checked_add_i256([yul_left], [yul_right]) }),
false => Ok(expression! { checked_add_u256([yul_left], [yul_right]) }),
},
fe::BinOperator::Sub => Ok(expression! { sub([yul_left], [yul_right]) }),
fe::BinOperator::Mult => Ok(expression! { mul([yul_left], [yul_right]) }),
fe::BinOperator::Div => match typ.is_signed_integer() {
Expand Down
25 changes: 25 additions & 0 deletions compiler/src/yul/runtime/functions/math.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
use yultsur::*;

/// Add two u256 numbers. Revert if result overflows.
pub fn checked_add_u256() -> yul::Statement {
function_definition! {
function checked_add_u256(val1, val2) -> sum {
// overflow, if val1 > (max_value - val2)
(if (gt(val1, (sub(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, val2)))) { (revert(0, 0)) })
(sum := add(val1, val2))
}
}
}

/// Add two i256 numbers. Revert if result over- or underflows.
pub fn checked_add_i256() -> yul::Statement {
function_definition! {
function checked_add_i256(val1, val2) -> sum {
// overflow, if val1 >= 0 and val2 > (max_value - val1)
(if (and((iszero((slt(val1, 0)))), (sgt(val2, (sub(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, val1)))))) { (revert(0, 0)) })
// underflow, if val1 < 0 and val2 < (min_val - val1)
(if (and((slt(val1, 0)), (slt(val2, (sub(0x8000000000000000000000000000000000000000000000000000000000000000, val1)))))) { (revert(0, 0)) })
(sum := add(val1, val2))
}
}
}
3 changes: 3 additions & 0 deletions compiler/src/yul/runtime/functions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use yultsur::*;
pub mod abi;
pub mod contracts;
pub mod data;
pub mod math;
pub mod structs;

/// Returns all functions that should be available during runtime.
Expand Down Expand Up @@ -35,5 +36,7 @@ pub fn std() -> Vec<yul::Statement> {
abi::pack(AbiDecodeLocation::Memory),
contracts::create2(),
contracts::create(),
math::checked_add_u256(),
math::checked_add_i256(),
]
}
53 changes: 53 additions & 0 deletions compiler/tests/evm_contracts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -707,6 +707,59 @@ fn sized_vals_in_sto() {
});
}

#[test]
fn checked_arithmetic() {
with_executor(&|mut executor| {
let harness = deploy_contract(
&mut executor,
"checked_arithmetic.fe",
"CheckedArithmetic",
&[],
);

let u256_max = ethabi::Token::Uint(U256::MAX);
let i256_max = ethabi::Token::Int(U256::from(2).pow(U256::from(255)) - 1);
let i256_min = ethabi::Token::Int(get_2s_complement_for_negative(
U256::from(2).pow(U256::from(255)),
));

harness.test_function_reverts(
&mut executor,
"add_u256",
&[u256_max.clone(), uint_token(1)],
);

harness.test_function(
&mut executor,
"add_u256",
&[u256_max.clone(), uint_token(0)],
Some(&u256_max),
);

harness.test_function_reverts(&mut executor, "add_i256", &[i256_max.clone(), int_token(1)]);

harness.test_function(
&mut executor,
"add_i256",
&[i256_max.clone(), uint_token(0)],
Some(&i256_max),
);

harness.test_function_reverts(
&mut executor,
"add_i256",
&[i256_min.clone(), int_token(-1)],
);

harness.test_function(
&mut executor,
"add_i256",
&[i256_min.clone(), int_token(0)],
Some(&i256_max),
);
});
}

#[test]
fn structs() {
with_executor(&|mut executor| {
Expand Down
7 changes: 7 additions & 0 deletions compiler/tests/fixtures/checked_arithmetic.fe
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
contract CheckedArithmetic:

pub def add_u256(left: u256, right: u256) -> u256:
return left + right

pub def add_i256(left: i256, right: i256) -> i256:
return left + right
4 changes: 4 additions & 0 deletions newsfragments/265.feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Do over/underflow checks for additions (SafeMath).

With this change all additions (e.g `x + y`) for signed and unsigned
integers check for over- and underflows and revert if necessary.

0 comments on commit 2f96c9d

Please sign in to comment.