Skip to main content

foundry_evm_symbolic/runtime/
evm.rs

1use super::*;
2
3pub(crate) fn failed_slot() -> U256 {
4    let mut bytes = [0u8; 32];
5    bytes[..6].copy_from_slice(b"failed");
6    U256::from_be_bytes(bytes)
7}
8
9pub(crate) fn pow_mod(base: U256, exponent: U256) -> U256 {
10    let mut result = U256::from(1);
11    let mut base = base;
12    let mut exponent = exponent;
13    while !exponent.is_zero() {
14        if exponent & U256::from(1) == U256::from(1) {
15            result = result.wrapping_mul(base);
16        }
17        exponent >>= 1;
18        base = base.wrapping_mul(base);
19    }
20    result
21}
22
23pub(crate) fn exp_expr_for_concrete_exponent(
24    cx: &mut SymCx,
25    base: SymExpr,
26    exponent: usize,
27) -> SymExpr {
28    if exponent == 0 {
29        return SymExpr::one(cx);
30    }
31    if let Some(base) = base.as_const() {
32        return SymExpr::constant(cx, pow_mod(base, U256::from(exponent)));
33    }
34
35    let mut expr = base.clone();
36    for _ in 1..exponent {
37        expr = SymExpr::binop(cx, SymBinOp::Mul, expr, base.clone());
38    }
39    expr
40}
41
42pub(crate) fn slt(left: U256, right: U256) -> bool {
43    let left_negative = (left >> 255) == U256::from(1);
44    let right_negative = (right >> 255) == U256::from(1);
45    match (left_negative, right_negative) {
46        (true, false) => true,
47        (false, true) => false,
48        _ => left < right,
49    }
50}
51
52pub(crate) fn signed_abs(value: U256) -> U256 {
53    if (value >> 255) == U256::from(1) { (!value).wrapping_add(U256::from(1)) } else { value }
54}
55
56pub(crate) fn sdiv(left: U256, right: U256) -> U256 {
57    if right.is_zero() {
58        return U256::ZERO;
59    }
60    let left_negative = (left >> 255) == U256::from(1);
61    let right_negative = (right >> 255) == U256::from(1);
62    let quotient = signed_abs(left) / signed_abs(right);
63    if left_negative ^ right_negative { (!quotient).wrapping_add(U256::from(1)) } else { quotient }
64}
65
66pub(crate) fn smod(left: U256, right: U256) -> U256 {
67    if right.is_zero() {
68        return U256::ZERO;
69    }
70    let left_negative = (left >> 255) == U256::from(1);
71    let remainder = signed_abs(left) % signed_abs(right);
72    if left_negative { (!remainder).wrapping_add(U256::from(1)) } else { remainder }
73}
74
75pub(crate) fn signextend(byte_index: U256, value: U256) -> U256 {
76    if byte_index >= U256::from(32) {
77        return value;
78    }
79    let bit_index = usize::try_from(byte_index).expect("checked byte index") * 8 + 7;
80    let sign_bit = U256::from(1) << bit_index;
81    let mask = sign_bit - U256::from(1);
82    if value & sign_bit == U256::ZERO { value & mask } else { value | !mask }
83}
84
85pub(crate) fn signextend_word(cx: &mut SymCx, byte_index: U256, value: SymExpr) -> SymExpr {
86    if byte_index >= U256::from(32) {
87        return value;
88    }
89    if let Some(value) = value.as_const() {
90        return SymExpr::constant(cx, signextend(byte_index, value));
91    }
92    let bit_index = usize::try_from(byte_index).expect("checked byte index") * 8 + 7;
93    let sign_bit = U256::from(1) << bit_index;
94    let mask_value = sign_bit - U256::from(1);
95    let sign_bit = SymExpr::constant(cx, sign_bit);
96    let masked_sign = SymExpr::binop(cx, SymBinOp::And, value.clone(), sign_bit);
97    let zero = SymExpr::zero(cx);
98    let condition = SymBoolExpr::eq(cx, masked_sign, zero);
99    let inverse_mask = SymExpr::constant(cx, !mask_value);
100    let mask = SymExpr::constant(cx, mask_value);
101    let masked = SymExpr::binop(cx, SymBinOp::And, value.clone(), mask);
102    let extended = SymExpr::binop(cx, SymBinOp::Or, value, inverse_mask);
103    SymExpr::ite(cx, condition, masked, extended)
104}
105
106pub(crate) fn signextend_word_dynamic(
107    cx: &mut SymCx,
108    byte_index: SymExpr,
109    value: SymExpr,
110) -> SymExpr {
111    if let Some(byte_index) = byte_index.as_const() {
112        return signextend_word(cx, byte_index, value);
113    }
114
115    let mut result = value.clone();
116    for idx in (0..31).rev() {
117        let idx_expr = SymExpr::constant(cx, U256::from(idx));
118        let condition = SymBoolExpr::eq(cx, byte_index.clone(), idx_expr);
119        let value = signextend_word(cx, U256::from(idx), value.clone());
120        result = SymExpr::ite(cx, condition, value, result);
121    }
122    result
123}
124
125pub(crate) fn byte_word(cx: &mut SymCx, index: U256, word: SymExpr) -> SymExpr {
126    if index >= U256::from(32) {
127        return SymExpr::zero(cx);
128    }
129    let index = usize::try_from(index).expect("checked byte index");
130    if let Some(word) = word.as_const() {
131        SymExpr::constant(cx, U256::from(word.to_be_bytes::<32>()[index]))
132    } else {
133        byte_expr(cx, index, &word)
134    }
135}
136
137pub(crate) fn byte_word_dynamic(cx: &mut SymCx, index: SymExpr, word: SymExpr) -> SymExpr {
138    if let Some(index) = index.as_const() {
139        return byte_word(cx, index, word);
140    }
141
142    let mut result = SymExpr::zero(cx);
143    if let Some(word) = word.as_const() {
144        let bytes = word.to_be_bytes::<32>();
145        for idx in (0..32).rev() {
146            let idx_expr = SymExpr::constant(cx, U256::from(idx));
147            let condition = SymBoolExpr::eq(cx, index.clone(), idx_expr);
148            let byte = SymExpr::constant(cx, U256::from(bytes[idx]));
149            result = SymExpr::ite(cx, condition, byte, result);
150        }
151    } else {
152        for idx in (0..32).rev() {
153            let idx_expr = SymExpr::constant(cx, U256::from(idx));
154            let condition = SymBoolExpr::eq(cx, index.clone(), idx_expr);
155            let byte = byte_expr(cx, idx, &word);
156            result = SymExpr::ite(cx, condition, byte, result);
157        }
158    }
159    result
160}
161
162/// Returns the byte extraction expression for a symbolic word.
163pub(crate) fn byte_expr(cx: &mut SymCx, index: usize, expr: &SymExpr) -> SymExpr {
164    debug_assert!(index < 32);
165    if let Some(byte) = expr.known_byte(index) {
166        return SymExpr::constant(cx, U256::from(byte));
167    }
168    expr.extracted_byte(cx, index)
169}
170
171pub(crate) fn sar(value: U256, shift: usize) -> U256 {
172    if shift >= 256 {
173        if (value >> 255) == U256::from(1) { U256::MAX } else { U256::ZERO }
174    } else if shift == 0 {
175        value
176    } else if (value >> 255) == U256::from(1) {
177        (value >> shift) | (U256::MAX << (256 - shift))
178    } else {
179        value >> shift
180    }
181}
182
183pub(crate) fn shift_left(cx: &mut SymCx, value: SymExpr, bits: usize) -> SymExpr {
184    if let Some(value) = value.as_const() {
185        SymExpr::constant(cx, value << bits)
186    } else {
187        let bits = SymExpr::constant(cx, U256::from(bits));
188        SymExpr::binop(cx, SymBinOp::Shl, value, bits)
189    }
190}
191
192pub(crate) fn ensure_jumpdest(dest: usize, jumpdests: &JumpTable) -> Result<(), SymbolicError> {
193    if jumpdests.is_valid(dest) { Ok(()) } else { Err(SymbolicError::InvalidJump(dest)) }
194}
195
196pub(crate) fn is_assertion_revert(data: &[u8]) -> bool {
197    is_assert_panic(data) || is_revert_assertion_failure(data)
198}
199
200pub(crate) fn is_assert_panic(data: &[u8]) -> bool {
201    data.len() >= ABI_SELECTOR_PLUS_WORD_LEN
202        && data.starts_with(&PANIC_SELECTOR)
203        && abi_word(&data[4..ABI_SELECTOR_PLUS_WORD_LEN])
204            .is_some_and(|code| code == ASSERT_PANIC_CODE)
205}
206
207pub(crate) fn is_revert_assertion_failure(data: &[u8]) -> bool {
208    if data.len() < ERROR_DATA_MIN_LEN || !data.starts_with(&ERROR_SELECTOR) {
209        return false;
210    }
211
212    let Some(offset) = abi_word_usize(&data[4..ABI_SELECTOR_PLUS_WORD_LEN]) else {
213        return false;
214    };
215    let Some(length_offset) = 4usize.checked_add(offset) else {
216        return false;
217    };
218    let Some(length_end) = length_offset.checked_add(32) else {
219        return false;
220    };
221    if length_end > data.len() {
222        return false;
223    }
224
225    let Some(length) = abi_word_usize(&data[length_offset..length_end]) else {
226        return false;
227    };
228    let Some(message_end) = length_end.checked_add(length) else {
229        return false;
230    };
231    if message_end > data.len() {
232        return false;
233    }
234
235    std::str::from_utf8(&data[length_end..message_end])
236        .is_ok_and(|message| message.contains(ASSERTION_FAILED_PREFIX))
237}
238
239pub(crate) fn abi_word_usize(word: &[u8]) -> Option<usize> {
240    usize::try_from(abi_word(word)?).ok()
241}
242
243pub(crate) const fn abi_word(word: &[u8]) -> Option<U256> {
244    if word.len() != 32 {
245        return None;
246    }
247    let mut bytes = [0u8; 32];
248    bytes.copy_from_slice(word);
249    Some(U256::from_be_bytes(bytes))
250}