1use super::{hashcons::HashConsed, *};
2
3pub(crate) fn keccak_word(cx: &mut SymCx, bytes: Vec<SymExpr>) -> SymExpr {
4 let len = bytes.len();
5 let len = SymExpr::constant(cx, U256::from(len));
6 keccak_word_with_len(cx, bytes, len)
7}
8
9pub(crate) fn keccak_word_with_len(cx: &mut SymCx, bytes: Vec<SymExpr>, len: SymExpr) -> SymExpr {
10 if let Some(len) = len.as_const()
11 && let Ok(len) = usize::try_from(len)
12 && len <= bytes.len()
13 && let Ok(concrete) = concrete_expr_bytes(&bytes[..len], "symbolic keccak input")
14 {
15 let hash = U256::from_be_bytes(keccak256(concrete).0);
16 if len == 64 {
17 cx.record_concrete_keccak_preimage(hash, bytes[..len].to_vec().into());
18 }
19 return SymExpr::constant(cx, hash);
20 }
21
22 let exprs = bytes;
23 let name = stable_symbol(cx, "keccak", format!("{len:?}:{exprs:?}").as_bytes());
24 SymExpr::keccak_symbol(cx, name, len, exprs)
25}
26
27pub(crate) fn symbolic_hash_word_with_len(
28 cx: &mut SymCx,
29 algorithm: &'static str,
30 bytes: Vec<SymExpr>,
31 len: SymExpr,
32) -> SymExpr {
33 let exprs = bytes;
34 let name = stable_symbol(cx, algorithm, format!("{len:?}:{exprs:?}").as_bytes());
35 let mut identity = Vec::with_capacity(exprs.len() + 1);
36 identity.push(len);
37 identity.extend(exprs);
38 SymExpr::hash_symbol(cx, name, algorithm, identity)
39}
40
41pub(crate) fn create2_address_word(
42 cx: &mut SymCx,
43 state: &mut PathState,
44 creator: Address,
45 salt: SymExpr,
46 initcode: &SymCode,
47) -> Result<(SymExpr, Address), SymbolicError> {
48 match (salt.as_const(), initcode.concrete_bytes(cx, "symbolic CREATE2 initcode")) {
49 (Some(salt), Ok(initcode)) => {
50 let address = creator.create2_from_code(salt.to_be_bytes::<32>(), &initcode);
51 Ok((SymExpr::constant(cx, address_word(address)), address))
52 }
53 (None, Ok(initcode)) => {
54 let initcode_hash = keccak256(&initcode);
55 let word = symbolic_create2_address_word(
56 cx,
57 state,
58 format!("{creator:?}"),
59 salt,
60 format!("{initcode_hash:?}"),
61 );
62 let address = state.world.symbolic_address_slot(word.clone());
63 Ok((word, address))
64 }
65 (_, Err(SymbolicError::Unsupported("symbolic CREATE2 initcode"))) => {
66 let initcode_bytes = initcode.read_byte_exprs(cx, 0, initcode.len());
67 let word = symbolic_create2_address_word(
68 cx,
69 state,
70 format!("{creator:?}"),
71 salt,
72 format!("{initcode_bytes:?}"),
73 );
74 let address = state.world.symbolic_address_slot(word.clone());
75 Ok((word, address))
76 }
77 (_, Err(err)) => Err(err),
78 }
79}
80
81pub(crate) fn compute_create2_address_word(
82 cx: &mut SymCx,
83 state: &mut PathState,
84 deployer: SymExpr,
85 salt: SymExpr,
86 init_code_hash: SymExpr,
87) -> Result<SymExpr, SymbolicError> {
88 let deployer_concrete = state.constrained_word(cx, &deployer).map(word_to_address);
89 let salt_concrete = state.constrained_word(cx, &salt);
90 let init_code_hash_concrete = state.constrained_word(cx, &init_code_hash);
91
92 if let (Some(deployer), Some(salt), Some(init_code_hash)) =
93 (deployer_concrete, salt_concrete, init_code_hash_concrete)
94 {
95 let init_code_hash = B256::from(init_code_hash.to_be_bytes::<32>());
96 let address = deployer.create2(B256::from(salt.to_be_bytes::<32>()), init_code_hash);
97 return Ok(SymExpr::constant(cx, address_word(address)));
98 }
99
100 let deployer_identity = deployer_concrete
101 .map(|deployer| format!("{deployer:?}"))
102 .unwrap_or_else(|| format!("{deployer:?}"));
103 let init_code_hash_identity = init_code_hash_concrete
104 .map(|init_code_hash| {
105 let init_code_hash = B256::from(init_code_hash.to_be_bytes::<32>());
106 format!("{init_code_hash:?}")
107 })
108 .unwrap_or_else(|| format!("{init_code_hash:?}"));
109
110 Ok(symbolic_create2_address_word(cx, state, deployer_identity, salt, init_code_hash_identity))
111}
112
113pub(crate) fn compute_create_address_word(
114 cx: &mut SymCx,
115 state: &mut PathState,
116 deployer: SymExpr,
117 nonce: SymExpr,
118) -> Result<SymExpr, SymbolicError> {
119 let deployer_concrete = state.constrained_word(cx, &deployer).map(word_to_address);
120 let nonce_concrete = state.constrained_word(cx, &nonce);
121
122 if let (Some(deployer), Some(nonce)) = (deployer_concrete, nonce_concrete) {
123 let Ok(nonce) = u64::try_from(nonce) else {
124 return Err(SymbolicError::Unsupported("symbolic vm.computeCreateAddress nonce"));
125 };
126 return Ok(SymExpr::constant(cx, address_word(deployer.create(nonce))));
127 }
128
129 let deployer_identity = deployer_concrete
130 .map(|deployer| format!("{deployer:?}"))
131 .unwrap_or_else(|| format!("{deployer:?}"));
132 Ok(symbolic_create_address_word(cx, state, deployer_identity, nonce))
133}
134
135pub(crate) fn symbolic_create_address_word(
136 cx: &mut SymCx,
137 state: &mut PathState,
138 creator_identity: String,
139 nonce: SymExpr,
140) -> SymExpr {
141 let name =
142 stable_symbol(cx, "create_address", format!("{creator_identity}:{nonce:?}").as_bytes());
143 let word = SymExpr::get_var(cx, name);
144 state.constraints.push(SymBoolExpr::cmp_word_const(cx, SymCmpOp::Ult, &word, U256::ONE << 160));
145 word
146}
147
148pub(crate) fn symbolic_create2_address_word(
149 cx: &mut SymCx,
150 state: &mut PathState,
151 creator_identity: String,
152 salt: SymExpr,
153 initcode_identity: String,
154) -> SymExpr {
155 let name = stable_symbol(
156 cx,
157 "create2_address",
158 format!("{creator_identity}:{salt:?}:{initcode_identity}").as_bytes(),
159 );
160 let word = SymExpr::get_var(cx, name);
161 state.constraints.push(SymBoolExpr::cmp_word_const(cx, SymCmpOp::Ult, &word, U256::ONE << 160));
162 word
163}
164
165impl SymExpr {
166 pub(crate) fn select_storage_write(
167 self,
168 cx: &mut SymCx,
169 write_key: Self,
170 write_value: Self,
171 base: Self,
172 ) -> Self {
173 if write_value == base {
174 return base;
175 }
176 let condition = self.storage_key_eq(cx, &write_key);
177 match condition.as_const() {
178 Some(true) => write_value,
179 Some(false) => base,
180 None => Self::ite(cx, condition, write_value, base),
181 }
182 }
183
184 pub(crate) fn storage_key_eq(&self, cx: &mut SymCx, write_key: &Self) -> SymBoolExpr {
185 if let (Some(read_root), Some(write_root)) =
186 (self.storage_mapping_root_slot(cx), write_key.storage_mapping_root_slot(cx))
187 && read_root != write_root
188 {
189 return SymBoolExpr::constant(cx, false);
190 }
191 match (self.storage_layout_key(cx), write_key.storage_layout_key(cx)) {
192 (Some((read_base, read_offset)), Some((write_base, write_offset))) => {
193 let read_base = read_base
194 .storage_base_eq(cx, &write_base)
195 .unwrap_or_else(|| SymBoolExpr::eq(cx, read_base, write_base));
196 let read_offset = SymBoolExpr::eq(cx, read_offset, write_offset);
197 SymBoolExpr::and(cx, vec![read_base, read_offset])
198 }
199 (Some(_), None) if write_key.as_const().is_some() => SymBoolExpr::constant(cx, false),
200 (None, Some(_)) if self.as_const().is_some() => SymBoolExpr::constant(cx, false),
201 _ => SymBoolExpr::eq(cx, self.clone(), write_key.clone()),
202 }
203 }
204
205 fn storage_base_eq(&self, cx: &mut SymCx, other: &Self) -> Option<SymBoolExpr> {
206 let read = self.storage_mapping_key(cx)?;
207 let write = other.storage_mapping_key(cx)?;
208
209 let key_eq = storage_mapping_key_eq(cx, &read, &write);
210 let slot_eq = read
211 .slot
212 .storage_base_eq(cx, &write.slot)
213 .unwrap_or_else(|| SymBoolExpr::eq(cx, read.slot, write.slot));
214 Some(SymBoolExpr::and(cx, vec![key_eq, slot_eq]))
215 }
216
217 fn storage_mapping_key(&self, cx: &mut SymCx) -> Option<StorageMappingKey> {
218 let bytes = self.storage_mapping_key_bytes(cx)?;
219 let key_bytes = &bytes[..32];
220 let preserve_key_bytes =
221 (!storage_mapping_key_bytes_form_compact_word(key_bytes)).then(|| key_bytes.to_vec());
222 let key = Self::from_bytes(cx, key_bytes.iter().cloned());
223 let slot = Self::from_bytes(cx, bytes[32..64].iter().cloned());
224 Some(StorageMappingKey { key, key_bytes: preserve_key_bytes, slot })
225 }
226
227 fn storage_mapping_root_slot(&self, cx: &mut SymCx) -> Option<U256> {
228 let bytes = self.storage_mapping_key_bytes(cx)?;
229 let slot = Self::from_bytes(cx, bytes[32..64].iter().cloned());
230 match slot.kind() {
231 SymExprKind::Const(value) if cx.concrete_keccak_preimage(*value).is_some() => {
232 slot.storage_mapping_root_slot(cx)
233 }
234 SymExprKind::Const(slot) => Some(*slot),
235 SymExprKind::Keccak { .. } => slot.storage_mapping_root_slot(cx),
236 _ => None,
237 }
238 }
239
240 fn storage_mapping_key_bytes(&self, cx: &SymCx) -> Option<Arc<[Self]>> {
241 match self.kind() {
242 SymExprKind::Keccak { len, bytes, .. }
243 if len.as_const() == Some(U256::from(64)) && bytes.len() >= 64 =>
244 {
245 Some(bytes.clone())
246 }
247 SymExprKind::Const(hash) => cx.concrete_keccak_preimage(*hash),
248 _ => None,
249 }
250 }
251
252 fn storage_layout_key(&self, cx: &mut SymCx) -> Option<(Self, Self)> {
253 match self.kind() {
254 SymExprKind::Keccak { .. } => Some((self.clone(), Self::zero(cx))),
255 SymExprKind::Const(hash) if cx.concrete_keccak_preimage(*hash).is_some() => {
256 Some((self.clone(), Self::zero(cx)))
257 }
258 SymExprKind::BinOp(SymBinOp::Add, left, right) => {
259 if let Some((base, offset)) = left.storage_layout_key(cx)
260 && !right.contains_keccak()
261 {
262 let offset = Self::binop(cx, SymBinOp::Add, offset, right.clone());
263 return Some((base, offset));
264 }
265 if let Some((base, offset)) = right.storage_layout_key(cx)
266 && !left.contains_keccak()
267 {
268 let offset = Self::binop(cx, SymBinOp::Add, offset, left.clone());
269 return Some((base, offset));
270 }
271 None
272 }
273 _ => None,
274 }
275 }
276}
277
278struct StorageMappingKey {
279 key: SymExpr,
280 key_bytes: Option<Vec<SymExpr>>,
281 slot: SymExpr,
282}
283
284fn storage_mapping_key_eq(
285 cx: &mut SymCx,
286 read: &StorageMappingKey,
287 write: &StorageMappingKey,
288) -> SymBoolExpr {
289 if read.key_bytes.is_some() || write.key_bytes.is_some() {
290 let read_owned;
291 let read_bytes = if let Some(bytes) = read.key_bytes.as_deref() {
292 bytes
293 } else {
294 read_owned = read.key.clone().into_byte_exprs(cx);
295 &read_owned
296 };
297 let write_owned;
298 let write_bytes = if let Some(bytes) = write.key_bytes.as_deref() {
299 bytes
300 } else {
301 write_owned = write.key.clone().into_byte_exprs(cx);
302 &write_owned
303 };
304 let byte_equalities = read_bytes
305 .iter()
306 .zip(write_bytes)
307 .map(|(read, write)| {
308 let read = read.byte_term(cx, 31).unwrap_or_else(|| read.clone().low_byte(cx));
309 let write = write.byte_term(cx, 31).unwrap_or_else(|| write.clone().low_byte(cx));
310 SymBoolExpr::eq(cx, read, write)
311 })
312 .collect();
313 SymBoolExpr::and(cx, byte_equalities)
314 } else {
315 SymBoolExpr::eq(cx, read.key.clone(), write.key.clone())
316 }
317}
318
319fn storage_mapping_key_bytes_form_compact_word(bytes: &[SymExpr]) -> bool {
320 bytes.iter().all(|byte| byte.as_const().is_some()) || word_from_extracted_bytes(bytes).is_some()
321}
322
323fn masked_expr_matches(candidate: &SymExprKind, target: &SymExpr) -> Option<U256> {
324 match candidate {
325 SymExprKind::BinOp(SymBinOp::And, left, right) if left == target => right.eval(),
326 SymExprKind::BinOp(SymBinOp::And, left, right) if right == target => left.eval(),
327 _ => None,
328 }
329}
330
331fn context_forces_masked_expr(context: &[SymBoolExpr], target: &SymExpr, mask: U256) -> bool {
332 context.iter().any(|condition| match condition.kind() {
333 SymBoolExprKind::Cmp(SymCmpOp::Eq, left, right) => {
334 (left == target && masked_expr_matches(right.kind(), target) == Some(mask))
335 || (right == target && masked_expr_matches(left.kind(), target) == Some(mask))
336 }
337 SymBoolExprKind::And(values) => context_forces_masked_expr(values, target, mask),
338 _ => false,
339 })
340}
341
342pub(crate) fn concrete_expr_bytes(
343 bytes: &[SymExpr],
344 reason: &'static str,
345) -> Result<Vec<u8>, SymbolicError> {
346 bytes
347 .iter()
348 .map(|byte| match byte.as_const() {
349 Some(value) => Ok(value.to::<u8>()),
350 None => Err(SymbolicError::Unsupported(reason)),
351 })
352 .collect()
353}
354
355pub(crate) fn mask_low_bits(mask: U256) -> Option<usize> {
356 let bits = mask.bit_len();
357 (mask == mask_bits(U256::MAX, bits)).then_some(bits)
358}
359
360fn power_of_two_shift(value: U256) -> Option<usize> {
361 if value <= U256::ONE || !value.is_power_of_two() {
362 return None;
363 }
364 Some(value.bit_len() - 1)
365}
366
367pub(in crate::runtime::expr) fn low_masked_source(expr: &SymExpr, bits: usize) -> Option<&SymExpr> {
368 match expr.kind() {
369 SymExprKind::BinOp(SymBinOp::And, left, right)
371 if right.as_const().and_then(mask_low_bits) == Some(bits) =>
372 {
373 Some(left)
374 }
375 _ => None,
376 }
377}
378
379pub(in crate::runtime::expr) fn low_masked_source_any(expr: &SymExpr) -> Option<&SymExpr> {
380 match expr.kind() {
381 SymExprKind::BinOp(SymBinOp::And, left, right)
383 if right.as_const().and_then(mask_low_bits).is_some() =>
384 {
385 Some(left)
386 }
387 _ => None,
388 }
389}
390
391fn word_from_extracted_bytes(bytes: &[SymExpr]) -> Option<SymExpr> {
392 if bytes.len() < 32 {
393 return None;
394 }
395
396 let source = bytes
397 .iter()
398 .take(32)
399 .enumerate()
400 .find_map(|(idx, byte)| byte.extracted_byte_source(idx))?;
401
402 for (idx, byte) in bytes.iter().take(32).enumerate() {
403 if let Some(byte_source) = byte.extracted_byte_source(idx) {
404 if byte_source != source {
405 return None;
406 }
407 continue;
408 }
409
410 let byte = byte.as_const()?;
411 if source.known_byte(idx) != Some(byte.to::<u8>()) {
412 return None;
413 }
414 }
415 Some(source)
416}
417
418#[derive(Clone, PartialEq, Eq, Hash)]
419pub(crate) struct SymExpr {
420 pub(in crate::runtime::expr) kind: HashConsed<SymExprKind>,
421}
422
423impl fmt::Debug for SymExpr {
424 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
425 self.kind().fmt(f)
426 }
427}
428
429#[derive(Clone, Debug, PartialEq, Eq, Hash)]
430pub(in crate::runtime) enum SymExprKind {
431 Const(U256),
432 Var(Symbol),
433 GasLeft(Symbol),
434 Keccak { name: Symbol, len: SymExpr, bytes: Arc<[SymExpr]> },
435 Hash { name: Symbol, algorithm: &'static str, bytes: Arc<[SymExpr]> },
436 Not(SymExpr),
437 BinOp(SymBinOp, SymExpr, SymExpr),
438 TernOp(SymTernOp, SymExpr, SymExpr, SymExpr),
439 Ite(SymBoolExpr, SymExpr, SymExpr),
440}
441
442impl SymExprKind {
443 pub(in crate::runtime) const fn get_var(&self) -> Option<Symbol> {
444 match self {
445 Self::Var(symbol)
446 | Self::GasLeft(symbol)
447 | Self::Keccak { name: symbol, .. }
448 | Self::Hash { name: symbol, .. } => Some(*symbol),
449 _ => None,
450 }
451 }
452
453 pub(in crate::runtime) const fn get_eval_var(&self) -> Option<Symbol> {
454 match self {
455 Self::Var(symbol) | Self::GasLeft(symbol) | Self::Hash { name: symbol, .. } => {
456 Some(*symbol)
457 }
458 _ => None,
459 }
460 }
461}
462
463impl SymExpr {
464 pub(in crate::runtime) fn kind(&self) -> &SymExprKind {
465 self.kind.value()
466 }
467
468 #[cfg(test)]
469 pub(crate) fn get_var_name<'a>(&self, cx: &'a SymCx) -> Option<&'a str> {
470 self.kind().get_var().map(|symbol| cx.symbol_name(symbol))
471 }
472
473 #[cfg(test)]
474 pub(crate) fn is_keccak(&self) -> bool {
475 matches!(self.kind(), SymExprKind::Keccak { .. })
476 }
477
478 #[cfg(test)]
479 pub(crate) fn keccak_len_and_byte_count(&self) -> Option<(&Self, usize)> {
480 match self.kind() {
481 SymExprKind::Keccak { len, bytes, .. } => Some((len, bytes.len())),
482 _ => None,
483 }
484 }
485
486 #[cfg(test)]
487 pub(crate) fn hash_algorithm(&self) -> Option<&'static str> {
488 match self.kind() {
489 SymExprKind::Hash { algorithm, .. } => Some(algorithm),
490 _ => None,
491 }
492 }
493
494 pub(in crate::runtime) fn into_kind(self) -> SymExprKind {
495 self.kind.into_value()
496 }
497
498 pub(in crate::runtime) fn from_kind(cx: &mut SymCx, kind: SymExprKind) -> Self {
499 cx.mk_expr_kind(kind)
500 }
501
502 pub(crate) fn zero(cx: &mut SymCx) -> Self {
503 Self::constant(cx, U256::ZERO)
504 }
505
506 pub(crate) fn one(cx: &mut SymCx) -> Self {
507 Self::constant(cx, U256::ONE)
508 }
509
510 pub(crate) fn constant(cx: &mut SymCx, value: U256) -> Self {
511 if value.is_zero() {
512 return cx.cached_zero();
513 }
514 if value == U256::ONE {
515 return cx.cached_one();
516 }
517 Self::from_kind(cx, SymExprKind::Const(value))
518 }
519
520 pub(crate) fn var(cx: &mut SymCx, name: &str) -> Self {
521 let symbol = cx.intern(name);
522 Self::get_var(cx, symbol)
523 }
524
525 pub(crate) fn get_var(cx: &mut SymCx, symbol: Symbol) -> Self {
526 Self::from_kind(cx, SymExprKind::Var(symbol))
527 }
528
529 pub(crate) fn gas_left(cx: &mut SymCx, id: usize) -> Self {
530 let symbol = cx.intern(&format!("gasleft_{id}"));
531 Self::from_kind(cx, SymExprKind::GasLeft(symbol))
532 }
533
534 pub(crate) fn not(cx: &mut SymCx, value: Self) -> Self {
535 match value.kind() {
536 SymExprKind::Const(value) => Self::constant(cx, !*value),
537 SymExprKind::Not(value) => value.clone(),
538 _ => Self::from_kind(cx, SymExprKind::Not(value)),
539 }
540 }
541
542 pub(crate) fn binop(cx: &mut SymCx, binop: SymBinOp, left: Self, right: Self) -> Self {
543 match binop {
544 SymBinOp::Add => match (left.kind(), right.kind()) {
545 (SymExprKind::Const(left_value), SymExprKind::Const(right_value)) => {
546 Self::constant(cx, binop.eval(*left_value, *right_value))
548 }
549 (SymExprKind::Const(value), _) if value.is_zero() => right,
551 (_, SymExprKind::Const(value)) if value.is_zero() => left,
553 _ => Self::commutative_binop(cx, binop, left, right),
554 },
555 SymBinOp::Sub => match (left.kind(), right.kind()) {
556 (SymExprKind::Const(left_value), SymExprKind::Const(right_value)) => {
557 Self::constant(cx, binop.eval(*left_value, *right_value))
559 }
560 (_, SymExprKind::Const(value)) if value.is_zero() => left,
562 _ if left == right => Self::zero(cx),
564 _ => Self::from_kind(cx, SymExprKind::BinOp(binop, left, right)),
565 },
566 SymBinOp::Mul => match (left.kind(), right.kind()) {
567 (SymExprKind::Const(left_value), SymExprKind::Const(right_value)) => {
568 Self::constant(cx, binop.eval(*left_value, *right_value))
570 }
571 (SymExprKind::Const(value), _) | (_, SymExprKind::Const(value))
573 if value.is_zero() =>
574 {
575 Self::zero(cx)
576 }
577 (SymExprKind::Const(value), _) if *value == U256::ONE => right,
579 (_, SymExprKind::Const(value)) if *value == U256::ONE => left,
581 (SymExprKind::Const(value), _) if let Some(shift) = power_of_two_shift(*value) => {
583 let shift = Self::constant(cx, U256::from(shift));
584 Self::binop(cx, SymBinOp::Shl, right, shift)
585 }
586 (_, SymExprKind::Const(value)) if let Some(shift) = power_of_two_shift(*value) => {
588 let shift = Self::constant(cx, U256::from(shift));
589 Self::binop(cx, SymBinOp::Shl, left, shift)
590 }
591 _ => Self::commutative_binop(cx, binop, left, right),
592 },
593 SymBinOp::UDiv | SymBinOp::SDiv => match (left.kind(), right.kind()) {
594 (SymExprKind::Const(left_value), SymExprKind::Const(right_value)) => {
595 Self::constant(cx, binop.eval(*left_value, *right_value))
597 }
598 (_, SymExprKind::Const(value)) if value.is_zero() => Self::zero(cx),
600 (_, SymExprKind::Const(value)) if *value == U256::ONE => left,
602 (
604 SymExprKind::BinOp(SymBinOp::Sub, value, low_bits),
605 SymExprKind::Const(divisor),
606 ) if binop == SymBinOp::UDiv
607 && let Some(shift) = power_of_two_shift(*divisor)
608 && low_masked_source(low_bits, shift) == Some(value) =>
609 {
610 let shift = Self::constant(cx, U256::from(shift));
611 Self::binop(cx, SymBinOp::Shr, value.clone(), shift)
612 }
613 (_, SymExprKind::Const(divisor))
615 if binop == SymBinOp::UDiv
616 && let Some(shift) = power_of_two_shift(*divisor) =>
617 {
618 let shift = Self::constant(cx, U256::from(shift));
619 Self::binop(cx, SymBinOp::Shr, left, shift)
620 }
621 _ => Self::from_kind(cx, SymExprKind::BinOp(binop, left, right)),
622 },
623 SymBinOp::URem | SymBinOp::SRem => match (left.kind(), right.kind()) {
624 (SymExprKind::Const(left_value), SymExprKind::Const(right_value)) => {
625 Self::constant(cx, binop.eval(*left_value, *right_value))
627 }
628 (_, SymExprKind::Const(value)) if value.is_zero() => Self::zero(cx),
630 (_, SymExprKind::Const(value)) if *value == U256::ONE => Self::zero(cx),
632 (_, SymExprKind::Const(divisor))
634 if binop == SymBinOp::URem
635 && let Some(bits) = power_of_two_shift(*divisor) =>
636 {
637 Self::and_const(cx, left, mask_bits(U256::MAX, bits))
638 }
639 _ => Self::from_kind(cx, SymExprKind::BinOp(binop, left, right)),
640 },
641 SymBinOp::And => match (left.kind(), right.kind()) {
642 (SymExprKind::Const(left_value), SymExprKind::Const(right_value)) => {
643 Self::constant(cx, binop.eval(*left_value, *right_value))
645 }
646 (SymExprKind::Const(value), _) | (_, SymExprKind::Const(value))
648 if value.is_zero() =>
649 {
650 Self::zero(cx)
651 }
652 (SymExprKind::Const(value), _) if *value == U256::MAX => right,
654 (_, SymExprKind::Const(value)) if *value == U256::MAX => left,
656 _ if left == right => left,
658 (SymExprKind::Const(mask), _) => Self::and_const(cx, right, *mask),
659 (_, SymExprKind::Const(mask)) => Self::and_const(cx, left, *mask),
660 _ => Self::commutative_binop(cx, binop, left, right),
661 },
662 SymBinOp::Or => match (left.kind(), right.kind()) {
663 (SymExprKind::Const(left_value), SymExprKind::Const(right_value)) => {
664 Self::constant(cx, binop.eval(*left_value, *right_value))
666 }
667 (SymExprKind::Const(value), _) if value.is_zero() => right,
669 (_, SymExprKind::Const(value)) if value.is_zero() => left,
671 _ if left == right => left,
673 _ => Self::or(cx, left, right),
674 },
675 SymBinOp::Xor => match (left.kind(), right.kind()) {
676 (SymExprKind::Const(left_value), SymExprKind::Const(right_value)) => {
677 Self::constant(cx, binop.eval(*left_value, *right_value))
679 }
680 (SymExprKind::Const(value), _) if value.is_zero() => right,
682 (_, SymExprKind::Const(value)) if value.is_zero() => left,
684 _ if left == right => Self::zero(cx),
686 _ => Self::commutative_binop(cx, binop, left, right),
687 },
688 SymBinOp::Shl => match (left.kind(), right.kind()) {
689 (SymExprKind::Const(left_value), SymExprKind::Const(right_value)) => {
690 Self::constant(cx, binop.eval(*left_value, *right_value))
692 }
693 (_, SymExprKind::Const(value)) if value.is_zero() => left,
695 (SymExprKind::Const(value), _) if value.is_zero() => Self::zero(cx),
697 (_, SymExprKind::Const(value)) if *value >= U256::from(256) => Self::zero(cx),
699 _ => Self::from_kind(cx, SymExprKind::BinOp(binop, left, right)),
700 },
701 SymBinOp::Shr => match (left.kind(), right.kind()) {
702 (SymExprKind::Const(left_value), SymExprKind::Const(right_value)) => {
703 Self::constant(cx, binop.eval(*left_value, *right_value))
705 }
706 (_, SymExprKind::Const(value)) if value.is_zero() => left,
708 (SymExprKind::Const(value), _) if value.is_zero() => Self::zero(cx),
710 (_, SymExprKind::Const(value)) => Self::shr_const(cx, left, *value),
711 _ => Self::from_kind(cx, SymExprKind::BinOp(binop, left, right)),
712 },
713 SymBinOp::Sar => match (left.kind(), right.kind()) {
714 (SymExprKind::Const(left_value), SymExprKind::Const(right_value)) => {
715 Self::constant(cx, binop.eval(*left_value, *right_value))
717 }
718 (_, SymExprKind::Const(value)) if value.is_zero() => left,
720 _ => Self::from_kind(cx, SymExprKind::BinOp(binop, left, right)),
721 },
722 }
723 }
724
725 pub(crate) fn ternop(
726 cx: &mut SymCx,
727 ternop: SymTernOp,
728 left: Self,
729 right: Self,
730 modulus: Self,
731 ) -> Self {
732 match (left.kind(), right.kind(), modulus.kind()) {
733 (_, _, SymExprKind::Const(modulus)) if modulus.is_zero() || *modulus == U256::ONE => {
734 Self::zero(cx)
736 }
737 (SymExprKind::Const(left), SymExprKind::Const(right), SymExprKind::Const(modulus)) => {
738 Self::constant(cx, ternop.eval(*left, *right, *modulus))
740 }
741 (_, _, SymExprKind::Const(modulus))
743 if let Some(bits) = power_of_two_shift(*modulus) =>
744 {
745 let binop = match ternop {
746 SymTernOp::AddMod => SymBinOp::Add,
747 SymTernOp::MulMod => SymBinOp::Mul,
748 };
749 let value = Self::binop(cx, binop, left, right);
750 Self::and_const(cx, value, mask_bits(U256::MAX, bits))
751 }
752 _ => {
753 let (left, right) = Self::ordered_commutative_operands(left, right);
755 Self::from_kind(cx, SymExprKind::TernOp(ternop, left, right, modulus))
756 }
757 }
758 }
759
760 pub(crate) fn ite(
761 cx: &mut SymCx,
762 condition: SymBoolExpr,
763 then_expr: Self,
764 else_expr: Self,
765 ) -> Self {
766 match condition.as_const() {
767 Some(true) => then_expr,
769 Some(false) => else_expr,
771 None if then_expr == else_expr => then_expr,
773 None if then_expr.as_const().is_some_and(|value| value.is_zero())
775 && Self::self_div_expr_matches_zero_check(&condition, &else_expr) =>
776 {
777 let condition = condition.not(cx);
778 Self::bool_word(cx, condition)
779 }
780 None if then_expr.as_const() == Some(U256::ONE)
782 && else_expr.bool_word_condition().as_ref() == Some(&condition) =>
783 {
784 else_expr
785 }
786 None if else_expr.as_const().is_some_and(|value| value.is_zero())
788 && then_expr.bool_word_condition().as_ref() == Some(&condition) =>
789 {
790 then_expr
791 }
792 None => Self::from_kind(cx, SymExprKind::Ite(condition, then_expr, else_expr)),
793 }
794 }
795
796 pub(crate) fn bool_word(cx: &mut SymCx, value: SymBoolExpr) -> Self {
797 let one = Self::one(cx);
798 let zero = Self::zero(cx);
799 Self::ite(cx, value, one, zero)
800 }
801
802 fn self_div_expr_matches_zero_check(cond: &SymBoolExpr, expr: &Self) -> bool {
803 let Some(zero_operand) = cond.zero_check_operand() else { return false };
804 let Some((numerator, denominator)) = expr.udiv_operands() else { return false };
805 numerator == zero_operand && denominator == zero_operand
806 }
807
808 pub(crate) fn keccak_symbol(cx: &mut SymCx, name: Symbol, len: Self, bytes: Vec<Self>) -> Self {
809 Self::from_kind(cx, SymExprKind::Keccak { name, len, bytes: bytes.into() })
810 }
811
812 pub(crate) fn hash_symbol(
813 cx: &mut SymCx,
814 name: Symbol,
815 algorithm: &'static str,
816 bytes: Vec<Self>,
817 ) -> Self {
818 Self::from_kind(cx, SymExprKind::Hash { name, algorithm, bytes: bytes.into() })
819 }
820
821 fn or(cx: &mut SymCx, left: Self, right: Self) -> Self {
822 if let Some(rebuilt) = Self::rebuild_from_or_terms(&left, &right) {
823 return rebuilt;
825 }
826 Self::commutative_binop(cx, SymBinOp::Or, left, right)
827 }
828
829 fn commutative_binop(cx: &mut SymCx, op: SymBinOp, left: Self, right: Self) -> Self {
830 let (left, right) = Self::ordered_commutative_operands(left, right);
832 Self::from_kind(cx, SymExprKind::BinOp(op, left, right))
833 }
834
835 pub(in crate::runtime::expr) fn ordered_commutative_operands(
836 left: Self,
837 right: Self,
838 ) -> (Self, Self) {
839 match left.complexity().cmp(&right.complexity()) {
840 std::cmp::Ordering::Less => (right, left),
842 std::cmp::Ordering::Greater => (left, right),
843 std::cmp::Ordering::Equal if right.kind.stable_hash_cmp(&left.kind).is_lt() => {
844 (right, left)
845 }
846 std::cmp::Ordering::Equal => (left, right),
847 }
848 }
849
850 fn complexity(&self) -> usize {
851 match self.kind() {
852 SymExprKind::Const(_) => 0,
853 SymExprKind::Not(_) => 1,
854 SymExprKind::BinOp(..) => 2,
855 SymExprKind::TernOp(..) => 3,
856 _ => 4,
857 }
858 }
859
860 fn and_const(cx: &mut SymCx, expr: Self, mask: U256) -> Self {
861 if mask.is_zero() {
862 return Self::zero(cx);
864 }
865 if mask == U256::MAX {
866 return expr;
868 }
869
870 match expr.kind() {
871 SymExprKind::Const(value) => Self::constant(cx, *value & mask),
873 SymExprKind::BinOp(SymBinOp::Or, left, right) => {
874 let left = Self::and_const(cx, left.clone(), mask);
876 let right = Self::and_const(cx, right.clone(), mask);
877 Self::binop(cx, SymBinOp::Or, left, right)
878 }
879 SymExprKind::BinOp(SymBinOp::Shl, _, shift)
880 if mask_low_bits(mask).is_some_and(|bits| {
881 shift
882 .as_const()
883 .and_then(|shift| usize::try_from(shift).ok())
884 .is_some_and(|shift| bits <= shift)
885 }) =>
886 {
887 Self::zero(cx)
889 }
890 SymExprKind::BinOp(SymBinOp::And, left, right) => {
891 if right.as_const() == Some(mask) {
892 Self::and_const(cx, left.clone(), mask)
894 } else if left == right {
895 Self::and_const(cx, left.clone(), mask)
897 } else {
898 let mask = Self::constant(cx, mask);
899 Self::from_kind(cx, SymExprKind::BinOp(SymBinOp::And, expr, mask))
900 }
901 }
902 _ => {
903 let mask = Self::constant(cx, mask);
904 Self::from_kind(cx, SymExprKind::BinOp(SymBinOp::And, expr, mask))
905 }
906 }
907 }
908
909 fn shr_const(cx: &mut SymCx, expr: Self, shift: U256) -> Self {
910 if shift.is_zero() {
911 return expr;
913 }
914 if shift >= U256::from(256) {
915 return Self::zero(cx);
917 }
918
919 let shift = usize::try_from(shift).expect("shift is less than 256");
920 if expr.unsigned_bits() <= shift {
921 return Self::zero(cx);
923 }
924
925 if let SymExprKind::BinOp(SymBinOp::Shl, inner, left_shift) = expr.kind()
926 && left_shift.as_const() == Some(U256::from(shift))
927 && inner.unsigned_bits() <= 256 - shift
928 {
929 return inner.clone();
931 }
932
933 if let SymExprKind::BinOp(SymBinOp::Or, left, right) = expr.kind() {
934 let left = Self::shr_const(cx, left.clone(), U256::from(shift));
937 let right = Self::shr_const(cx, right.clone(), U256::from(shift));
938 if left.as_const().is_some_and(|value| value.is_zero()) {
939 return right;
940 }
941 if right.as_const().is_some_and(|value| value.is_zero()) {
942 return left;
943 }
944 }
945
946 let shift = Self::constant(cx, U256::from(shift));
947 Self::from_kind(cx, SymExprKind::BinOp(SymBinOp::Shr, expr, shift))
948 }
949
950 fn rebuild_from_or_terms(left: &Self, right: &Self) -> Option<Self> {
951 let mut terms = Vec::new();
952 left.push_or_terms(&mut terms);
953 right.push_or_terms(&mut terms);
954 Self::rebuild_from_extracted_byte_terms(&terms)
955 .or_else(|| Self::rebuild_from_shifted_word_fragments(&terms))
956 }
957
958 pub(in crate::runtime) fn push_or_terms<'a>(&'a self, terms: &mut Vec<&'a Self>) {
959 match self.kind() {
960 SymExprKind::BinOp(SymBinOp::Or, left, right) => {
961 left.push_or_terms(terms);
962 right.push_or_terms(terms);
963 }
964 _ => terms.push(self),
965 }
966 }
967
968 fn rebuild_from_extracted_byte_terms(terms: &[&Self]) -> Option<Self> {
969 if terms.len() <= 1 {
970 return None;
971 }
972
973 let mut source = None;
974 let mut seen = [false; 32];
975 for term in terms {
976 if term.as_const().is_some_and(|value| value.is_zero()) {
977 continue;
978 }
979 let (term_source, index) = term.extracted_shifted_byte_term()?;
980 match &source {
981 Some(source) if source != &term_source => return None,
982 Some(_) => {}
983 None => source = Some(term_source),
984 }
985 seen[index] = true;
986 }
987
988 let source = source?;
989 for (index, seen) in seen.into_iter().enumerate() {
990 if !seen && source.known_byte(index) != Some(0) {
991 return None;
992 }
993 }
994 Some(source)
995 }
996
997 fn extracted_shifted_byte_term(&self) -> Option<(Self, usize)> {
998 match self.kind() {
999 SymExprKind::BinOp(SymBinOp::Shl, byte, shift) => {
1000 let shift = shift.as_const()?;
1001 let Ok(shift) = usize::try_from(shift) else { return None };
1002 if shift % 8 != 0 || shift > 248 {
1003 return None;
1004 }
1005 let index = 31 - shift / 8;
1006 let source = byte.extracted_unshifted_byte_source(index)?;
1007 Some((source, index))
1008 }
1009 _ => self.extracted_unshifted_byte_source(31).map(|source| (source, 31)),
1010 }
1011 }
1012
1013 fn extracted_unshifted_byte_source(&self, index: usize) -> Option<Self> {
1014 let expr = self.strip_low_byte_mask();
1015 if index == 31 {
1016 return Some(expr.clone());
1017 }
1018 let SymExprKind::BinOp(SymBinOp::Shr, source, shift) = expr.kind() else { return None };
1019 let shift = shift.as_const()?;
1020 (shift == U256::from((31 - index) * 8)).then(|| source.clone())
1021 }
1022
1023 fn rebuild_from_shifted_word_fragments(terms: &[&Self]) -> Option<Self> {
1024 if terms.len() != 2 {
1025 return None;
1026 }
1027
1028 let left_low = terms[0].low_word_fragment();
1029 let right_low = terms[1].low_word_fragment();
1030 let left_high = terms[0].shifted_high_word_fragment();
1031 let right_high = terms[1].shifted_high_word_fragment();
1032 match (left_low, right_low, left_high, right_high) {
1033 (Some((low_source, low_bits)), None, None, Some((high_source, high_bits)))
1034 | (None, Some((low_source, low_bits)), Some((high_source, high_bits)), None)
1035 if low_source == high_source && low_bits == high_bits =>
1036 {
1037 Some(low_source)
1038 }
1039 _ => None,
1040 }
1041 }
1042
1043 fn low_word_fragment(&self) -> Option<(Self, usize)> {
1044 let SymExprKind::BinOp(SymBinOp::And, left, right) = self.kind() else { return None };
1045 let mask = right.as_const()?;
1046 mask_low_bits(mask).map(|bits| (left.clone(), bits))
1047 }
1048
1049 fn shifted_high_word_fragment(&self) -> Option<(Self, usize)> {
1050 let SymExprKind::BinOp(SymBinOp::Shl, value, shift) = self.kind() else { return None };
1051 let bits = shift.as_const().and_then(|shift| usize::try_from(shift).ok())?;
1052 if bits == 0 || bits >= 256 {
1053 return None;
1054 }
1055
1056 let (source, source_shift, width) = value.shifted_low_fragment_source()?;
1057 (source_shift == bits && width == 256 - bits).then_some((source, bits))
1058 }
1059
1060 fn shifted_low_fragment_source(&self) -> Option<(Self, usize, usize)> {
1061 let SymExprKind::BinOp(SymBinOp::And, left, right) = self.kind() else { return None };
1062 let mask = right.as_const()?;
1063 Self::shifted_low_fragment_source_with_mask(left, mask)
1064 }
1065
1066 fn shifted_low_fragment_source_with_mask(
1067 value: &Self,
1068 mask: U256,
1069 ) -> Option<(Self, usize, usize)> {
1070 let width = mask_low_bits(mask)?;
1071 match value.kind() {
1072 SymExprKind::BinOp(SymBinOp::Shr, source, shift) => {
1073 let shift = shift.as_const().and_then(|shift| usize::try_from(shift).ok())?;
1074 Some((source.clone(), shift, width))
1075 }
1076 _ => Some((value.clone(), 0, width)),
1077 }
1078 }
1079
1080 pub(crate) fn low_byte(self, cx: &mut SymCx) -> Self {
1081 if let Some(word) = self.as_const() {
1082 return Self::constant(cx, U256::from(word.to::<u8>()));
1083 }
1084 let mask = Self::constant(cx, U256::from(0xff));
1085 Self::binop(cx, SymBinOp::And, self, mask)
1086 }
1087
1088 pub(crate) fn into_byte_exprs(self, cx: &mut SymCx) -> Vec<Self> {
1089 SymBytes::word(cx, self).materialize(cx)
1090 }
1091
1092 pub(crate) fn into_bytes(self, cx: &mut SymCx) -> SymBytes {
1093 SymBytes::word(cx, self)
1094 }
1095
1096 pub(crate) fn from_bytes(cx: &mut SymCx, bytes: impl IntoIterator<Item = Self>) -> Self {
1097 let bytes = bytes.into_iter().collect::<Vec<_>>();
1098 if let Ok(concrete) = concrete_expr_bytes(&bytes, "symbolic word bytes") {
1099 let mut word = [0u8; 32];
1100 for (idx, byte) in concrete.into_iter().take(32).enumerate() {
1101 word[idx] = byte;
1102 }
1103 return Self::constant(cx, U256::from_be_bytes(word));
1104 }
1105
1106 if let Some(expr) = word_from_extracted_bytes(&bytes) {
1107 return expr;
1108 }
1109
1110 let mut expr = Self::zero(cx);
1111 for (idx, byte) in bytes.into_iter().take(32).enumerate() {
1112 let shift = (31 - idx) * 8;
1113 let byte = byte.low_byte(cx);
1114 let byte = if shift == 0 {
1115 byte
1116 } else {
1117 let shift = Self::constant(cx, U256::from(shift));
1118 Self::binop(cx, SymBinOp::Shl, byte, shift)
1119 };
1120 expr = Self::binop(cx, SymBinOp::Or, expr, byte);
1121 }
1122 expr
1123 }
1124
1125 pub(crate) fn as_const(&self) -> Option<U256> {
1126 match self.kind() {
1127 SymExprKind::Const(value) => Some(*value),
1128 _ => None,
1129 }
1130 }
1131
1132 pub(crate) fn eval(&self) -> Option<U256> {
1133 self.eval_model_if_complete(&NoopModel).ok().flatten()
1134 }
1135
1136 pub(crate) fn eval_model<M: SymbolicModelLookup + ?Sized>(
1137 &self,
1138 model: &M,
1139 ) -> Result<U256, SymbolicError> {
1140 let kind = self.kind();
1141 if let Some(var) = kind.get_eval_var() {
1142 return Ok(model.value(var).unwrap_or_default());
1143 }
1144 Ok(match kind {
1145 SymExprKind::Const(value) => *value,
1146 SymExprKind::Var(_) | SymExprKind::GasLeft(_) | SymExprKind::Hash { .. } => {
1147 unreachable!("symbolic eval leaf handled above")
1148 }
1149 SymExprKind::Keccak { len, bytes, .. } => {
1150 let len = len.eval_model(model)?;
1151 let Ok(len) = usize::try_from(len) else {
1152 return Err(SymbolicError::Solver(
1153 "solver model uses an invalid keccak length".to_string(),
1154 ));
1155 };
1156 if len > bytes.len() {
1157 return Err(SymbolicError::Solver(
1158 "solver model uses an invalid keccak length".to_string(),
1159 ));
1160 }
1161
1162 let mut input = Vec::with_capacity(len);
1163 for byte in bytes.iter().take(len) {
1164 input.push((byte.eval_model(model)? & U256::from(0xff)).to::<u8>());
1165 }
1166
1167 U256::from_be_bytes(keccak256(input).0)
1168 }
1169 SymExprKind::Not(value) => !value.eval_model(model)?,
1170 SymExprKind::BinOp(op, left, right) => {
1171 op.eval(left.eval_model(model)?, right.eval_model(model)?)
1172 }
1173 SymExprKind::TernOp(op, left, right, modulus) => op.eval(
1174 left.eval_model(model)?,
1175 right.eval_model(model)?,
1176 modulus.eval_model(model)?,
1177 ),
1178 SymExprKind::Ite(cond, then_expr, else_expr) => {
1179 if cond.eval_model(model)? {
1180 then_expr.eval_model(model)?
1181 } else {
1182 else_expr.eval_model(model)?
1183 }
1184 }
1185 })
1186 }
1187
1188 pub(crate) fn eval_model_if_complete<M: SymbolicModelLookup + ?Sized>(
1189 &self,
1190 model: &M,
1191 ) -> Result<Option<U256>, SymbolicError> {
1192 let mut vars = SymbolicVars::default();
1193 self.collect_eval_vars(&mut vars);
1194 if vars.iter().copied().all(|var| model.contains_name(var)) {
1195 self.eval_model(model).map(Some)
1196 } else {
1197 Ok(None)
1198 }
1199 }
1200
1201 pub(crate) fn assign_model_value(&self, model: &mut SymbolicModel, value: U256) -> bool {
1202 match self.kind() {
1203 SymExprKind::Const(existing) => *existing == value,
1204 SymExprKind::Var(var) => {
1205 if let Some(existing) = model.get(var) {
1206 *existing == value
1207 } else {
1208 model.insert(*var, value);
1209 true
1210 }
1211 }
1212 SymExprKind::GasLeft(symbol) => {
1213 if let Some(existing) = model.get(symbol) {
1214 *existing == value
1215 } else {
1216 model.insert(*symbol, value);
1217 true
1218 }
1219 }
1220 _ => false,
1221 }
1222 }
1223
1224 pub(crate) fn bool_word_condition(&self) -> Option<SymBoolExpr> {
1225 let SymExprKind::Ite(condition, then_expr, else_expr) = self.kind() else {
1226 return None;
1227 };
1228 Self::bool_word_condition_from_parts(condition, then_expr, else_expr)
1229 }
1230
1231 fn bool_word_condition_from_parts(
1232 condition: &SymBoolExpr,
1233 then_expr: &Self,
1234 else_expr: &Self,
1235 ) -> Option<SymBoolExpr> {
1236 match (then_expr.as_const(), else_expr.as_const()) {
1237 (Some(then_value), Some(else_value))
1238 if then_value == U256::ONE && else_value.is_zero() =>
1239 {
1240 Some(condition.clone())
1241 }
1242 (Some(then_value), Some(else_value))
1243 if then_value.is_zero() && else_value == U256::ONE =>
1244 {
1245 None
1246 }
1247 _ => None,
1248 }
1249 }
1250
1251 pub(crate) fn truth(&self) -> Option<bool> {
1252 self.as_const().map(|value| !value.is_zero())
1253 }
1254
1255 pub(crate) fn into_zero_bool(self, cx: &mut SymCx) -> SymBoolExpr {
1256 match self.kind() {
1257 SymExprKind::Const(value) => SymBoolExpr::constant(cx, value.is_zero()),
1258 SymExprKind::Ite(condition, then_expr, else_expr) => {
1259 match Self::bool_word_condition_from_parts(condition, then_expr, else_expr) {
1260 Some(condition) => SymBoolExpr::not_bool(cx, condition),
1261 None => {
1262 let zero = Self::zero(cx);
1263 SymBoolExpr::eq(cx, self, zero)
1264 }
1265 }
1266 }
1267 _ => {
1268 let zero = Self::zero(cx);
1269 SymBoolExpr::eq(cx, self, zero)
1270 }
1271 }
1272 }
1273
1274 pub(crate) fn nonzero_bool(self, cx: &mut SymCx) -> SymBoolExpr {
1275 let zero = self.into_zero_bool(cx);
1276 SymBoolExpr::not_bool(cx, zero)
1277 }
1278
1279 pub(crate) fn as_const_or(&self, reason: &'static str) -> Result<U256, SymbolicError> {
1280 self.as_const().ok_or(SymbolicError::Unsupported(reason))
1281 }
1282
1283 pub(crate) fn as_usize_or(&self, reason: &'static str) -> Result<usize, SymbolicError> {
1284 let value = self.as_const_or(reason)?;
1285 usize::try_from(value).map_err(|_| SymbolicError::Unsupported(reason))
1286 }
1287
1288 pub(crate) fn contains_keccak(&self) -> bool {
1289 self.visit_bool(|expr| matches!(expr.kind(), SymExprKind::Keccak { .. }))
1290 }
1291
1292 pub(crate) fn contains_gasleft(&self) -> bool {
1293 self.visit_bool(|expr| matches!(expr.kind(), SymExprKind::GasLeft(_)))
1294 }
1295
1296 pub(crate) fn contains_udiv(&self) -> bool {
1297 self.visit_bool(|expr| matches!(expr.kind(), SymExprKind::BinOp(SymBinOp::UDiv, _, _)))
1298 }
1299
1300 pub(crate) fn contains_ite(&self) -> bool {
1301 self.visit_bool(|expr| matches!(expr.kind(), SymExprKind::Ite(_, _, _)))
1302 }
1303
1304 pub(in crate::runtime) fn udiv_operands(&self) -> Option<(&Self, &Self)> {
1305 match self.kind() {
1306 SymExprKind::BinOp(SymBinOp::UDiv, numerator, denominator) => {
1307 Some((numerator, denominator))
1308 }
1309 _ => None,
1310 }
1311 }
1312
1313 pub(crate) fn collect_eval_vars(&self, vars: &mut SymbolicVars) {
1314 let _ = self.visit(&mut |expr| {
1315 if let Some(var) = expr.kind().get_eval_var() {
1316 vars.insert(var);
1317 }
1318 ControlFlow::<()>::Continue(())
1319 });
1320 }
1321
1322 pub(crate) fn known_byte(&self, index: usize) -> Option<u8> {
1323 debug_assert!(index < 32);
1324 match self.kind() {
1325 SymExprKind::Const(value) => Some(value.to_be_bytes::<32>()[index]),
1326 SymExprKind::Var(_)
1327 | SymExprKind::GasLeft(_)
1328 | SymExprKind::Keccak { .. }
1329 | SymExprKind::Hash { .. } => None,
1330 SymExprKind::Not(value) => value.known_byte(index).map(|byte| !byte),
1331 SymExprKind::Ite(_, then_expr, else_expr) => {
1332 let then_byte = then_expr.known_byte(index)?;
1333 let else_byte = else_expr.known_byte(index)?;
1334 (then_byte == else_byte).then_some(then_byte)
1335 }
1336 SymExprKind::BinOp(op, left, right) => match op {
1337 SymBinOp::And => match (left.known_byte(index), right.known_byte(index)) {
1338 (Some(left), Some(right)) => Some(left & right),
1339 (Some(0), _) | (_, Some(0)) => Some(0),
1340 _ => None,
1341 },
1342 SymBinOp::Or => Some(left.known_byte(index)? | right.known_byte(index)?),
1343 SymBinOp::Xor => Some(left.known_byte(index)? ^ right.known_byte(index)?),
1344 SymBinOp::Shl => {
1345 let shift = right.as_const()?;
1346 if shift >= U256::from(256) {
1347 return Some(0);
1348 }
1349 let shift = usize::try_from(shift).expect("checked byte shift");
1350 if shift % 8 != 0 {
1351 return None;
1352 }
1353 let source_index = index + shift / 8;
1354 if source_index >= 32 { Some(0) } else { left.known_byte(source_index) }
1355 }
1356 SymBinOp::Shr => {
1357 let shift = right.as_const()?;
1358 if shift >= U256::from(256) {
1359 return Some(0);
1360 }
1361 let shift = usize::try_from(shift).expect("checked byte shift");
1362 if shift % 8 != 0 {
1363 return None;
1364 }
1365 let byte_shift = shift / 8;
1366 if index < byte_shift { Some(0) } else { left.known_byte(index - byte_shift) }
1367 }
1368 SymBinOp::Add
1369 | SymBinOp::Sub
1370 | SymBinOp::Mul
1371 | SymBinOp::UDiv
1372 | SymBinOp::URem
1373 | SymBinOp::SDiv
1374 | SymBinOp::SRem
1375 | SymBinOp::Sar => None,
1376 },
1377 SymExprKind::TernOp(_, _, _, _) => None,
1378 }
1379 }
1380
1381 pub(crate) fn known_word(&self) -> Option<U256> {
1382 let mut word = [0u8; 32];
1383 for (idx, byte) in word.iter_mut().enumerate() {
1384 *byte = self.known_byte(idx)?;
1385 }
1386 Some(U256::from_be_bytes(word))
1387 }
1388
1389 pub(crate) fn unsigned_bits(&self) -> usize {
1390 match self.kind() {
1391 SymExprKind::Const(value) => value.bit_len().max(1),
1392 SymExprKind::BinOp(SymBinOp::And, left, right) => {
1393 if let Some(mask) = right.as_const() {
1394 left.unsigned_bits().min(mask.bit_len())
1395 } else {
1396 256
1397 }
1398 }
1399 SymExprKind::BinOp(SymBinOp::Add, left, right) => {
1400 left.unsigned_bits().max(right.unsigned_bits()).saturating_add(1).min(256)
1401 }
1402 SymExprKind::BinOp(SymBinOp::Mul, left, right) => {
1403 left.unsigned_bits().saturating_add(right.unsigned_bits()).min(256)
1404 }
1405 SymExprKind::BinOp(SymBinOp::Shl, left, right) => {
1406 if let Some(shift) = right.as_const().and_then(|shift| usize::try_from(shift).ok())
1407 {
1408 left.unsigned_bits().saturating_add(shift).min(256)
1409 } else {
1410 256
1411 }
1412 }
1413 SymExprKind::BinOp(SymBinOp::Shr, left, right) => {
1414 if let Some(shift) = right.as_const().and_then(|shift| usize::try_from(shift).ok())
1415 {
1416 left.unsigned_bits().saturating_sub(shift).max(1)
1417 } else {
1418 256
1419 }
1420 }
1421 SymExprKind::BinOp(SymBinOp::UDiv, left, _) => left.unsigned_bits(),
1422 SymExprKind::TernOp(_, _, _, modulus) => modulus.unsigned_bits(),
1423 SymExprKind::Ite(_, left, right) => left.unsigned_bits().max(right.unsigned_bits()),
1424 _ => 256,
1425 }
1426 }
1427
1428 pub(crate) fn extracted_byte(&self, cx: &mut SymCx, index: usize) -> Self {
1429 debug_assert!(index < 32);
1430 let shift = Self::constant(cx, U256::from((31 - index) * 8));
1431 let shifted = Self::binop(cx, SymBinOp::Shr, self.clone(), shift);
1432 let mask = Self::constant(cx, U256::from(0xff));
1433 Self::binop(cx, SymBinOp::And, shifted, mask)
1434 }
1435
1436 pub(crate) fn extracted_byte_source(&self, index: usize) -> Option<Self> {
1437 let expr = self.strip_low_byte_mask();
1438 if index == 31 {
1439 return Some(expr.clone());
1440 }
1441 let SymExprKind::BinOp(SymBinOp::Shr, source, shift) = expr.kind() else { return None };
1442 let shift = shift.as_const()?;
1443 (shift == U256::from((31 - index) * 8)).then(|| source.clone())
1444 }
1445
1446 pub(crate) fn strip_low_byte_mask(&self) -> &Self {
1447 match self.kind() {
1448 SymExprKind::BinOp(SymBinOp::And, left, right)
1449 if right.as_const() == Some(U256::from(0xff)) =>
1450 {
1451 left.strip_low_byte_mask()
1452 }
1453 _ => self,
1454 }
1455 }
1456
1457 pub(crate) fn byte_term(&self, cx: &mut SymCx, index: usize) -> Option<Self> {
1458 debug_assert!(index < 32);
1459
1460 match self.kind() {
1461 SymExprKind::Const(value) => {
1462 Some(Self::constant(cx, U256::from(value.to_be_bytes::<32>()[index])))
1463 }
1464 SymExprKind::Var(_)
1465 | SymExprKind::GasLeft(_)
1466 | SymExprKind::Keccak { .. }
1467 | SymExprKind::Hash { .. } => Some(self.extracted_byte(cx, index)),
1468 SymExprKind::Not(value) => {
1469 let value = value.byte_term(cx, index)?;
1470 Some(Self::not(cx, value))
1471 }
1472 SymExprKind::Ite(cond, then_expr, else_expr) => {
1473 let then_expr = then_expr.byte_term(cx, index)?;
1474 let else_expr = else_expr.byte_term(cx, index)?;
1475 Some(Self::ite(cx, cond.clone(), then_expr, else_expr))
1476 }
1477 SymExprKind::BinOp(op, left, right) => match op {
1478 SymBinOp::And => Self::binary_byte_term(
1479 cx,
1480 left,
1481 right,
1482 index,
1483 SymBinOp::And,
1484 |byte| byte == 0xff,
1485 |byte| byte == 0,
1486 ),
1487 SymBinOp::Or => Self::binary_byte_term(
1488 cx,
1489 left,
1490 right,
1491 index,
1492 SymBinOp::Or,
1493 |byte| byte == 0,
1494 |_| false,
1495 ),
1496 SymBinOp::Xor => Self::binary_byte_term(
1497 cx,
1498 left,
1499 right,
1500 index,
1501 SymBinOp::Xor,
1502 |byte| byte == 0,
1503 |_| false,
1504 ),
1505 SymBinOp::Shl => {
1506 let shift = right.eval()?;
1507 if shift >= U256::from(256) {
1508 return Some(Self::zero(cx));
1509 }
1510 let shift = usize::try_from(shift).expect("checked byte shift");
1511 if shift % 8 != 0 {
1512 return None;
1513 }
1514 let source_index = index + shift / 8;
1515 if source_index >= 32 {
1516 Some(Self::zero(cx))
1517 } else {
1518 left.byte_term(cx, source_index)
1519 }
1520 }
1521 SymBinOp::Shr => {
1522 let shift = right.eval()?;
1523 if shift >= U256::from(256) {
1524 return Some(Self::zero(cx));
1525 }
1526 let shift = usize::try_from(shift).expect("checked byte shift");
1527 if shift % 8 != 0 {
1528 return None;
1529 }
1530 let byte_shift = shift / 8;
1531 if index < byte_shift {
1532 Some(Self::zero(cx))
1533 } else {
1534 left.byte_term(cx, index - byte_shift)
1535 }
1536 }
1537 SymBinOp::Add
1538 | SymBinOp::Sub
1539 | SymBinOp::Mul
1540 | SymBinOp::UDiv
1541 | SymBinOp::URem
1542 | SymBinOp::SDiv
1543 | SymBinOp::SRem
1544 | SymBinOp::Sar => None,
1545 },
1546 SymExprKind::TernOp(_, _, _, _) => None,
1547 }
1548 }
1549
1550 fn binary_byte_term(
1551 cx: &mut SymCx,
1552 left: &Self,
1553 right: &Self,
1554 index: usize,
1555 op: SymBinOp,
1556 identity: impl Fn(u8) -> bool,
1557 absorbing: impl Fn(u8) -> bool,
1558 ) -> Option<Self> {
1559 let left = left.byte_term(cx, index)?;
1560 let right = right.byte_term(cx, index)?;
1561 match (left.byte_const(), right.byte_const()) {
1562 (Some(left), _) if absorbing(left) => Some(Self::constant(cx, U256::from(left))),
1563 (_, Some(right)) if absorbing(right) => Some(Self::constant(cx, U256::from(right))),
1564 (Some(left), _) if identity(left) => Some(right),
1565 (_, Some(right)) if identity(right) => Some(left),
1566 _ => Some(Self::binop(cx, op, left, right)),
1567 }
1568 }
1569
1570 pub(crate) fn byte_const(&self) -> Option<u8> {
1571 self.as_const().map(|value| value.to::<u8>())
1572 }
1573
1574 pub(crate) fn equality_forces_const(
1575 &self,
1576 value: U256,
1577 expr: &Self,
1578 context: &[SymBoolExpr],
1579 ) -> Option<U256> {
1580 if self == expr {
1581 return Some(value);
1582 }
1583 self.equality_forces_const_inner(value, expr, context)
1584 }
1585
1586 fn equality_forces_const_inner(
1587 &self,
1588 value: U256,
1589 expr: &Self,
1590 context: &[SymBoolExpr],
1591 ) -> Option<U256> {
1592 let mask = masked_expr_matches(self.kind(), expr)?;
1593 if value & !mask != U256::ZERO || !context_forces_masked_expr(context, expr, mask) {
1594 return None;
1595 }
1596 Some(value)
1597 }
1598
1599 pub(crate) fn nonzero_forces_const(
1600 &self,
1601 target: &Self,
1602 context: &[SymBoolExpr],
1603 ) -> Option<U256> {
1604 match self.kind() {
1605 SymExprKind::Const(_)
1606 | SymExprKind::Var(_)
1607 | SymExprKind::GasLeft(_)
1608 | SymExprKind::Keccak { .. }
1609 | SymExprKind::Hash { .. }
1610 | SymExprKind::Not(_) => None,
1611 SymExprKind::Ite(cond, then_expr, else_expr) => {
1612 if then_expr.eval().is_some_and(|value| !value.is_zero())
1613 && else_expr.eval().is_some_and(|value| value.is_zero())
1614 {
1615 cond.forces_expr_const_with_context(target, context)
1616 } else {
1617 None
1618 }
1619 }
1620 SymExprKind::BinOp(SymBinOp::Or, left, right) => {
1621 if left.eval().is_some_and(|value| value.is_zero()) {
1622 return right.nonzero_forces_const(target, context);
1623 }
1624 if right.eval().is_some_and(|value| value.is_zero()) {
1625 return left.nonzero_forces_const(target, context);
1626 }
1627 None
1628 }
1629 SymExprKind::BinOp(SymBinOp::And, left, right) => {
1630 if left.eval().is_some_and(|value| !value.is_zero()) {
1631 return right.nonzero_forces_const(target, context);
1632 }
1633 if right.eval().is_some_and(|value| !value.is_zero()) {
1634 return left.nonzero_forces_const(target, context);
1635 }
1636 None
1637 }
1638 SymExprKind::BinOp(SymBinOp::Shl | SymBinOp::Shr, value, shift)
1639 if shift.eval().is_some_and(|shift| shift.is_zero()) =>
1640 {
1641 value.nonzero_forces_const(target, context)
1642 }
1643 SymExprKind::TernOp(_, _, _, _) => None,
1644 SymExprKind::BinOp(_, _, _) => None,
1645 }
1646 }
1647
1648 pub(crate) fn is_raw_gasleft(&self) -> bool {
1649 matches!(self.kind(), SymExprKind::GasLeft(_))
1650 }
1651
1652 pub(crate) fn add_const(cx: &mut SymCx, expr: Self, value: U256) -> Self {
1653 if value.is_zero() {
1654 return expr;
1655 }
1656 match expr.kind() {
1657 SymExprKind::Const(expr) => Self::constant(cx, expr.wrapping_add(value)),
1658 _ => {
1659 let value = Self::constant(cx, value);
1660 Self::binop(cx, SymBinOp::Add, expr, value)
1661 }
1662 }
1663 }
1664
1665 pub(crate) fn visit<B>(
1667 &self,
1668 visitor: &mut impl FnMut(&Self) -> ControlFlow<B>,
1669 ) -> ControlFlow<B> {
1670 visitor(self)?;
1671 match self.kind() {
1672 SymExprKind::Const(_) | SymExprKind::Var(_) | SymExprKind::GasLeft(_) => {}
1673 SymExprKind::Keccak { len, bytes, .. } => {
1674 len.visit(visitor)?;
1675 for byte in bytes.iter() {
1676 byte.visit(visitor)?;
1677 }
1678 }
1679 SymExprKind::Hash { bytes, .. } => {
1680 for byte in bytes.iter() {
1681 byte.visit(visitor)?;
1682 }
1683 }
1684 SymExprKind::Not(value) => value.visit(visitor)?,
1685 SymExprKind::BinOp(_, left, right) => {
1686 left.visit(visitor)?;
1687 right.visit(visitor)?;
1688 }
1689 SymExprKind::TernOp(_, left, right, modulus) => {
1690 left.visit(visitor)?;
1691 right.visit(visitor)?;
1692 modulus.visit(visitor)?;
1693 }
1694 SymExprKind::Ite(cond, left, right) => {
1695 cond.visit_exprs(visitor)?;
1696 left.visit(visitor)?;
1697 right.visit(visitor)?;
1698 }
1699 }
1700 ControlFlow::Continue(())
1701 }
1702
1703 pub(crate) fn visit_bool(&self, mut visitor: impl FnMut(&Self) -> bool) -> bool {
1704 self.visit(&mut |expr| {
1705 if visitor(expr) { ControlFlow::Break(()) } else { ControlFlow::Continue(()) }
1706 })
1707 .is_break()
1708 }
1709
1710 pub(crate) fn fold(
1711 self,
1712 cx: &mut SymCx,
1713 folder: &mut impl FnMut(&mut SymCx, Self) -> Self,
1714 ) -> Self {
1715 if matches!(
1716 self.kind(),
1717 SymExprKind::Const(_) | SymExprKind::Var(_) | SymExprKind::GasLeft(_)
1718 ) {
1719 return folder(cx, self);
1720 }
1721
1722 let expr = match self.into_kind() {
1723 SymExprKind::Keccak { name, len, bytes } => {
1724 let len = len.fold(cx, folder);
1725 let bytes = bytes.iter().cloned().map(|byte| byte.fold(cx, folder)).collect();
1726 Self::keccak_symbol(cx, name, len, bytes)
1727 }
1728 SymExprKind::Hash { name, algorithm, bytes } => {
1729 let bytes = bytes.iter().cloned().map(|byte| byte.fold(cx, folder)).collect();
1730 Self::hash_symbol(cx, name, algorithm, bytes)
1731 }
1732 SymExprKind::Not(value) => {
1733 let value = value.fold(cx, folder);
1734 Self::not(cx, value)
1735 }
1736 SymExprKind::BinOp(op, left, right) => {
1737 let left = left.fold(cx, folder);
1738 let right = right.fold(cx, folder);
1739 Self::binop(cx, op, left, right)
1740 }
1741 SymExprKind::TernOp(op, left, right, modulus) => {
1742 let left = left.fold(cx, folder);
1743 let right = right.fold(cx, folder);
1744 let modulus = modulus.fold(cx, folder);
1745 Self::ternop(cx, op, left, right, modulus)
1746 }
1747 SymExprKind::Ite(condition, then_expr, else_expr) => {
1748 let condition = condition.fold_exprs(cx, folder);
1749 let then_expr = then_expr.fold(cx, folder);
1750 let else_expr = else_expr.fold(cx, folder);
1751 Self::ite(cx, condition, then_expr, else_expr)
1752 }
1753 SymExprKind::Const(_) | SymExprKind::Var(_) | SymExprKind::GasLeft(_) => {
1754 unreachable!("leaf expression returned before folding children")
1755 }
1756 };
1757 folder(cx, expr)
1758 }
1759
1760 #[cfg(test)]
1761 pub(crate) fn smt(&self, cx: &SymCx) -> String {
1762 let mut smt = String::new();
1763 self.write_smt(cx, &mut smt);
1764 smt
1765 }
1766
1767 pub(in crate::runtime::expr) fn write_smt(&self, cx: &SymCx, out: &mut String) {
1768 match self.kind() {
1769 SymExprKind::Const(value) => {
1770 let _ = write!(out, "(_ bv{value} 256)");
1771 }
1772 SymExprKind::Var(symbol)
1773 | SymExprKind::GasLeft(symbol)
1774 | SymExprKind::Keccak { name: symbol, .. }
1775 | SymExprKind::Hash { name: symbol, .. } => out.push_str(cx.symbol_name(*symbol)),
1776 SymExprKind::Not(value) => {
1777 out.push_str("(bvnot ");
1778 value.write_smt(cx, out);
1779 out.push(')');
1780 }
1781 SymExprKind::BinOp(op, left, right) => {
1782 let _ = write!(out, "({} ", op.smt());
1783 left.write_smt(cx, out);
1784 out.push(' ');
1785 right.write_smt(cx, out);
1786 out.push(')');
1787 }
1788 SymExprKind::TernOp(op, left, right, modulus) => {
1789 write_smt_wide_modular_arithmetic(cx, out, op.smt(), left, right, modulus);
1790 }
1791 SymExprKind::Ite(cond, left, right) => {
1792 out.push_str("(ite ");
1793 cond.write_smt(cx, out);
1794 out.push(' ');
1795 left.write_smt(cx, out);
1796 out.push(' ');
1797 right.write_smt(cx, out);
1798 out.push(')');
1799 }
1800 }
1801 }
1802}
1803
1804fn write_smt_wide_modular_arithmetic(
1805 cx: &SymCx,
1806 out: &mut String,
1807 op: &'static str,
1808 left: &SymExpr,
1809 right: &SymExpr,
1810 modulus: &SymExpr,
1811) {
1812 out.push_str("(ite (= ");
1817 modulus.write_smt(cx, out);
1818 out.push_str(" (_ bv0 256)) (_ bv0 256) ((_ extract 255 0) (bvurem (");
1819 out.push_str(op);
1820 out.push_str(" ((_ zero_extend 256) ");
1821 left.write_smt(cx, out);
1822 out.push_str(") ((_ zero_extend 256) ");
1823 right.write_smt(cx, out);
1824 out.push_str(")) ((_ zero_extend 256) ");
1825 modulus.write_smt(cx, out);
1826 out.push_str("))))");
1827}
1828
1829#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1830pub(crate) enum SymTernOp {
1831 AddMod,
1832 MulMod,
1833}
1834
1835impl SymTernOp {
1836 pub(crate) const fn smt(self) -> &'static str {
1837 match self {
1838 Self::AddMod => "bvadd",
1839 Self::MulMod => "bvmul",
1840 }
1841 }
1842
1843 pub(crate) fn eval(self, left: U256, right: U256, modulus: U256) -> U256 {
1844 if modulus.is_zero() {
1845 return U256::ZERO;
1846 }
1847 match self {
1848 Self::AddMod => left.add_mod(right, modulus),
1849 Self::MulMod => left.mul_mod(right, modulus),
1850 }
1851 }
1852}
1853
1854#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1855pub(crate) enum SymBinOp {
1856 Add,
1857 Sub,
1858 Mul,
1859 UDiv,
1860 URem,
1861 SDiv,
1862 SRem,
1863 And,
1864 Or,
1865 Xor,
1866 Shl,
1867 Shr,
1868 Sar,
1869}
1870
1871impl SymBinOp {
1872 pub(crate) const fn smt(self) -> &'static str {
1873 match self {
1874 Self::Add => "bvadd",
1875 Self::Sub => "bvsub",
1876 Self::Mul => "bvmul",
1877 Self::UDiv => "bvudiv",
1878 Self::URem => "bvurem",
1879 Self::SDiv => "bvsdiv",
1880 Self::SRem => "bvsrem",
1881 Self::And => "bvand",
1882 Self::Or => "bvor",
1883 Self::Xor => "bvxor",
1884 Self::Shl => "bvshl",
1885 Self::Shr => "bvlshr",
1886 Self::Sar => "bvashr",
1887 }
1888 }
1889
1890 pub(crate) fn eval(self, left: U256, right: U256) -> U256 {
1891 match self {
1892 Self::Add => left.wrapping_add(right),
1893 Self::Sub => left.wrapping_sub(right),
1894 Self::Mul => left.wrapping_mul(right),
1895 Self::UDiv => {
1896 if right.is_zero() {
1897 U256::ZERO
1898 } else {
1899 left / right
1900 }
1901 }
1902 Self::URem => {
1903 if right.is_zero() {
1904 U256::ZERO
1905 } else {
1906 left % right
1907 }
1908 }
1909 Self::SDiv => sdiv(left, right),
1910 Self::SRem => smod(left, right),
1911 Self::And => left & right,
1912 Self::Or => left | right,
1913 Self::Xor => left ^ right,
1914 Self::Shl => {
1915 if right >= U256::from(256) {
1916 U256::ZERO
1917 } else {
1918 left << usize::try_from(right).expect("checked word shift")
1919 }
1920 }
1921 Self::Shr => {
1922 if right >= U256::from(256) {
1923 U256::ZERO
1924 } else {
1925 left >> usize::try_from(right).expect("checked word shift")
1926 }
1927 }
1928 Self::Sar => {
1929 if right >= U256::from(256) {
1930 sar(left, 256)
1931 } else {
1932 sar(left, usize::try_from(right).expect("checked word shift"))
1933 }
1934 }
1935 }
1936 }
1937}