mirror of
https://github.com/pezkuwichain/revive.git
synced 2026-10-25 05:05:06 +00:00
Fix optimization specific SREM edge case (#527)
Existing test coverage didn't catch a signed remainder overflow semantics edge case behavior where at least one the operators is being constant folded by LLVM instead of solc. This PR fixes the SREM translation bug and adds missing test coverage for division ops with constants. Closes #524 --------- Signed-off-by: xermicus <cyrill@parity.io>
This commit is contained in:
1 parent
62a62770e3
commit
40e97d11eb
23 files changed
+1336
-21
No files matched your search
@@ -186,6 +186,78 @@ case!("DivisionArithmetics.sol", DivisionArithmetics, sdivCall, division_arithme
|
||||
case!("DivisionArithmetics.sol", DivisionArithmetics, modCall, division_arithmetics_mod, n: U256, d: U256);
|
||||
case!("DivisionArithmetics.sol", DivisionArithmetics, smodCall, division_arithmetics_smod, n: I256, d: I256);
|
||||
|
||||
sol!(
|
||||
contract DivConst {
|
||||
function divRhsZero(uint256 n) external pure returns (uint256);
|
||||
function divRhsOne(uint256 n) external pure returns (uint256);
|
||||
function divRhsTwo(uint256 n) external pure returns (uint256);
|
||||
function divRhsFive(uint256 n) external pure returns (uint256);
|
||||
function divRhsMax(uint256 n) external pure returns (uint256);
|
||||
function divLhsZero(uint256 d) external pure returns (uint256);
|
||||
function divLhsOne(uint256 d) external pure returns (uint256);
|
||||
function divLhsTwo(uint256 d) external pure returns (uint256);
|
||||
function divLhsFive(uint256 d) external pure returns (uint256);
|
||||
function divLhsMax(uint256 d) external pure returns (uint256);
|
||||
}
|
||||
|
||||
contract SdivConst {
|
||||
function sdivRhsZero(int256 n) external pure returns (int256);
|
||||
function sdivRhsOne(int256 n) external pure returns (int256);
|
||||
function sdivRhsNegOne(int256 n) external pure returns (int256);
|
||||
function sdivRhsTwo(int256 n) external pure returns (int256);
|
||||
function sdivRhsNegTwo(int256 n) external pure returns (int256);
|
||||
function sdivRhsFive(int256 n) external pure returns (int256);
|
||||
function sdivRhsNegFive(int256 n) external pure returns (int256);
|
||||
function sdivRhsMin(int256 n) external pure returns (int256);
|
||||
function sdivRhsMinPlusOne(int256 n) external pure returns (int256);
|
||||
function sdivRhsMax(int256 n) external pure returns (int256);
|
||||
function sdivLhsZero(int256 d) external pure returns (int256);
|
||||
function sdivLhsOne(int256 d) external pure returns (int256);
|
||||
function sdivLhsNegOne(int256 d) external pure returns (int256);
|
||||
function sdivLhsTwo(int256 d) external pure returns (int256);
|
||||
function sdivLhsNegTwo(int256 d) external pure returns (int256);
|
||||
function sdivLhsFive(int256 d) external pure returns (int256);
|
||||
function sdivLhsNegFive(int256 d) external pure returns (int256);
|
||||
function sdivLhsMin(int256 d) external pure returns (int256);
|
||||
function sdivLhsMinPlusOne(int256 d) external pure returns (int256);
|
||||
function sdivLhsMax(int256 d) external pure returns (int256);
|
||||
}
|
||||
|
||||
contract ModConst {
|
||||
function modRhsZero(uint256 n) external pure returns (uint256);
|
||||
function modRhsOne(uint256 n) external pure returns (uint256);
|
||||
function modRhsTwo(uint256 n) external pure returns (uint256);
|
||||
function modRhsFive(uint256 n) external pure returns (uint256);
|
||||
function modRhsMax(uint256 n) external pure returns (uint256);
|
||||
function modLhsZero(uint256 d) external pure returns (uint256);
|
||||
function modLhsOne(uint256 d) external pure returns (uint256);
|
||||
function modLhsTwo(uint256 d) external pure returns (uint256);
|
||||
function modLhsFive(uint256 d) external pure returns (uint256);
|
||||
function modLhsMax(uint256 d) external pure returns (uint256);
|
||||
}
|
||||
|
||||
contract SmodConst {
|
||||
function smodRhsZero(int256 n) external pure returns (int256);
|
||||
function smodRhsOne(int256 n) external pure returns (int256);
|
||||
function smodRhsNegOne(int256 n) external pure returns (int256);
|
||||
function smodRhsTwo(int256 n) external pure returns (int256);
|
||||
function smodRhsNegTwo(int256 n) external pure returns (int256);
|
||||
function smodRhsFive(int256 n) external pure returns (int256);
|
||||
function smodRhsNegFive(int256 n) external pure returns (int256);
|
||||
function smodRhsMin(int256 n) external pure returns (int256);
|
||||
function smodRhsMax(int256 n) external pure returns (int256);
|
||||
function smodLhsZero(int256 d) external pure returns (int256);
|
||||
function smodLhsOne(int256 d) external pure returns (int256);
|
||||
function smodLhsNegOne(int256 d) external pure returns (int256);
|
||||
function smodLhsTwo(int256 d) external pure returns (int256);
|
||||
function smodLhsNegTwo(int256 d) external pure returns (int256);
|
||||
function smodLhsFive(int256 d) external pure returns (int256);
|
||||
function smodLhsNegFive(int256 d) external pure returns (int256);
|
||||
function smodLhsMin(int256 d) external pure returns (int256);
|
||||
function smodLhsMax(int256 d) external pure returns (int256);
|
||||
}
|
||||
);
|
||||
|
||||
sol!(
|
||||
contract Send {
|
||||
function transfer_self(uint _amount) public payable;
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use alloy_primitives::*;
|
||||
use alloy_sol_types::SolCall;
|
||||
use resolc::test_utils::build_yul;
|
||||
use revive_runner::*;
|
||||
use SpecsAction::*;
|
||||
|
||||
use crate::cases::Contract;
|
||||
use crate::cases::{DivConst, ModConst, SdivConst, SmodConst};
|
||||
|
||||
/// Parameters:
|
||||
/// - The function name of the test
|
||||
@@ -268,6 +270,522 @@ fn signed_remainder() {
|
||||
run_differential(actions);
|
||||
}
|
||||
|
||||
/// Build calldata for the both-const Yul fixtures. Each fixture reads the case
|
||||
/// index from `calldataload(0)` and a 32-byte "tag" from `calldataload(32)`
|
||||
/// that gets XORed into the returned result. A non-zero tag turns silent
|
||||
/// poison/undef from a UB-triggering const-fold into an observable divergence
|
||||
/// from EVM (otherwise the buggy path coincidentally returns 0, which matches
|
||||
/// EVM SMOD/SDIV's defined result for INT_MIN op -1 and masks the bug).
|
||||
fn yul_which_calldata(which: u8) -> Vec<u8> {
|
||||
let mut data = vec![0u8; 64];
|
||||
data[31] = which;
|
||||
// tag: 0xdeadbeef padded into the second 32-byte word
|
||||
data[60..64].copy_from_slice(&[0xde, 0xad, 0xbe, 0xef]);
|
||||
data
|
||||
}
|
||||
|
||||
fn instantiate_yul(path: &str, contract: &str) -> Vec<SpecsAction> {
|
||||
vec![Instantiate {
|
||||
origin: TestAddress::Alice,
|
||||
value: 0,
|
||||
gas_limit: Some(GAS_LIMIT),
|
||||
storage_deposit_limit: None,
|
||||
code: Code::Yul {
|
||||
path: path.into(),
|
||||
contract: contract.to_string(),
|
||||
},
|
||||
data: vec![],
|
||||
salt: OptionalHex::default(),
|
||||
}]
|
||||
}
|
||||
|
||||
fn unsigned_const_set() -> (U256, U256, U256, U256) {
|
||||
(U256::from(1), U256::from(2), U256::from(5), U256::MAX)
|
||||
}
|
||||
|
||||
fn signed_const_set() -> (I256, I256, I256, I256, I256, I256, I256, I256) {
|
||||
(
|
||||
I256::try_from(1).unwrap(),
|
||||
I256::try_from(2).unwrap(),
|
||||
I256::try_from(-2).unwrap(),
|
||||
I256::try_from(5).unwrap(),
|
||||
I256::try_from(-5).unwrap(),
|
||||
I256::MIN,
|
||||
I256::MIN + I256::ONE,
|
||||
I256::MAX,
|
||||
)
|
||||
}
|
||||
|
||||
fn div_rhs_const_data(n: U256, d: U256) -> Vec<u8> {
|
||||
let (one, two, five, max) = unsigned_const_set();
|
||||
if d == U256::ZERO {
|
||||
DivConst::divRhsZeroCall::new((n,)).abi_encode()
|
||||
} else if d == one {
|
||||
DivConst::divRhsOneCall::new((n,)).abi_encode()
|
||||
} else if d == two {
|
||||
DivConst::divRhsTwoCall::new((n,)).abi_encode()
|
||||
} else if d == five {
|
||||
DivConst::divRhsFiveCall::new((n,)).abi_encode()
|
||||
} else if d == max {
|
||||
DivConst::divRhsMaxCall::new((n,)).abi_encode()
|
||||
} else {
|
||||
panic!("no divRhsConst variant for d={d}")
|
||||
}
|
||||
}
|
||||
|
||||
fn div_lhs_const_data(n: U256, d: U256) -> Vec<u8> {
|
||||
let (one, two, five, max) = unsigned_const_set();
|
||||
if n == U256::ZERO {
|
||||
DivConst::divLhsZeroCall::new((d,)).abi_encode()
|
||||
} else if n == one {
|
||||
DivConst::divLhsOneCall::new((d,)).abi_encode()
|
||||
} else if n == two {
|
||||
DivConst::divLhsTwoCall::new((d,)).abi_encode()
|
||||
} else if n == five {
|
||||
DivConst::divLhsFiveCall::new((d,)).abi_encode()
|
||||
} else if n == max {
|
||||
DivConst::divLhsMaxCall::new((d,)).abi_encode()
|
||||
} else {
|
||||
panic!("no divLhsConst variant for n={n}")
|
||||
}
|
||||
}
|
||||
|
||||
fn mod_rhs_const_data(n: U256, d: U256) -> Vec<u8> {
|
||||
let (one, two, five, max) = unsigned_const_set();
|
||||
if d == U256::ZERO {
|
||||
ModConst::modRhsZeroCall::new((n,)).abi_encode()
|
||||
} else if d == one {
|
||||
ModConst::modRhsOneCall::new((n,)).abi_encode()
|
||||
} else if d == two {
|
||||
ModConst::modRhsTwoCall::new((n,)).abi_encode()
|
||||
} else if d == five {
|
||||
ModConst::modRhsFiveCall::new((n,)).abi_encode()
|
||||
} else if d == max {
|
||||
ModConst::modRhsMaxCall::new((n,)).abi_encode()
|
||||
} else {
|
||||
panic!("no modRhsConst variant for d={d}")
|
||||
}
|
||||
}
|
||||
|
||||
fn mod_lhs_const_data(n: U256, d: U256) -> Vec<u8> {
|
||||
let (one, two, five, max) = unsigned_const_set();
|
||||
if n == U256::ZERO {
|
||||
ModConst::modLhsZeroCall::new((d,)).abi_encode()
|
||||
} else if n == one {
|
||||
ModConst::modLhsOneCall::new((d,)).abi_encode()
|
||||
} else if n == two {
|
||||
ModConst::modLhsTwoCall::new((d,)).abi_encode()
|
||||
} else if n == five {
|
||||
ModConst::modLhsFiveCall::new((d,)).abi_encode()
|
||||
} else if n == max {
|
||||
ModConst::modLhsMaxCall::new((d,)).abi_encode()
|
||||
} else {
|
||||
panic!("no modLhsConst variant for n={n}")
|
||||
}
|
||||
}
|
||||
|
||||
fn sdiv_rhs_const_data(n: I256, d: I256) -> Vec<u8> {
|
||||
let (one, two, neg_two, five, neg_five, min, min_p1, max) = signed_const_set();
|
||||
if d == I256::ZERO {
|
||||
SdivConst::sdivRhsZeroCall::new((n,)).abi_encode()
|
||||
} else if d == one {
|
||||
SdivConst::sdivRhsOneCall::new((n,)).abi_encode()
|
||||
} else if d == I256::MINUS_ONE {
|
||||
SdivConst::sdivRhsNegOneCall::new((n,)).abi_encode()
|
||||
} else if d == two {
|
||||
SdivConst::sdivRhsTwoCall::new((n,)).abi_encode()
|
||||
} else if d == neg_two {
|
||||
SdivConst::sdivRhsNegTwoCall::new((n,)).abi_encode()
|
||||
} else if d == five {
|
||||
SdivConst::sdivRhsFiveCall::new((n,)).abi_encode()
|
||||
} else if d == neg_five {
|
||||
SdivConst::sdivRhsNegFiveCall::new((n,)).abi_encode()
|
||||
} else if d == min {
|
||||
SdivConst::sdivRhsMinCall::new((n,)).abi_encode()
|
||||
} else if d == min_p1 {
|
||||
SdivConst::sdivRhsMinPlusOneCall::new((n,)).abi_encode()
|
||||
} else if d == max {
|
||||
SdivConst::sdivRhsMaxCall::new((n,)).abi_encode()
|
||||
} else {
|
||||
panic!("no sdivRhsConst variant for d={d}")
|
||||
}
|
||||
}
|
||||
|
||||
fn sdiv_lhs_const_data(n: I256, d: I256) -> Vec<u8> {
|
||||
let (one, two, neg_two, five, neg_five, min, min_p1, max) = signed_const_set();
|
||||
if n == I256::ZERO {
|
||||
SdivConst::sdivLhsZeroCall::new((d,)).abi_encode()
|
||||
} else if n == one {
|
||||
SdivConst::sdivLhsOneCall::new((d,)).abi_encode()
|
||||
} else if n == I256::MINUS_ONE {
|
||||
SdivConst::sdivLhsNegOneCall::new((d,)).abi_encode()
|
||||
} else if n == two {
|
||||
SdivConst::sdivLhsTwoCall::new((d,)).abi_encode()
|
||||
} else if n == neg_two {
|
||||
SdivConst::sdivLhsNegTwoCall::new((d,)).abi_encode()
|
||||
} else if n == five {
|
||||
SdivConst::sdivLhsFiveCall::new((d,)).abi_encode()
|
||||
} else if n == neg_five {
|
||||
SdivConst::sdivLhsNegFiveCall::new((d,)).abi_encode()
|
||||
} else if n == min {
|
||||
SdivConst::sdivLhsMinCall::new((d,)).abi_encode()
|
||||
} else if n == min_p1 {
|
||||
SdivConst::sdivLhsMinPlusOneCall::new((d,)).abi_encode()
|
||||
} else if n == max {
|
||||
SdivConst::sdivLhsMaxCall::new((d,)).abi_encode()
|
||||
} else {
|
||||
panic!("no sdivLhsConst variant for n={n}")
|
||||
}
|
||||
}
|
||||
|
||||
fn smod_rhs_const_data(n: I256, d: I256) -> Vec<u8> {
|
||||
let (one, two, neg_two, five, neg_five, min, _min_p1, max) = signed_const_set();
|
||||
if d == I256::ZERO {
|
||||
SmodConst::smodRhsZeroCall::new((n,)).abi_encode()
|
||||
} else if d == one {
|
||||
SmodConst::smodRhsOneCall::new((n,)).abi_encode()
|
||||
} else if d == I256::MINUS_ONE {
|
||||
SmodConst::smodRhsNegOneCall::new((n,)).abi_encode()
|
||||
} else if d == two {
|
||||
SmodConst::smodRhsTwoCall::new((n,)).abi_encode()
|
||||
} else if d == neg_two {
|
||||
SmodConst::smodRhsNegTwoCall::new((n,)).abi_encode()
|
||||
} else if d == five {
|
||||
SmodConst::smodRhsFiveCall::new((n,)).abi_encode()
|
||||
} else if d == neg_five {
|
||||
SmodConst::smodRhsNegFiveCall::new((n,)).abi_encode()
|
||||
} else if d == min {
|
||||
SmodConst::smodRhsMinCall::new((n,)).abi_encode()
|
||||
} else if d == max {
|
||||
SmodConst::smodRhsMaxCall::new((n,)).abi_encode()
|
||||
} else {
|
||||
panic!("no smodRhsConst variant for d={d}")
|
||||
}
|
||||
}
|
||||
|
||||
fn smod_lhs_const_data(n: I256, d: I256) -> Vec<u8> {
|
||||
let (one, two, neg_two, five, neg_five, min, _min_p1, max) = signed_const_set();
|
||||
if n == I256::ZERO {
|
||||
SmodConst::smodLhsZeroCall::new((d,)).abi_encode()
|
||||
} else if n == one {
|
||||
SmodConst::smodLhsOneCall::new((d,)).abi_encode()
|
||||
} else if n == I256::MINUS_ONE {
|
||||
SmodConst::smodLhsNegOneCall::new((d,)).abi_encode()
|
||||
} else if n == two {
|
||||
SmodConst::smodLhsTwoCall::new((d,)).abi_encode()
|
||||
} else if n == neg_two {
|
||||
SmodConst::smodLhsNegTwoCall::new((d,)).abi_encode()
|
||||
} else if n == five {
|
||||
SmodConst::smodLhsFiveCall::new((d,)).abi_encode()
|
||||
} else if n == neg_five {
|
||||
SmodConst::smodLhsNegFiveCall::new((d,)).abi_encode()
|
||||
} else if n == min {
|
||||
SmodConst::smodLhsMinCall::new((d,)).abi_encode()
|
||||
} else if n == max {
|
||||
SmodConst::smodLhsMaxCall::new((d,)).abi_encode()
|
||||
} else {
|
||||
panic!("no smodLhsConst variant for n={n}")
|
||||
}
|
||||
}
|
||||
|
||||
fn push_call(actions: &mut Vec<SpecsAction>, dest: TestAddress, data: Vec<u8>) {
|
||||
actions.push(Call {
|
||||
origin: TestAddress::Alice,
|
||||
dest,
|
||||
value: 0,
|
||||
gas_limit: None,
|
||||
storage_deposit_limit: None,
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unsigned_division_half_const() {
|
||||
let mut actions = instantiate("contracts/DivisionArithmeticsConst.sol", "DivConst");
|
||||
let (one, two, five, _max) = unsigned_const_set();
|
||||
let pairs = [
|
||||
(five, five),
|
||||
(five, one),
|
||||
(U256::ZERO, U256::MAX),
|
||||
(five, two),
|
||||
(one, U256::ZERO),
|
||||
];
|
||||
for (n, d) in pairs {
|
||||
push_call(
|
||||
&mut actions,
|
||||
TestAddress::Instantiated(0),
|
||||
div_rhs_const_data(n, d),
|
||||
);
|
||||
push_call(
|
||||
&mut actions,
|
||||
TestAddress::Instantiated(0),
|
||||
div_lhs_const_data(n, d),
|
||||
);
|
||||
}
|
||||
run_differential(actions);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unsigned_division_both_const() {
|
||||
let mut actions = instantiate_yul("contracts/DivBothConst.yul", "DivBothConst");
|
||||
let pair_count = 5;
|
||||
for i in 0..pair_count {
|
||||
push_call(
|
||||
&mut actions,
|
||||
TestAddress::Instantiated(0),
|
||||
yul_which_calldata(i),
|
||||
);
|
||||
}
|
||||
run_differential(actions);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn signed_division_half_const() {
|
||||
let mut actions = instantiate("contracts/DivisionArithmeticsConst.sol", "SdivConst");
|
||||
let (one, two, neg_two, five, neg_five, _min, _min_p1, _max) = signed_const_set();
|
||||
let pairs = [
|
||||
(five, five),
|
||||
(five, one),
|
||||
(I256::ZERO, I256::MAX),
|
||||
(I256::ZERO, I256::MINUS_ONE),
|
||||
(five, two),
|
||||
(five, I256::MINUS_ONE),
|
||||
(I256::MINUS_ONE, neg_two),
|
||||
(neg_five, neg_five),
|
||||
(neg_five, two),
|
||||
(I256::MINUS_ONE, I256::MIN),
|
||||
(one, I256::ZERO),
|
||||
(I256::MIN, I256::MINUS_ONE),
|
||||
(I256::MIN + I256::ONE, I256::MINUS_ONE),
|
||||
];
|
||||
for (n, d) in pairs {
|
||||
push_call(
|
||||
&mut actions,
|
||||
TestAddress::Instantiated(0),
|
||||
sdiv_rhs_const_data(n, d),
|
||||
);
|
||||
push_call(
|
||||
&mut actions,
|
||||
TestAddress::Instantiated(0),
|
||||
sdiv_lhs_const_data(n, d),
|
||||
);
|
||||
}
|
||||
run_differential(actions);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn signed_division_both_const() {
|
||||
let mut actions = instantiate_yul("contracts/SdivBothConst.yul", "SdivBothConst");
|
||||
let pair_count = 13;
|
||||
for i in 0..pair_count {
|
||||
push_call(
|
||||
&mut actions,
|
||||
TestAddress::Instantiated(0),
|
||||
yul_which_calldata(i),
|
||||
);
|
||||
}
|
||||
run_differential(actions);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unsigned_remainder_half_const() {
|
||||
let mut actions = instantiate("contracts/DivisionArithmeticsConst.sol", "ModConst");
|
||||
let (one, two, five, _max) = unsigned_const_set();
|
||||
let pairs = [
|
||||
(five, five),
|
||||
(five, one),
|
||||
(U256::ZERO, U256::MAX),
|
||||
(U256::MAX, U256::MAX),
|
||||
(five, two),
|
||||
(two, five),
|
||||
(U256::MAX, U256::ZERO),
|
||||
];
|
||||
for (n, d) in pairs {
|
||||
push_call(
|
||||
&mut actions,
|
||||
TestAddress::Instantiated(0),
|
||||
mod_rhs_const_data(n, d),
|
||||
);
|
||||
push_call(
|
||||
&mut actions,
|
||||
TestAddress::Instantiated(0),
|
||||
mod_lhs_const_data(n, d),
|
||||
);
|
||||
}
|
||||
run_differential(actions);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unsigned_remainder_both_const() {
|
||||
let mut actions = instantiate_yul("contracts/ModBothConst.yul", "ModBothConst");
|
||||
let pair_count = 7;
|
||||
for i in 0..pair_count {
|
||||
push_call(
|
||||
&mut actions,
|
||||
TestAddress::Instantiated(0),
|
||||
yul_which_calldata(i),
|
||||
);
|
||||
}
|
||||
run_differential(actions);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn signed_remainder_half_const() {
|
||||
let mut actions = instantiate("contracts/DivisionArithmeticsConst.sol", "SmodConst");
|
||||
let (one, two, neg_two, five, neg_five, _min, _min_p1, _max) = signed_const_set();
|
||||
let pairs = [
|
||||
(five, five),
|
||||
(five, one),
|
||||
(I256::ZERO, I256::MAX),
|
||||
(I256::MAX, I256::MAX),
|
||||
(five, two),
|
||||
(two, five),
|
||||
(five, neg_five),
|
||||
(five, I256::MINUS_ONE),
|
||||
(five, neg_two),
|
||||
(neg_five, two),
|
||||
(neg_two, five),
|
||||
(neg_five, neg_five),
|
||||
(neg_five, I256::MINUS_ONE),
|
||||
(neg_five, neg_two),
|
||||
(neg_two, neg_five),
|
||||
(I256::MIN, I256::MINUS_ONE),
|
||||
(I256::ZERO, I256::ZERO),
|
||||
];
|
||||
for (n, d) in pairs {
|
||||
push_call(
|
||||
&mut actions,
|
||||
TestAddress::Instantiated(0),
|
||||
smod_rhs_const_data(n, d),
|
||||
);
|
||||
push_call(
|
||||
&mut actions,
|
||||
TestAddress::Instantiated(0),
|
||||
smod_lhs_const_data(n, d),
|
||||
);
|
||||
}
|
||||
run_differential(actions);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn signed_remainder_both_const() {
|
||||
let mut actions = instantiate_yul("contracts/SmodBothConst.yul", "SmodBothConst");
|
||||
let pair_count = 17;
|
||||
for i in 0..pair_count {
|
||||
push_call(
|
||||
&mut actions,
|
||||
TestAddress::Instantiated(0),
|
||||
yul_which_calldata(i),
|
||||
);
|
||||
}
|
||||
run_differential(actions);
|
||||
}
|
||||
|
||||
/// Surfaces the `smod(INT_MIN, -1)` LLVM-UB const-fold bug from
|
||||
/// paritytech/revive#524. See `SmodIntMinNegOneBug.yul` for why this specific
|
||||
/// fixture is shaped the way it is. Expected to FAIL until the bug is fixed.
|
||||
#[test]
|
||||
fn signed_remainder_int_min_neg_one_bug() {
|
||||
let mut actions = instantiate_yul("contracts/SmodIntMinNegOneBug.yul", "SmodIntMinNegOneBug");
|
||||
let mut tag = vec![0u8; 32];
|
||||
tag[28..32].copy_from_slice(&[0xde, 0xad, 0xbe, 0xef]);
|
||||
push_call(&mut actions, TestAddress::Instantiated(0), tag);
|
||||
run_differential(actions);
|
||||
}
|
||||
|
||||
/// Sibling to `signed_remainder_int_min_neg_one_bug`. Per the issue, sdiv is
|
||||
/// claimed to be guarded; this test pins that guard so regressions surface.
|
||||
#[test]
|
||||
fn signed_division_int_min_neg_one_bug() {
|
||||
let mut actions = instantiate_yul("contracts/SdivIntMinNegOneBug.yul", "SdivIntMinNegOneBug");
|
||||
let mut tag = vec![0u8; 32];
|
||||
tag[28..32].copy_from_slice(&[0xde, 0xad, 0xbe, 0xef]);
|
||||
push_call(&mut actions, TestAddress::Instantiated(0), tag);
|
||||
run_differential(actions);
|
||||
}
|
||||
|
||||
/// Build the standard "tag plus extra calldata words" payload used by the
|
||||
/// single-case probe fixtures.
|
||||
fn probe_calldata(extra_words: &[U256]) -> Vec<u8> {
|
||||
let mut data = vec![0u8; 32 * (1 + extra_words.len())];
|
||||
data[28..32].copy_from_slice(&[0xde, 0xad, 0xbe, 0xef]);
|
||||
for (i, word) in extra_words.iter().enumerate() {
|
||||
let off = 32 * (i + 1);
|
||||
data[off..off + 32].copy_from_slice(&word.to_be_bytes::<32>());
|
||||
}
|
||||
data
|
||||
}
|
||||
|
||||
fn run_probe(path: &str, contract: &str, extra: &[U256]) {
|
||||
let mut actions = instantiate_yul(path, contract);
|
||||
push_call(
|
||||
&mut actions,
|
||||
TestAddress::Instantiated(0),
|
||||
probe_calldata(extra),
|
||||
);
|
||||
run_differential(actions);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn probe_shl_overflow() {
|
||||
run_probe(
|
||||
"contracts/ShlOverflowProbe.yul",
|
||||
"ShlOverflowProbe",
|
||||
&[U256::from(0x123456789abcdef_u64)],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn probe_shr_overflow() {
|
||||
run_probe(
|
||||
"contracts/ShrOverflowProbe.yul",
|
||||
"ShrOverflowProbe",
|
||||
&[U256::from(0x123456789abcdef_u64)],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn probe_sar_overflow() {
|
||||
run_probe("contracts/SarOverflowProbe.yul", "SarOverflowProbe", &[]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn probe_addmod_zero() {
|
||||
run_probe(
|
||||
"contracts/AddModZeroProbe.yul",
|
||||
"AddModZeroProbe",
|
||||
&[U256::from(42), U256::from(99)],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn probe_mulmod_zero() {
|
||||
run_probe(
|
||||
"contracts/MulModZeroProbe.yul",
|
||||
"MulModZeroProbe",
|
||||
&[U256::from(42), U256::from(99)],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn probe_signextend_oob() {
|
||||
run_probe(
|
||||
"contracts/SignExtendOobProbe.yul",
|
||||
"SignExtendOobProbe",
|
||||
&[U256::MAX],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn probe_byte_oob() {
|
||||
run_probe("contracts/ByteOobProbe.yul", "ByteOobProbe", &[U256::MAX]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn probe_exp_zero_zero() {
|
||||
run_probe("contracts/ExpZeroZeroProbe.yul", "ExpZeroZeroProbe", &[]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ext_code_hash() {
|
||||
let mut actions = instantiate("contracts/ExtCode.sol", "ExtCode");
|
||||
|
||||
Reference in new issue
Block a user