foundry_evm_symbolic/runtime/
evm.rs1use 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
162pub(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}