mirror of
https://github.com/pezkuwichain/revive.git
synced 2026-10-20 18:56:17 +00:00
Fix __ulongrem stdlib edge case (#531)
The implementation is off-by-one. --------- Signed-off-by: xermicus <cyrill@parity.io>
This commit is contained in:
1 parent
c67b95805c
commit
ae8033130b
5 files changed
+107
-4
No files matched your search
@@ -18,6 +18,7 @@
|
||||
- SREM over the two constant operands `type(int256).min` and `-1`.
|
||||
- At least one of the two constants needs folding (i.e. not just `type(int256).min % -1`),
|
||||
and is written specifically such that solc doesn't fold but LLVM does, resulting in UB (signed-overflow).
|
||||
- Fix `mulmod` returning out-of-range result for large moduli.
|
||||
|
||||
## v1.1.0
|
||||
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
|
||||
pragma solidity ^0.8;
|
||||
|
||||
/* runner.json
|
||||
{
|
||||
"differential": true,
|
||||
"actions": [
|
||||
{
|
||||
"Upload": {
|
||||
"code": {
|
||||
"Solidity": {
|
||||
"contract": "UlongRem"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"Instantiate": {
|
||||
"code": {
|
||||
"Solidity": {
|
||||
"contract": "UlongRemTester"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"VerifyCall": {
|
||||
"success": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
*/
|
||||
|
||||
/// Exercises the stdlib `__ulongrem` Knuth-D helper via mulmod's slow path
|
||||
/// (modulus >= 2^128). Inputs come over calldata to defeat solc folding.
|
||||
contract UlongRem {
|
||||
function bigMulMod(uint256 a, uint256 b, uint256 m) external pure returns (uint256) {
|
||||
return mulmod(a, b, m);
|
||||
}
|
||||
}
|
||||
|
||||
contract UlongRemTester {
|
||||
constructor() {
|
||||
UlongRem c = new UlongRem();
|
||||
|
||||
// PoC from the bug report. Drives the second-iteration trial-quotient
|
||||
// candidate to exactly 2^128 - 1, the boundary the off-by-one loop bound
|
||||
// in `__ulongrem` mishandled. Failure mode (pre-fix): PVM returns
|
||||
// `correct + m`.
|
||||
uint256 a = 1 << 159;
|
||||
uint256 b = type(uint256).max - (1 << 54);
|
||||
uint256 m = (1 << 255) + 1;
|
||||
uint256 r = c.bigMulMod(a, b, m);
|
||||
assert(r < m);
|
||||
}
|
||||
}
|
||||
@@ -353,6 +353,13 @@ sol!(
|
||||
);
|
||||
case!("Bitwise.sol", Bitwise, opByteCall, bitwise_byte, index: U256, value: U256);
|
||||
|
||||
sol!(
|
||||
contract UlongRem {
|
||||
function bigMulMod(uint a, uint b, uint m) external pure returns (uint);
|
||||
}
|
||||
);
|
||||
case!("UlongRem.sol", UlongRem, bigMulModCall, ulongrem_big_mulmod, a: U256, b: U256, m: U256);
|
||||
|
||||
sol!(
|
||||
contract Storage {
|
||||
function transient(uint value) public returns (uint ret);
|
||||
|
||||
@@ -66,6 +66,7 @@ test_spec!(function_type, "FunctionType", "FunctionType.sol");
|
||||
test_spec!(layout_at, "LayoutAt", "LayoutAt.sol");
|
||||
test_spec!(shift_arithmetic_right, "SAR", "SAR.sol");
|
||||
test_spec!(add_mod_mul_mod, "AddModMulModTester", "AddModMulMod.sol");
|
||||
test_spec!(ulongrem, "UlongRemTester", "UlongRem.sol");
|
||||
test_spec!(memory_bounds, "MemoryBounds", "MemoryBounds.sol");
|
||||
test_spec!(selfdestruct, "Selfdestruct", "Selfdestruct.sol");
|
||||
test_spec!(clz, "CountLeadingZeros", "CountLeadingZeros.sol");
|
||||
@@ -104,6 +105,42 @@ fn run_differential(actions: Vec<SpecsAction>) {
|
||||
.run();
|
||||
}
|
||||
|
||||
/// Rust-driven fuzz against the stdlib `__ulongrem` slow path. Each iteration
|
||||
/// computes (a, b, m) deterministically from the index and dispatches a single
|
||||
/// `bigMulMod` call via the differential runner — there is no in-contract loop,
|
||||
/// so PVM never sees more than one mulmod per call dispatch. Any divergence
|
||||
/// shows up as a returndata mismatch on the specific failing iteration.
|
||||
#[test]
|
||||
fn ulongrem_fuzz() {
|
||||
use alloy_primitives::keccak256;
|
||||
|
||||
let mut actions = instantiate("contracts/UlongRem.sol", "UlongRem");
|
||||
|
||||
for i in 0u64..256 {
|
||||
let derive = |tag: &[u8]| -> U256 {
|
||||
let mut buf = Vec::with_capacity(8 + tag.len());
|
||||
buf.extend_from_slice(&i.to_be_bytes());
|
||||
buf.extend_from_slice(tag);
|
||||
U256::from_be_bytes::<32>(keccak256(&buf).0)
|
||||
};
|
||||
let a = derive(b"a");
|
||||
let b = derive(b"b");
|
||||
// Force modulus into the slow path (m >= 2^255).
|
||||
let m = derive(b"m") | (U256::from(1u64) << 255);
|
||||
|
||||
actions.push(Call {
|
||||
origin: TestAddress::Alice,
|
||||
dest: TestAddress::Instantiated(0),
|
||||
value: 0,
|
||||
gas_limit: None,
|
||||
storage_deposit_limit: None,
|
||||
data: Contract::ulongrem_big_mulmod(a, b, m).calldata,
|
||||
});
|
||||
}
|
||||
|
||||
run_differential(actions);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bitwise_byte() {
|
||||
let mut actions = instantiate("contracts/Bitwise.sol", "Bitwise");
|
||||
|
||||
@@ -104,7 +104,7 @@ define private i256 @__ulongrem(i256 %0, i256 %1, i256 %2) #0 {
|
||||
19:
|
||||
%.056 = phi i256 [ %15, %13 ], [ %25, %.critedge ]
|
||||
%.055 = phi i256 [ %16, %13 ], [ %26, %.critedge ]
|
||||
%.not62 = icmp ult i256 %.056, 340282366920938463463374607431768211455
|
||||
%.not62 = icmp ult i256 %.056, 340282366920938463463374607431768211456
|
||||
br i1 %.not62, label %20, label %.critedge
|
||||
|
||||
20:
|
||||
@@ -117,7 +117,7 @@ define private i256 @__ulongrem(i256 %0, i256 %1, i256 %2) #0 {
|
||||
.critedge:
|
||||
%25 = add i256 %.056, -1
|
||||
%26 = add i256 %.055, %14
|
||||
%.not65 = icmp ult i256 %26, 340282366920938463463374607431768211455
|
||||
%.not65 = icmp ult i256 %26, 340282366920938463463374607431768211456
|
||||
br i1 %.not65, label %19, label %27
|
||||
|
||||
27:
|
||||
@@ -135,7 +135,7 @@ define private i256 @__ulongrem(i256 %0, i256 %1, i256 %2) #0 {
|
||||
36:
|
||||
%.2 = phi i256 [ %33, %27 ], [ %42, %.critedge1 ]
|
||||
%.1 = phi i256 [ %34, %27 ], [ %43, %.critedge1 ]
|
||||
%.not63 = icmp ult i256 %.2, 340282366920938463463374607431768211455
|
||||
%.not63 = icmp ult i256 %.2, 340282366920938463463374607431768211456
|
||||
br i1 %.not63, label %37, label %.critedge1
|
||||
|
||||
37:
|
||||
@@ -148,7 +148,7 @@ define private i256 @__ulongrem(i256 %0, i256 %1, i256 %2) #0 {
|
||||
.critedge1:
|
||||
%42 = add i256 %.2, -1
|
||||
%43 = add i256 %.1, %14
|
||||
%.not64 = icmp ult i256 %43, 340282366920938463463374607431768211455
|
||||
%.not64 = icmp ult i256 %43, 340282366920938463463374607431768211456
|
||||
br i1 %.not64, label %36, label %44
|
||||
|
||||
44:
|
||||
|
||||
Reference in new issue
Block a user