1use super::{hashcons::HashConsed, *};
2use foundry_evm::revm::interpreter::instructions::i256::{i256_div, i256_mod};
3
4const MAX_BITWISE_BOOL_WORD_VISITS: usize = 256;
7const MAX_CONSTANT_DIFFERENCE_VISITS: usize = 256;
8
9impl SymExpr {
10 pub(crate) fn select_storage_write(
11 self,
12 cx: &mut SymCx,
13 write_key: Self,
14 write_value: Self,
15 base: Self,
16 ) -> Self {
17 if write_value == base {
18 return base;
19 }
20 let condition = self.storage_key_eq(cx, &write_key);
21 match condition.as_const() {
22 Some(true) => write_value,
23 Some(false) => base,
24 None => Self::ite(cx, condition, write_value, base),
25 }
26 }
27
28 pub(crate) fn storage_key_eq(&self, cx: &mut SymCx, write_key: &Self) -> SymBoolExpr {
29 if let (Some(read_root), Some(write_root)) =
30 (self.storage_mapping_root_slot(cx), write_key.storage_mapping_root_slot(cx))
31 && read_root != write_root
32 {
33 return SymBoolExpr::constant(cx, false);
34 }
35 match (self.storage_layout_key(cx), write_key.storage_layout_key(cx)) {
36 (Some((read_base, read_offset)), Some((write_base, write_offset))) => {
37 let read_base = read_base
38 .storage_base_eq(cx, &write_base)
39 .unwrap_or_else(|| SymBoolExpr::eq(cx, read_base, write_base));
40 let read_offset = SymBoolExpr::eq(cx, read_offset, write_offset);
41 SymBoolExpr::and(cx, vec![read_base, read_offset])
42 }
43 (Some(_), None) if write_key.as_const().is_some() => SymBoolExpr::constant(cx, false),
44 (None, Some(_)) if self.as_const().is_some() => SymBoolExpr::constant(cx, false),
45 _ => SymBoolExpr::eq(cx, self.clone(), write_key.clone()),
46 }
47 }
48
49 fn storage_base_eq(&self, cx: &mut SymCx, other: &Self) -> Option<SymBoolExpr> {
50 let read = self.storage_mapping_key(cx)?;
51 let write = other.storage_mapping_key(cx)?;
52
53 let key_eq = storage_mapping_key_eq(cx, &read, &write);
54 let slot_eq = read
55 .slot
56 .storage_base_eq(cx, &write.slot)
57 .unwrap_or_else(|| SymBoolExpr::eq(cx, read.slot, write.slot));
58 Some(SymBoolExpr::and(cx, vec![key_eq, slot_eq]))
59 }
60
61 pub(crate) fn storage_mapping_key(&self, cx: &mut SymCx) -> Option<StorageMappingKey> {
62 let bytes = self.storage_mapping_key_bytes(cx)?;
63 let key_bytes = &bytes[..32];
64 let preserve_key_bytes = (!key_bytes.iter().all(|byte| byte.as_const().is_some())
65 && word_from_extracted_bytes(key_bytes).is_none())
66 .then(|| key_bytes.to_vec());
67 let key = Self::from_bytes(cx, key_bytes.iter().cloned());
68 let slot = Self::from_bytes(cx, bytes[32..64].iter().cloned());
69 Some(StorageMappingKey { key, key_bytes: preserve_key_bytes, slot })
70 }
71
72 pub(crate) fn storage_mapping_provenance_observed_with(
73 &self,
74 cx: &mut SymCx,
75 mut observed_preimage: impl FnMut(&Self) -> Option<Arc<[Self]>>,
76 ) -> Option<SymbolicMappingProvenance> {
77 let mut current = self.clone();
78 let mut keys = Vec::new();
79 let mut visited = Vec::new();
80 loop {
81 if visited.contains(¤t) {
82 return None;
83 }
84 visited.push(current.clone());
85 let bytes = observed_preimage(¤t)?;
86 if bytes.len() != 64 {
87 return None;
88 }
89 let key = Self::from_bytes(cx, bytes[..32].iter().cloned());
90 keys.push(key);
91 current = Self::from_bytes(cx, bytes[32..64].iter().cloned());
92 match current.kind() {
93 SymExprKind::Const(root_slot) if observed_preimage(¤t).is_none() => {
94 keys.reverse();
95 return Some(SymbolicMappingProvenance { root_slot: *root_slot, keys });
96 }
97 SymExprKind::Const(_) | SymExprKind::Keccak { .. } => {}
98 _ => return None,
99 }
100 }
101 }
102
103 fn storage_mapping_root_slot(&self, cx: &mut SymCx) -> Option<U256> {
104 let bytes = self.storage_mapping_key_bytes(cx)?;
105 let slot = Self::from_bytes(cx, bytes[32..64].iter().cloned());
106 match slot.kind() {
107 SymExprKind::Const(value) if cx.concrete_keccak_preimage(*value).is_some() => {
108 slot.storage_mapping_root_slot(cx)
109 }
110 SymExprKind::Const(slot) => Some(*slot),
111 SymExprKind::Keccak { .. } => slot.storage_mapping_root_slot(cx),
112 _ => None,
113 }
114 }
115
116 fn storage_mapping_key_bytes(&self, cx: &SymCx) -> Option<Arc<[Self]>> {
117 match self.kind() {
118 SymExprKind::Keccak { len, bytes, .. }
119 if len.as_const() == Some(U256::from(64)) && bytes.len() >= 64 =>
120 {
121 Some(bytes.clone())
122 }
123 SymExprKind::Const(hash) => cx.concrete_keccak_preimage(*hash),
124 _ => None,
125 }
126 }
127
128 fn storage_layout_key(&self, cx: &mut SymCx) -> Option<(Self, Self)> {
129 match self.kind() {
130 SymExprKind::Keccak { .. } => Some((self.clone(), Self::zero(cx))),
131 SymExprKind::Const(hash) if cx.concrete_keccak_preimage(*hash).is_some() => {
132 Some((self.clone(), Self::zero(cx)))
133 }
134 SymExprKind::BinOp(SymBinOp::Add, left, right) => {
135 if let Some((base, offset)) = left.storage_layout_key(cx)
136 && !right.contains_keccak()
137 {
138 let offset = Self::binop(cx, SymBinOp::Add, offset, right.clone());
139 return Some((base, offset));
140 }
141 if let Some((base, offset)) = right.storage_layout_key(cx)
142 && !left.contains_keccak()
143 {
144 let offset = Self::binop(cx, SymBinOp::Add, offset, left.clone());
145 return Some((base, offset));
146 }
147 None
148 }
149 _ => None,
150 }
151 }
152}
153
154pub(crate) struct StorageMappingKey {
155 key: SymExpr,
156 key_bytes: Option<Vec<SymExpr>>,
157 slot: SymExpr,
158}
159
160#[derive(Clone, Debug, PartialEq, Eq)]
161pub(crate) struct SymbolicMappingProvenance {
162 pub(crate) root_slot: U256,
163 pub(crate) keys: Vec<SymExpr>,
164}
165
166fn storage_mapping_key_eq(
167 cx: &mut SymCx,
168 read: &StorageMappingKey,
169 write: &StorageMappingKey,
170) -> SymBoolExpr {
171 if read.key_bytes.is_some() || write.key_bytes.is_some() {
172 let read_owned;
173 let read_bytes = if let Some(bytes) = read.key_bytes.as_deref() {
174 bytes
175 } else {
176 read_owned = read.key.clone().into_byte_exprs(cx);
177 &read_owned
178 };
179 let write_owned;
180 let write_bytes = if let Some(bytes) = write.key_bytes.as_deref() {
181 bytes
182 } else {
183 write_owned = write.key.clone().into_byte_exprs(cx);
184 &write_owned
185 };
186 let byte_equalities = read_bytes
187 .iter()
188 .zip(write_bytes)
189 .map(|(read, write)| {
190 let read = read.byte_term(cx, 31).unwrap_or_else(|| read.clone().low_byte(cx));
191 let write = write.byte_term(cx, 31).unwrap_or_else(|| write.clone().low_byte(cx));
192 SymBoolExpr::eq(cx, read, write)
193 })
194 .collect();
195 SymBoolExpr::and(cx, byte_equalities)
196 } else {
197 SymBoolExpr::eq(cx, read.key.clone(), write.key.clone())
198 }
199}
200
201fn masked_expr_matches(candidate: &SymExprKind, target: &SymExpr) -> Option<U256> {
202 match candidate {
203 SymExprKind::BinOp(SymBinOp::And, left, right) if left == target => right.eval(),
204 SymExprKind::BinOp(SymBinOp::And, left, right) if right == target => left.eval(),
205 _ => None,
206 }
207}
208
209fn context_forces_masked_expr(context: &[SymBoolExpr], target: &SymExpr, mask: U256) -> bool {
210 context.iter().any(|condition| match condition.kind() {
211 SymBoolExprKind::Cmp(SymCmpOp::Eq, left, right) => {
212 (left == target && masked_expr_matches(right.kind(), target) == Some(mask))
213 || (right == target && masked_expr_matches(left.kind(), target) == Some(mask))
214 }
215 SymBoolExprKind::And(values) => context_forces_masked_expr(values, target, mask),
216 _ => false,
217 })
218}
219
220pub(crate) fn concrete_expr_bytes(
221 bytes: &[SymExpr],
222 reason: &'static str,
223) -> Result<Vec<u8>, SymbolicError> {
224 bytes
225 .iter()
226 .map(|byte| match byte.as_const() {
227 Some(value) => Ok(value.to::<u8>()),
228 None => Err(SymbolicError::Unsupported(reason)),
229 })
230 .collect()
231}
232
233pub(crate) fn mask_low_bits(mask: U256) -> Option<usize> {
234 let bits = mask.bit_len();
235 (mask == mask_bits(U256::MAX, bits)).then_some(bits)
236}
237
238fn power_of_two_shift(value: U256) -> Option<usize> {
239 if value <= U256::ONE || !value.is_power_of_two() {
240 return None;
241 }
242 Some(value.bit_len() - 1)
243}
244
245pub(in crate::runtime::expr) fn low_masked_source(expr: &SymExpr, bits: usize) -> Option<&SymExpr> {
246 match expr.kind() {
247 SymExprKind::BinOp(SymBinOp::And, left, right)
249 if right.as_const().and_then(mask_low_bits) == Some(bits) =>
250 {
251 Some(left)
252 }
253 _ => None,
254 }
255}
256
257pub(in crate::runtime::expr) fn low_masked_source_any(expr: &SymExpr) -> Option<&SymExpr> {
258 match expr.kind() {
259 SymExprKind::BinOp(SymBinOp::And, left, right)
261 if right.as_const().and_then(mask_low_bits).is_some() =>
262 {
263 Some(left)
264 }
265 _ => None,
266 }
267}
268
269fn word_from_extracted_bytes(bytes: &[SymExpr]) -> Option<SymExpr> {
270 if bytes.len() < 32 {
271 return None;
272 }
273
274 let source = bytes
275 .iter()
276 .take(32)
277 .enumerate()
278 .find_map(|(idx, byte)| byte.extracted_byte_source(idx))?;
279
280 for (idx, byte) in bytes.iter().take(32).enumerate() {
281 if let Some(byte_source) = byte.extracted_byte_source(idx) {
282 if byte_source != source {
283 return None;
284 }
285 continue;
286 }
287
288 let byte = byte.as_const()?;
289 if source.known_byte(idx) != Some(byte.to::<u8>()) {
290 return None;
291 }
292 }
293 Some(source)
294}
295
296#[derive(Clone, PartialEq, Eq, Hash)]
297pub(crate) struct SymExpr {
298 pub(in crate::runtime::expr) kind: HashConsed<SymExprKind>,
299}
300
301impl fmt::Debug for SymExpr {
302 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
303 self.kind().fmt(f)
304 }
305}
306
307#[derive(Clone, PartialEq, Eq, Hash)]
308pub(in crate::runtime) enum SymExprKind {
309 Const(U256),
310 Var(Symbol),
311 GasLeft(Symbol),
312 Keccak { name: Symbol, len: SymExpr, bytes: Arc<[SymExpr]> },
313 Hash { name: Symbol, algorithm: &'static str, bytes: Arc<[SymExpr]> },
314 Not(SymExpr),
315 BinOp(SymBinOp, SymExpr, SymExpr),
316 TernOp(SymTernOp, SymExpr, SymExpr, SymExpr),
317 Ite(SymBoolExpr, SymExpr, SymExpr),
318}
319
320impl fmt::Debug for SymExprKind {
326 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
327 match self {
328 Self::Const(value) => f.debug_tuple("Const").field(value).finish(),
329 Self::Var(symbol) => f.debug_tuple("Var").field(symbol).finish(),
330 Self::GasLeft(symbol) => f.debug_tuple("GasLeft").field(symbol).finish(),
331 Self::Keccak { name, len, .. } => f
332 .debug_struct("Keccak")
333 .field("name", name)
334 .field("len", len)
335 .finish_non_exhaustive(),
336 Self::Hash { name, algorithm, .. } => f
337 .debug_struct("Hash")
338 .field("name", name)
339 .field("algorithm", algorithm)
340 .finish_non_exhaustive(),
341 Self::Not(expr) => f.debug_tuple("Not").field(expr).finish(),
342 Self::BinOp(op, left, right) => {
343 f.debug_tuple("BinOp").field(op).field(left).field(right).finish()
344 }
345 Self::TernOp(op, first, second, third) => {
346 f.debug_tuple("TernOp").field(op).field(first).field(second).field(third).finish()
347 }
348 Self::Ite(condition, then, otherwise) => {
349 f.debug_tuple("Ite").field(condition).field(then).field(otherwise).finish()
350 }
351 }
352 }
353}
354
355impl SymExprKind {
356 pub(in crate::runtime) const fn get_var(&self) -> Option<Symbol> {
357 match self {
358 Self::Var(symbol)
359 | Self::GasLeft(symbol)
360 | Self::Keccak { name: symbol, .. }
361 | Self::Hash { name: symbol, .. } => Some(*symbol),
362 _ => None,
363 }
364 }
365
366 pub(in crate::runtime) const fn get_eval_var(&self) -> Option<Symbol> {
367 match self {
368 Self::Var(symbol) | Self::GasLeft(symbol) | Self::Hash { name: symbol, .. } => {
369 Some(*symbol)
370 }
371 _ => None,
372 }
373 }
374}
375
376impl SymExpr {
377 pub(in crate::runtime) fn kind(&self) -> &SymExprKind {
378 self.kind.value()
379 }
380
381 pub(in crate::runtime) fn from_kind(cx: &mut SymCx, kind: SymExprKind) -> Self {
382 cx.mk_expr_kind(kind)
383 }
384
385 pub(crate) fn zero(cx: &mut SymCx) -> Self {
386 Self::constant(cx, U256::ZERO)
387 }
388
389 pub(crate) fn one(cx: &mut SymCx) -> Self {
390 Self::constant(cx, U256::ONE)
391 }
392
393 pub(crate) fn constant(cx: &mut SymCx, value: U256) -> Self {
394 if value.is_zero() {
395 return cx.cached_zero();
396 }
397 if value == U256::ONE {
398 return cx.cached_one();
399 }
400 Self::from_kind(cx, SymExprKind::Const(value))
401 }
402
403 pub(crate) fn var(cx: &mut SymCx, name: &str) -> Self {
404 let symbol = cx.intern(name);
405 Self::get_var(cx, symbol)
406 }
407
408 pub(crate) fn get_var(cx: &mut SymCx, symbol: Symbol) -> Self {
409 Self::from_kind(cx, SymExprKind::Var(symbol))
410 }
411
412 pub(crate) fn gas_left(cx: &mut SymCx, id: usize) -> Self {
413 let symbol = cx.intern(&format!("gasleft_{id}"));
414 Self::from_kind(cx, SymExprKind::GasLeft(symbol))
415 }
416
417 pub(crate) fn not(cx: &mut SymCx, value: Self) -> Self {
418 match value.kind() {
419 SymExprKind::Const(value) => Self::constant(cx, !*value),
420 SymExprKind::Not(value) => value.clone(),
421 _ => Self::from_kind(cx, SymExprKind::Not(value)),
422 }
423 }
424
425 pub(crate) fn binop(cx: &mut SymCx, binop: SymBinOp, left: Self, right: Self) -> Self {
426 match binop {
427 SymBinOp::Add => match (left.kind(), right.kind()) {
428 (SymExprKind::Const(left_value), SymExprKind::Const(right_value)) => {
429 Self::constant(cx, binop.eval(*left_value, *right_value))
431 }
432 (SymExprKind::Const(value), _) if value.is_zero() => right,
434 (_, SymExprKind::Const(value)) if value.is_zero() => left,
436 (SymExprKind::Const(value), _) | (_, SymExprKind::Const(value))
438 if *value == U256::MAX
439 && let Some(condition) = if left.as_const() == Some(U256::MAX) {
440 right.bitwise_bool_word_condition(cx)
441 } else {
442 left.bitwise_bool_word_condition(cx)
443 } =>
444 {
445 let zero = Self::zero(cx);
446 let max = Self::constant(cx, U256::MAX);
447 Self::ite(cx, condition, zero, max)
448 }
449 (SymExprKind::Const(value), _)
451 if let Some(condition) = right.bitwise_bool_word_condition(cx) =>
452 {
453 let incremented = Self::constant(cx, value.wrapping_add(U256::ONE));
454 let value = Self::constant(cx, *value);
455 Self::ite(cx, condition, incremented, value)
456 }
457 (_, SymExprKind::Const(value))
458 if let Some(condition) = left.bitwise_bool_word_condition(cx) =>
459 {
460 let incremented = Self::constant(cx, value.wrapping_add(U256::ONE));
461 let value = Self::constant(cx, *value);
462 Self::ite(cx, condition, incremented, value)
463 }
464 _ => {
465 let (left, right) = Self::ordered_commutative_operands(left, right);
466 if let Some(value) = Self::add_with_const_ite(cx, &left, &right) {
467 value
468 } else if let Some(value) = Self::add_with_const_ite(cx, &right, &left) {
469 value
470 } else {
471 Self::from_kind(cx, SymExprKind::BinOp(binop, left, right))
472 }
473 }
474 },
475 SymBinOp::Sub => match (left.kind(), right.kind()) {
476 (SymExprKind::Const(left_value), SymExprKind::Const(right_value)) => {
477 Self::constant(cx, binop.eval(*left_value, *right_value))
479 }
480 (_, SymExprKind::Const(value)) if value.is_zero() => left,
482 _ if left == right => Self::zero(cx),
484 (_, SymExprKind::Const(value))
486 if *value == U256::ONE
487 && let Some(condition) = left.bitwise_bool_word_condition(cx) =>
488 {
489 let zero = Self::zero(cx);
490 let max = Self::constant(cx, U256::MAX);
491 Self::ite(cx, condition, zero, max)
492 }
493 (SymExprKind::Const(value), _)
495 if let Some(condition) = right.bitwise_bool_word_condition(cx) =>
496 {
497 let decremented = Self::constant(cx, value.wrapping_sub(U256::ONE));
498 let value = Self::constant(cx, *value);
499 Self::ite(cx, condition, decremented, value)
500 }
501 _ => Self::from_kind(cx, SymExprKind::BinOp(binop, left, right)),
502 },
503 SymBinOp::Mul => match (left.kind(), right.kind()) {
504 (SymExprKind::Const(left_value), SymExprKind::Const(right_value)) => {
505 Self::constant(cx, binop.eval(*left_value, *right_value))
507 }
508 (SymExprKind::Const(value), _) | (_, SymExprKind::Const(value))
510 if value.is_zero() =>
511 {
512 Self::zero(cx)
513 }
514 (SymExprKind::Const(value), _) if *value == U256::ONE => right,
516 (_, SymExprKind::Const(value)) if *value == U256::ONE => left,
518 _ => {
519 let (left, right) = Self::ordered_commutative_operands(left, right);
520 if let Some(condition) = left.direct_bool_word_condition(cx) {
521 let zero = Self::zero(cx);
523 Self::ite(cx, condition, right, zero)
524 } else if let Some(condition) = right.direct_bool_word_condition(cx) {
525 let zero = Self::zero(cx);
527 Self::ite(cx, condition, left, zero)
528 } else if let Some(shift) = right.as_const().and_then(power_of_two_shift) {
529 let shift = Self::constant(cx, U256::from(shift));
531 Self::binop(cx, SymBinOp::Shl, left, shift)
532 } else {
533 Self::from_kind(cx, SymExprKind::BinOp(binop, left, right))
534 }
535 }
536 },
537 SymBinOp::UDiv | SymBinOp::SDiv => match (left.kind(), right.kind()) {
538 (SymExprKind::Const(left_value), SymExprKind::Const(right_value)) => {
539 Self::constant(cx, binop.eval(*left_value, *right_value))
541 }
542 (_, SymExprKind::Const(value)) if value.is_zero() => Self::zero(cx),
544 (_, SymExprKind::Const(value)) if *value == U256::ONE => left,
546 (
548 SymExprKind::BinOp(SymBinOp::Sub, value, low_bits),
549 SymExprKind::Const(divisor),
550 ) if binop == SymBinOp::UDiv
551 && let Some(shift) = power_of_two_shift(*divisor)
552 && low_masked_source(low_bits, shift) == Some(value) =>
553 {
554 let shift = Self::constant(cx, U256::from(shift));
555 Self::binop(cx, SymBinOp::Shr, value.clone(), shift)
556 }
557 (_, SymExprKind::Const(divisor))
559 if binop == SymBinOp::UDiv
560 && let Some(shift) = power_of_two_shift(*divisor) =>
561 {
562 let shift = Self::constant(cx, U256::from(shift));
563 Self::binop(cx, SymBinOp::Shr, left, shift)
564 }
565 _ => Self::from_kind(cx, SymExprKind::BinOp(binop, left, right)),
566 },
567 SymBinOp::URem | SymBinOp::SRem => match (left.kind(), right.kind()) {
568 (SymExprKind::Const(left_value), SymExprKind::Const(right_value)) => {
569 Self::constant(cx, binop.eval(*left_value, *right_value))
571 }
572 (_, SymExprKind::Const(value)) if value.is_zero() => Self::zero(cx),
574 (_, SymExprKind::Const(value)) if *value == U256::ONE => Self::zero(cx),
576 (_, SymExprKind::Const(divisor))
578 if binop == SymBinOp::URem
579 && let Some(bits) = power_of_two_shift(*divisor) =>
580 {
581 Self::and_const(cx, left, mask_bits(U256::MAX, bits))
582 }
583 _ => Self::from_kind(cx, SymExprKind::BinOp(binop, left, right)),
584 },
585 SymBinOp::And => match (left.kind(), right.kind()) {
586 (SymExprKind::Const(left_value), SymExprKind::Const(right_value)) => {
587 Self::constant(cx, binop.eval(*left_value, *right_value))
589 }
590 (SymExprKind::Const(value), _) | (_, SymExprKind::Const(value))
592 if value.is_zero() =>
593 {
594 Self::zero(cx)
595 }
596 (SymExprKind::Const(value), _) if *value == U256::MAX => right,
598 (_, SymExprKind::Const(value)) if *value == U256::MAX => left,
600 _ if left == right => left,
602 (SymExprKind::Const(mask), _) => Self::and_const(cx, right, *mask),
603 (_, SymExprKind::Const(mask)) => Self::and_const(cx, left, *mask),
604 _ => Self::commutative_binop(cx, binop, left, right),
605 },
606 SymBinOp::Or => match (left.kind(), right.kind()) {
607 (SymExprKind::Const(left_value), SymExprKind::Const(right_value)) => {
608 Self::constant(cx, binop.eval(*left_value, *right_value))
610 }
611 (SymExprKind::Const(value), _) if value.is_zero() => right,
613 (_, SymExprKind::Const(value)) if value.is_zero() => left,
615 _ if left == right => left,
617 _ if let Some(value) = Self::or_with_absorbing_ite(cx, &left, right.clone()) => {
618 value
619 }
620 _ if let Some(value) = Self::or_with_absorbing_ite(cx, &right, left.clone()) => {
621 value
622 }
623 _ => Self::or(cx, left, right),
624 },
625 SymBinOp::Xor => match (left.kind(), right.kind()) {
626 (SymExprKind::Const(left_value), SymExprKind::Const(right_value)) => {
627 Self::constant(cx, binop.eval(*left_value, *right_value))
629 }
630 (SymExprKind::Const(value), _) if value.is_zero() => right,
632 (_, SymExprKind::Const(value)) if value.is_zero() => left,
634 _ if left == right => Self::zero(cx),
636 _ => {
637 let (left, right) = Self::ordered_commutative_operands(left, right);
638 if let Some(value) = Self::xor_with_shared_operand(&left, &right)
640 .or_else(|| Self::xor_with_shared_operand(&right, &left))
641 {
642 value
643 } else if let Some(value) = Self::xor_with_bool_select(cx, &left, &right) {
645 value
646 } else if let Some(value) = Self::xor_with_bool_select(cx, &right, &left) {
647 value
648 } else if let Some(value) = Self::xor_with_zero_ite(cx, &left, &right) {
650 value
651 } else if let Some(value) = Self::xor_with_zero_ite(cx, &right, &left) {
652 value
653 } else {
654 Self::from_kind(cx, SymExprKind::BinOp(binop, left, right))
655 }
656 }
657 },
658 SymBinOp::Shl => match (left.kind(), right.kind()) {
659 (SymExprKind::Const(left_value), SymExprKind::Const(right_value)) => {
660 Self::constant(cx, binop.eval(*left_value, *right_value))
662 }
663 (_, SymExprKind::Const(value)) if value.is_zero() => left,
665 (SymExprKind::Const(value), _) if value.is_zero() => Self::zero(cx),
667 (_, SymExprKind::Const(value)) if *value >= U256::from(256) => Self::zero(cx),
669 _ => Self::from_kind(cx, SymExprKind::BinOp(binop, left, right)),
670 },
671 SymBinOp::Shr => match (left.kind(), right.kind()) {
672 (SymExprKind::Const(left_value), SymExprKind::Const(right_value)) => {
673 Self::constant(cx, binop.eval(*left_value, *right_value))
675 }
676 (_, SymExprKind::Const(value)) if value.is_zero() => left,
678 (SymExprKind::Const(value), _) if value.is_zero() => Self::zero(cx),
680 (_, SymExprKind::Const(value)) => Self::shr_const(cx, left, *value),
681 _ => Self::from_kind(cx, SymExprKind::BinOp(binop, left, right)),
682 },
683 SymBinOp::Sar => match (left.kind(), right.kind()) {
684 (SymExprKind::Const(left_value), SymExprKind::Const(right_value)) => {
685 Self::constant(cx, binop.eval(*left_value, *right_value))
687 }
688 (_, SymExprKind::Const(value)) if value.is_zero() => left,
690 _ => Self::from_kind(cx, SymExprKind::BinOp(binop, left, right)),
691 },
692 }
693 }
694
695 pub(crate) fn ternop(
696 cx: &mut SymCx,
697 ternop: SymTernOp,
698 left: Self,
699 right: Self,
700 modulus: Self,
701 ) -> Self {
702 match (left.kind(), right.kind(), modulus.kind()) {
703 (_, _, SymExprKind::Const(modulus)) if modulus.is_zero() || *modulus == U256::ONE => {
704 Self::zero(cx)
706 }
707 (SymExprKind::Const(left), SymExprKind::Const(right), SymExprKind::Const(modulus)) => {
708 Self::constant(cx, ternop.eval(*left, *right, *modulus))
710 }
711 (_, _, SymExprKind::Const(modulus))
713 if let Some(bits) = power_of_two_shift(*modulus) =>
714 {
715 let binop = match ternop {
716 SymTernOp::AddMod => SymBinOp::Add,
717 SymTernOp::MulMod => SymBinOp::Mul,
718 };
719 let value = Self::binop(cx, binop, left, right);
720 Self::and_const(cx, value, mask_bits(U256::MAX, bits))
721 }
722 _ => {
723 let (left, right) = Self::ordered_commutative_operands(left, right);
725 Self::from_kind(cx, SymExprKind::TernOp(ternop, left, right, modulus))
726 }
727 }
728 }
729
730 pub(crate) fn ite(
731 cx: &mut SymCx,
732 condition: SymBoolExpr,
733 then_expr: Self,
734 else_expr: Self,
735 ) -> Self {
736 match condition.as_const() {
737 Some(true) => then_expr,
739 Some(false) => else_expr,
741 None if then_expr == else_expr => then_expr,
743 None if then_expr.as_const().is_some_and(|value| value.is_zero())
745 && Self::self_div_expr_matches_zero_check(&condition, &else_expr) =>
746 {
747 let condition = condition.not(cx);
748 Self::bool_word(cx, condition)
749 }
750 None if then_expr.as_const() == Some(U256::ONE)
752 && else_expr.bool_word_condition().as_ref() == Some(&condition) =>
753 {
754 else_expr
755 }
756 None if else_expr.as_const().is_some_and(|value| value.is_zero())
758 && then_expr.bool_word_condition().as_ref() == Some(&condition) =>
759 {
760 then_expr
761 }
762 None => Self::from_kind(cx, SymExprKind::Ite(condition, then_expr, else_expr)),
763 }
764 }
765
766 pub(crate) fn bool_word(cx: &mut SymCx, value: SymBoolExpr) -> Self {
767 let one = Self::one(cx);
768 let zero = Self::zero(cx);
769 Self::ite(cx, value, one, zero)
770 }
771
772 fn self_div_expr_matches_zero_check(cond: &SymBoolExpr, expr: &Self) -> bool {
773 let Some(zero_operand) = cond.zero_check_operand() else { return false };
774 let Some((numerator, denominator)) = expr.udiv_operands() else { return false };
775 numerator == zero_operand && denominator == zero_operand
776 }
777
778 pub(crate) fn keccak_symbol(cx: &mut SymCx, name: Symbol, len: Self, bytes: Vec<Self>) -> Self {
779 Self::from_kind(cx, SymExprKind::Keccak { name, len, bytes: bytes.into() })
780 }
781
782 pub(crate) fn hash_symbol(
783 cx: &mut SymCx,
784 name: Symbol,
785 algorithm: &'static str,
786 bytes: Vec<Self>,
787 ) -> Self {
788 Self::from_kind(cx, SymExprKind::Hash { name, algorithm, bytes: bytes.into() })
789 }
790
791 fn or(cx: &mut SymCx, left: Self, right: Self) -> Self {
792 if let Some(rebuilt) = Self::rebuild_from_or_terms(&left, &right) {
793 return rebuilt;
795 }
796 Self::commutative_binop(cx, SymBinOp::Or, left, right)
797 }
798
799 fn or_with_absorbing_ite(cx: &mut SymCx, conditional: &Self, other: Self) -> Option<Self> {
800 let SymExprKind::Ite(condition, then_expr, else_expr) = conditional.kind() else {
801 return None;
802 };
803 if then_expr.as_const().is_some_and(|value| value.is_zero())
804 && else_expr.as_const() == Some(U256::MAX)
805 {
806 return Some(Self::ite(cx, condition.clone(), other, else_expr.clone()));
807 }
808 if then_expr.as_const() == Some(U256::MAX)
809 && else_expr.as_const().is_some_and(|value| value.is_zero())
810 {
811 return Some(Self::ite(cx, condition.clone(), then_expr.clone(), other));
812 }
813 None
814 }
815
816 fn add_with_const_ite(cx: &mut SymCx, other: &Self, conditional: &Self) -> Option<Self> {
817 if other.as_const().is_some() {
818 return None;
819 }
820 let SymExprKind::Ite(condition, then_expr, else_expr) = conditional.kind() else {
821 return None;
822 };
823 if then_expr.as_const().is_none() || else_expr.as_const().is_none() {
824 return None;
825 }
826 if !Self::duplicating_branchless_rewrite_fits(other, conditional) {
827 return None;
828 }
829 let then_expr = Self::binop(cx, SymBinOp::Add, other.clone(), then_expr.clone());
830 let else_expr = Self::binop(cx, SymBinOp::Add, other.clone(), else_expr.clone());
831 Some(Self::ite(cx, condition.clone(), then_expr, else_expr))
832 }
833
834 fn xor_with_bool_select(cx: &mut SymCx, base: &Self, selector: &Self) -> Option<Self> {
835 let SymExprKind::BinOp(SymBinOp::Mul, left, right) = selector.kind() else { return None };
836 let (condition_word, selected) = match left.kind() {
837 SymExprKind::BinOp(SymBinOp::Xor, delta_left, delta_right) if delta_left == base => {
838 (right, delta_right.clone())
839 }
840 SymExprKind::BinOp(SymBinOp::Xor, delta_left, delta_right) if delta_right == base => {
841 (right, delta_left.clone())
842 }
843 _ => match right.kind() {
844 SymExprKind::BinOp(SymBinOp::Xor, delta_left, delta_right)
845 if delta_left == base =>
846 {
847 (left, delta_right.clone())
848 }
849 SymExprKind::BinOp(SymBinOp::Xor, delta_left, delta_right)
850 if delta_right == base =>
851 {
852 (left, delta_left.clone())
853 }
854 _ => return None,
855 },
856 };
857 let condition = condition_word.bitwise_bool_word_condition(cx)?;
858 Some(Self::ite(cx, condition, selected, base.clone()))
859 }
860
861 fn xor_with_shared_operand(base: &Self, nested: &Self) -> Option<Self> {
862 let SymExprKind::BinOp(SymBinOp::Xor, left, right) = nested.kind() else { return None };
863 if left == base {
864 Some(right.clone())
865 } else if right == base {
866 Some(left.clone())
867 } else {
868 None
869 }
870 }
871
872 fn xor_with_zero_ite(cx: &mut SymCx, base: &Self, conditional: &Self) -> Option<Self> {
873 let SymExprKind::Ite(condition, then_expr, else_expr) = conditional.kind() else {
874 return None;
875 };
876 if then_expr.as_const().is_some_and(|value| value.is_zero()) {
877 if !Self::duplicating_branchless_rewrite_fits(base, conditional) {
878 return None;
879 }
880 let selected = Self::binop(cx, SymBinOp::Xor, base.clone(), else_expr.clone());
881 return Some(Self::ite(cx, condition.clone(), base.clone(), selected));
882 }
883 if else_expr.as_const().is_some_and(|value| value.is_zero()) {
884 if !Self::duplicating_branchless_rewrite_fits(base, conditional) {
885 return None;
886 }
887 let selected = Self::binop(cx, SymBinOp::Xor, base.clone(), then_expr.clone());
888 return Some(Self::ite(cx, condition.clone(), selected, base.clone()));
889 }
890 None
891 }
892
893 fn duplicating_branchless_rewrite_fits(operand: &Self, conditional: &Self) -> bool {
899 let mut counter = UnfoldedNodeCounter::new();
900 let Some(operand_nodes) = counter.expr_nodes(operand) else {
901 return false;
902 };
903 let Some(conditional_nodes) = counter.expr_nodes(conditional) else {
904 return false;
905 };
906
907 operand_nodes
908 .checked_mul(2)
909 .and_then(|duplicated| conditional_nodes.checked_add(duplicated))
910 .and_then(|nodes| nodes.checked_add(2))
912 .is_some_and(|nodes| nodes <= MAX_BRANCHLESS_REWRITE_UNFOLDED_NODES)
913 }
914
915 fn commutative_binop(cx: &mut SymCx, op: SymBinOp, left: Self, right: Self) -> Self {
916 let (left, right) = Self::ordered_commutative_operands(left, right);
918 Self::from_kind(cx, SymExprKind::BinOp(op, left, right))
919 }
920
921 pub(in crate::runtime::expr) fn ordered_commutative_operands(
922 left: Self,
923 right: Self,
924 ) -> (Self, Self) {
925 match left.complexity().cmp(&right.complexity()) {
926 std::cmp::Ordering::Less => (right, left),
928 std::cmp::Ordering::Greater => (left, right),
929 std::cmp::Ordering::Equal if right.kind.stable_hash_cmp(&left.kind).is_lt() => {
930 (right, left)
931 }
932 std::cmp::Ordering::Equal => (left, right),
933 }
934 }
935
936 pub(in crate::runtime) fn sort_interned_factors(factors: &mut [Self]) {
941 factors.sort_unstable_by(|left, right| left.kind.identity_cmp(&right.kind));
942 }
943
944 fn complexity(&self) -> usize {
945 match self.kind() {
946 SymExprKind::Const(_) => 0,
947 SymExprKind::Not(_) => 1,
948 SymExprKind::BinOp(..) => 2,
949 SymExprKind::TernOp(..) => 3,
950 _ => 4,
951 }
952 }
953
954 fn and_const(cx: &mut SymCx, expr: Self, mask: U256) -> Self {
955 if mask.is_zero() {
956 return Self::zero(cx);
958 }
959 if mask == U256::MAX {
960 return expr;
962 }
963
964 match expr.kind() {
965 SymExprKind::Const(value) => Self::constant(cx, *value & mask),
967 SymExprKind::BinOp(SymBinOp::Or, left, right) => {
968 let left = Self::and_const(cx, left.clone(), mask);
970 let right = Self::and_const(cx, right.clone(), mask);
971 Self::binop(cx, SymBinOp::Or, left, right)
972 }
973 SymExprKind::BinOp(SymBinOp::Shl, _, shift)
974 if mask_low_bits(mask).is_some_and(|bits| {
975 shift
976 .as_const()
977 .and_then(|shift| usize::try_from(shift).ok())
978 .is_some_and(|shift| bits <= shift)
979 }) =>
980 {
981 Self::zero(cx)
983 }
984 SymExprKind::BinOp(SymBinOp::And, left, right) => {
985 if right.as_const() == Some(mask) {
986 Self::and_const(cx, left.clone(), mask)
988 } else if left == right {
989 Self::and_const(cx, left.clone(), mask)
991 } else {
992 let mask = Self::constant(cx, mask);
993 Self::from_kind(cx, SymExprKind::BinOp(SymBinOp::And, expr, mask))
994 }
995 }
996 _ => {
997 let mask = Self::constant(cx, mask);
998 Self::from_kind(cx, SymExprKind::BinOp(SymBinOp::And, expr, mask))
999 }
1000 }
1001 }
1002
1003 fn shr_const(cx: &mut SymCx, expr: Self, shift: U256) -> Self {
1004 if shift.is_zero() {
1005 return expr;
1007 }
1008 if shift >= U256::from(256) {
1009 return Self::zero(cx);
1011 }
1012
1013 let shift = usize::try_from(shift).expect("shift is less than 256");
1014 if expr.unsigned_bits() <= shift {
1015 return Self::zero(cx);
1017 }
1018
1019 if let SymExprKind::BinOp(SymBinOp::Shl, inner, left_shift) = expr.kind()
1020 && left_shift.as_const() == Some(U256::from(shift))
1021 && inner.unsigned_bits() <= 256 - shift
1022 {
1023 return inner.clone();
1025 }
1026
1027 if let SymExprKind::BinOp(SymBinOp::Or, left, right) = expr.kind() {
1028 let left = Self::shr_const(cx, left.clone(), U256::from(shift));
1031 let right = Self::shr_const(cx, right.clone(), U256::from(shift));
1032 if left.as_const().is_some_and(|value| value.is_zero()) {
1033 return right;
1034 }
1035 if right.as_const().is_some_and(|value| value.is_zero()) {
1036 return left;
1037 }
1038 }
1039
1040 let shift = Self::constant(cx, U256::from(shift));
1041 Self::from_kind(cx, SymExprKind::BinOp(SymBinOp::Shr, expr, shift))
1042 }
1043
1044 fn rebuild_from_or_terms(left: &Self, right: &Self) -> Option<Self> {
1045 let mut terms = Vec::new();
1046 left.push_or_terms(&mut terms);
1047 right.push_or_terms(&mut terms);
1048 Self::rebuild_from_extracted_byte_terms(&terms)
1049 .or_else(|| Self::rebuild_from_shifted_word_fragments(&terms))
1050 }
1051
1052 pub(in crate::runtime) fn push_or_terms<'a>(&'a self, terms: &mut Vec<&'a Self>) {
1053 match self.kind() {
1054 SymExprKind::BinOp(SymBinOp::Or, left, right) => {
1055 left.push_or_terms(terms);
1056 right.push_or_terms(terms);
1057 }
1058 _ => terms.push(self),
1059 }
1060 }
1061
1062 fn rebuild_from_extracted_byte_terms(terms: &[&Self]) -> Option<Self> {
1063 if terms.len() <= 1 {
1064 return None;
1065 }
1066
1067 let mut source = None;
1068 let mut seen = [false; 32];
1069 for term in terms {
1070 if term.as_const().is_some_and(|value| value.is_zero()) {
1071 continue;
1072 }
1073 let (term_source, index) = term.extracted_shifted_byte_term()?;
1074 match &source {
1075 Some(source) if source != &term_source => return None,
1076 Some(_) => {}
1077 None => source = Some(term_source),
1078 }
1079 seen[index] = true;
1080 }
1081
1082 let source = source?;
1083 for (index, seen) in seen.into_iter().enumerate() {
1084 if !seen && source.known_byte(index) != Some(0) {
1085 return None;
1086 }
1087 }
1088 Some(source)
1089 }
1090
1091 fn extracted_shifted_byte_term(&self) -> Option<(Self, usize)> {
1092 match self.kind() {
1093 SymExprKind::BinOp(SymBinOp::Shl, byte, shift) => {
1094 let shift = shift.as_const()?;
1095 let Ok(shift) = usize::try_from(shift) else { return None };
1096 if shift % 8 != 0 || shift > 248 {
1097 return None;
1098 }
1099 let index = 31 - shift / 8;
1100 let source = byte.extracted_unshifted_byte_source(index)?;
1101 Some((source, index))
1102 }
1103 _ => self.extracted_unshifted_byte_source(31).map(|source| (source, 31)),
1104 }
1105 }
1106
1107 fn extracted_unshifted_byte_source(&self, index: usize) -> Option<Self> {
1108 let expr = self.strip_low_byte_mask();
1109 if index == 31 {
1110 return Some(expr.clone());
1111 }
1112 let SymExprKind::BinOp(SymBinOp::Shr, source, shift) = expr.kind() else { return None };
1113 let shift = shift.as_const()?;
1114 (shift == U256::from((31 - index) * 8)).then(|| source.clone())
1115 }
1116
1117 fn rebuild_from_shifted_word_fragments(terms: &[&Self]) -> Option<Self> {
1118 if terms.len() != 2 {
1119 return None;
1120 }
1121
1122 let left_low = terms[0].low_word_fragment();
1123 let right_low = terms[1].low_word_fragment();
1124 let left_high = terms[0].shifted_high_word_fragment();
1125 let right_high = terms[1].shifted_high_word_fragment();
1126 match (left_low, right_low, left_high, right_high) {
1127 (Some((low_source, low_bits)), None, None, Some((high_source, high_bits)))
1128 | (None, Some((low_source, low_bits)), Some((high_source, high_bits)), None)
1129 if low_source == high_source && low_bits == high_bits =>
1130 {
1131 Some(low_source)
1132 }
1133 _ => None,
1134 }
1135 }
1136
1137 fn low_word_fragment(&self) -> Option<(Self, usize)> {
1138 let SymExprKind::BinOp(SymBinOp::And, left, right) = self.kind() else { return None };
1139 let mask = right.as_const()?;
1140 mask_low_bits(mask).map(|bits| (left.clone(), bits))
1141 }
1142
1143 fn shifted_high_word_fragment(&self) -> Option<(Self, usize)> {
1144 let SymExprKind::BinOp(SymBinOp::Shl, value, shift) = self.kind() else { return None };
1145 let bits = shift.as_const().and_then(|shift| usize::try_from(shift).ok())?;
1146 if bits == 0 || bits >= 256 {
1147 return None;
1148 }
1149
1150 let (source, source_shift, width) = value.shifted_low_fragment_source()?;
1151 (source_shift == bits && width == 256 - bits).then_some((source, bits))
1152 }
1153
1154 fn shifted_low_fragment_source(&self) -> Option<(Self, usize, usize)> {
1155 let SymExprKind::BinOp(SymBinOp::And, left, right) = self.kind() else { return None };
1156 let mask = right.as_const()?;
1157 Self::shifted_low_fragment_source_with_mask(left, mask)
1158 }
1159
1160 fn shifted_low_fragment_source_with_mask(
1161 value: &Self,
1162 mask: U256,
1163 ) -> Option<(Self, usize, usize)> {
1164 let width = mask_low_bits(mask)?;
1165 match value.kind() {
1166 SymExprKind::BinOp(SymBinOp::Shr, source, shift) => {
1167 let shift = shift.as_const().and_then(|shift| usize::try_from(shift).ok())?;
1168 Some((source.clone(), shift, width))
1169 }
1170 _ => Some((value.clone(), 0, width)),
1171 }
1172 }
1173
1174 pub(crate) fn low_byte(self, cx: &mut SymCx) -> Self {
1175 if let Some(word) = self.as_const() {
1176 return Self::constant(cx, U256::from(word.to::<u8>()));
1177 }
1178 let mask = Self::constant(cx, U256::from(0xff));
1179 Self::binop(cx, SymBinOp::And, self, mask)
1180 }
1181
1182 pub(crate) fn into_byte_exprs(self, cx: &mut SymCx) -> Vec<Self> {
1183 SymBytes::word(cx, self).materialize(cx)
1184 }
1185
1186 pub(crate) fn into_bytes(self, cx: &mut SymCx) -> SymBytes {
1187 SymBytes::word(cx, self)
1188 }
1189
1190 pub(crate) fn from_bytes(cx: &mut SymCx, bytes: impl IntoIterator<Item = Self>) -> Self {
1191 let bytes = bytes.into_iter().collect::<Vec<_>>();
1192 if let Ok(concrete) = concrete_expr_bytes(&bytes, "symbolic word bytes") {
1193 let mut word = [0u8; 32];
1194 for (idx, byte) in concrete.into_iter().take(32).enumerate() {
1195 word[idx] = byte;
1196 }
1197 return Self::constant(cx, U256::from_be_bytes(word));
1198 }
1199
1200 if let Some(expr) = word_from_extracted_bytes(&bytes) {
1201 return expr;
1202 }
1203
1204 let mut expr = Self::zero(cx);
1205 for (idx, byte) in bytes.into_iter().take(32).enumerate() {
1206 let shift = (31 - idx) * 8;
1207 let byte = byte.low_byte(cx);
1208 let byte = if shift == 0 {
1209 byte
1210 } else {
1211 let shift = Self::constant(cx, U256::from(shift));
1212 Self::binop(cx, SymBinOp::Shl, byte, shift)
1213 };
1214 expr = Self::binop(cx, SymBinOp::Or, expr, byte);
1215 }
1216 expr
1217 }
1218
1219 pub(crate) fn as_const(&self) -> Option<U256> {
1220 match self.kind() {
1221 SymExprKind::Const(value) => Some(*value),
1222 _ => None,
1223 }
1224 }
1225
1226 pub(crate) fn eval(&self) -> Option<U256> {
1227 self.eval_model_if_complete(&NoopModel).ok().flatten()
1228 }
1229
1230 pub(crate) fn eval_model<M: SymbolicModelLookup + ?Sized>(
1231 &self,
1232 model: &M,
1233 ) -> Result<U256, SymbolicError> {
1234 ModelEvaluator::new(model).eval_word(self)
1235 }
1236
1237 pub(crate) fn eval_model_if_complete<M: SymbolicModelLookup + ?Sized>(
1238 &self,
1239 model: &M,
1240 ) -> Result<Option<U256>, SymbolicError> {
1241 let mut vars = SymbolicVars::default();
1242 self.collect_eval_vars(&mut vars);
1243 if vars.iter().copied().all(|var| model.contains_name(var)) {
1244 self.eval_model(model).map(Some)
1245 } else {
1246 Ok(None)
1247 }
1248 }
1249
1250 pub(crate) fn assign_model_value(&self, model: &mut SymbolicModel, value: U256) -> bool {
1251 match self.kind() {
1252 SymExprKind::Const(existing) => *existing == value,
1253 SymExprKind::Var(var) => {
1254 if let Some(existing) = model.get(var) {
1255 *existing == value
1256 } else {
1257 model.insert(*var, value);
1258 true
1259 }
1260 }
1261 SymExprKind::GasLeft(symbol) => {
1262 if let Some(existing) = model.get(symbol) {
1263 *existing == value
1264 } else {
1265 model.insert(*symbol, value);
1266 true
1267 }
1268 }
1269 _ => false,
1270 }
1271 }
1272
1273 pub(crate) fn bool_word_condition(&self) -> Option<SymBoolExpr> {
1274 let SymExprKind::Ite(condition, then_expr, else_expr) = self.kind() else {
1275 return None;
1276 };
1277 Self::bool_word_condition_from_parts(condition, then_expr, else_expr)
1278 }
1279
1280 pub(in crate::runtime) fn bitwise_bool_word_condition(
1281 &self,
1282 cx: &mut SymCx,
1283 ) -> Option<SymBoolExpr> {
1284 let mut pending = vec![self.clone()];
1285 let mut seen_words = HashSet::<Self>::default();
1286 let mut leaf_conditions = IndexSet::<SymBoolExpr>::default();
1287 let mut bit_widths = HashMap::default();
1288 let mut remaining = MAX_BITWISE_BOOL_WORD_VISITS;
1289 while let Some(word) = pending.pop() {
1290 if !seen_words.insert(word.clone()) {
1291 continue;
1292 }
1293 if remaining == 0 {
1294 return None;
1295 }
1296 remaining -= 1;
1297
1298 if let Some(condition) = word.direct_bool_word_condition(cx) {
1299 leaf_conditions.insert(condition);
1300 continue;
1301 }
1302 if let SymExprKind::BinOp(SymBinOp::Or, left, right) = word.kind() {
1303 pending.push(right.clone());
1304 pending.push(left.clone());
1305 continue;
1306 }
1307
1308 if word.unsigned_bits_cached(&mut bit_widths, &mut remaining) == Some(1) {
1309 let zero = Self::zero(cx);
1310 let (word, zero) = Self::ordered_commutative_operands(word, zero);
1311 let zero_check =
1312 SymBoolExpr::from_kind(cx, SymBoolExprKind::Cmp(SymCmpOp::Eq, word, zero));
1313 leaf_conditions.insert(zero_check.not(cx));
1314 continue;
1315 }
1316 return None;
1317 }
1318
1319 Some(SymBoolExpr::or(cx, leaf_conditions.into_iter().collect()))
1320 }
1321
1322 fn direct_bool_word_condition(&self, cx: &mut SymCx) -> Option<SymBoolExpr> {
1328 let SymExprKind::Ite(condition, then_expr, else_expr) = self.kind() else {
1329 return None;
1330 };
1331 match (then_expr.as_const(), else_expr.as_const()) {
1332 (Some(then_value), Some(else_value))
1333 if then_value == U256::ONE && else_value.is_zero() =>
1334 {
1335 Some(condition.clone())
1336 }
1337 (Some(then_value), Some(else_value))
1338 if then_value.is_zero() && else_value == U256::ONE =>
1339 {
1340 Some(condition.clone().not(cx))
1341 }
1342 _ => None,
1343 }
1344 }
1345
1346 fn bool_word_condition_from_parts(
1347 condition: &SymBoolExpr,
1348 then_expr: &Self,
1349 else_expr: &Self,
1350 ) -> Option<SymBoolExpr> {
1351 match (then_expr.as_const(), else_expr.as_const()) {
1352 (Some(then_value), Some(else_value))
1353 if then_value == U256::ONE && else_value.is_zero() =>
1354 {
1355 Some(condition.clone())
1356 }
1357 (Some(then_value), Some(else_value))
1358 if then_value.is_zero() && else_value == U256::ONE =>
1359 {
1360 None
1361 }
1362 _ => None,
1363 }
1364 }
1365
1366 pub(crate) fn into_zero_bool(self, cx: &mut SymCx) -> SymBoolExpr {
1367 match self.kind() {
1368 SymExprKind::Const(value) => SymBoolExpr::constant(cx, value.is_zero()),
1369 SymExprKind::Ite(condition, then_expr, else_expr) => {
1370 match Self::bool_word_condition_from_parts(condition, then_expr, else_expr) {
1371 Some(condition) => SymBoolExpr::not_bool(cx, condition),
1372 None => {
1373 let zero = Self::zero(cx);
1374 SymBoolExpr::eq(cx, self, zero)
1375 }
1376 }
1377 }
1378 _ => {
1379 let zero = Self::zero(cx);
1380 SymBoolExpr::eq(cx, self, zero)
1381 }
1382 }
1383 }
1384
1385 pub(crate) fn nonzero_bool(self, cx: &mut SymCx) -> SymBoolExpr {
1386 let zero = self.into_zero_bool(cx);
1387 SymBoolExpr::not_bool(cx, zero)
1388 }
1389
1390 pub(crate) fn as_const_or(&self, reason: &'static str) -> Result<U256, SymbolicError> {
1391 self.as_const().ok_or(SymbolicError::Unsupported(reason))
1392 }
1393
1394 pub(crate) fn as_usize_or(&self, reason: &'static str) -> Result<usize, SymbolicError> {
1395 let value = self.as_const_or(reason)?;
1396 usize::try_from(value).map_err(|_| SymbolicError::Unsupported(reason))
1397 }
1398
1399 pub(crate) fn contains_keccak(&self) -> bool {
1400 self.visit_bool(|expr| matches!(expr.kind(), SymExprKind::Keccak { .. }))
1401 }
1402
1403 pub(crate) fn contains_gasleft(&self) -> bool {
1404 self.visit_bool(|expr| expr.is_raw_gasleft())
1405 }
1406
1407 pub(crate) fn contains_udiv(&self) -> bool {
1408 self.visit_bool(|expr| matches!(expr.kind(), SymExprKind::BinOp(SymBinOp::UDiv, _, _)))
1409 }
1410
1411 pub(crate) fn contains_ite(&self) -> bool {
1412 let mut visited = HashSet::<&Self>::default();
1413 self.contains_ite_cached(&mut visited, false)
1414 }
1415
1416 fn contains_ite_cached<'a>(&'a self, visited: &mut HashSet<&'a Self>, memoize: bool) -> bool {
1417 match self.kind() {
1418 SymExprKind::Const(_) | SymExprKind::Var(_) | SymExprKind::GasLeft(_) => return false,
1419 SymExprKind::Ite(_, _, _) => return true,
1420 _ => {}
1421 }
1422 if memoize && !visited.insert(self) {
1423 return false;
1424 }
1425
1426 match self.kind() {
1427 SymExprKind::Keccak { len, bytes, .. } => {
1428 len.contains_ite_cached(visited, true)
1429 || bytes.iter().any(|byte| byte.contains_ite_cached(visited, true))
1430 }
1431 SymExprKind::Hash { bytes, .. } => {
1432 bytes.iter().any(|byte| byte.contains_ite_cached(visited, true))
1433 }
1434 SymExprKind::Not(value) => value.contains_ite_cached(visited, true),
1435 SymExprKind::BinOp(_, left, right) => {
1436 left.contains_ite_cached(visited, true) || right.contains_ite_cached(visited, true)
1437 }
1438 SymExprKind::TernOp(_, left, right, modulus) => {
1439 left.contains_ite_cached(visited, true)
1440 || right.contains_ite_cached(visited, true)
1441 || modulus.contains_ite_cached(visited, true)
1442 }
1443 SymExprKind::Const(_)
1444 | SymExprKind::Var(_)
1445 | SymExprKind::GasLeft(_)
1446 | SymExprKind::Ite(_, _, _) => unreachable!("leaf expression handled before descent"),
1447 }
1448 }
1449
1450 pub(in crate::runtime) fn udiv_operands(&self) -> Option<(&Self, &Self)> {
1451 match self.kind() {
1452 SymExprKind::BinOp(SymBinOp::UDiv, numerator, denominator) => {
1453 Some((numerator, denominator))
1454 }
1455 _ => None,
1456 }
1457 }
1458
1459 pub(crate) fn collect_eval_vars(&self, vars: &mut SymbolicVars) {
1460 let _ = self.visit(&mut |expr| {
1461 if let Some(var) = expr.kind().get_eval_var() {
1462 vars.insert(var);
1463 }
1464 ControlFlow::<()>::Continue(())
1465 });
1466 }
1467
1468 pub(crate) fn known_byte(&self, index: usize) -> Option<u8> {
1469 debug_assert!(index < 32);
1470 match self.kind() {
1471 SymExprKind::Const(value) => Some(value.to_be_bytes::<32>()[index]),
1472 SymExprKind::Var(_)
1473 | SymExprKind::GasLeft(_)
1474 | SymExprKind::Keccak { .. }
1475 | SymExprKind::Hash { .. } => None,
1476 SymExprKind::Not(value) => value.known_byte(index).map(|byte| !byte),
1477 SymExprKind::Ite(_, then_expr, else_expr) => {
1478 let then_byte = then_expr.known_byte(index)?;
1479 let else_byte = else_expr.known_byte(index)?;
1480 (then_byte == else_byte).then_some(then_byte)
1481 }
1482 SymExprKind::BinOp(op, left, right) => match op {
1483 SymBinOp::And => match (left.known_byte(index), right.known_byte(index)) {
1484 (Some(left), Some(right)) => Some(left & right),
1485 (Some(0), _) | (_, Some(0)) => Some(0),
1486 _ => None,
1487 },
1488 SymBinOp::Or => Some(left.known_byte(index)? | right.known_byte(index)?),
1489 SymBinOp::Xor => Some(left.known_byte(index)? ^ right.known_byte(index)?),
1490 SymBinOp::Shl => {
1491 let shift = right.as_const()?;
1492 if shift >= U256::from(256) {
1493 return Some(0);
1494 }
1495 let shift = usize::try_from(shift).expect("checked byte shift");
1496 if shift % 8 != 0 {
1497 return None;
1498 }
1499 let source_index = index + shift / 8;
1500 if source_index >= 32 { Some(0) } else { left.known_byte(source_index) }
1501 }
1502 SymBinOp::Shr => {
1503 let shift = right.as_const()?;
1504 if shift >= U256::from(256) {
1505 return Some(0);
1506 }
1507 let shift = usize::try_from(shift).expect("checked byte shift");
1508 if shift % 8 != 0 {
1509 return None;
1510 }
1511 let byte_shift = shift / 8;
1512 if index < byte_shift { Some(0) } else { left.known_byte(index - byte_shift) }
1513 }
1514 SymBinOp::Add
1515 | SymBinOp::Sub
1516 | SymBinOp::Mul
1517 | SymBinOp::UDiv
1518 | SymBinOp::URem
1519 | SymBinOp::SDiv
1520 | SymBinOp::SRem
1521 | SymBinOp::Sar => None,
1522 },
1523 SymExprKind::TernOp(_, _, _, _) => None,
1524 }
1525 }
1526
1527 pub(crate) fn known_word(&self) -> Option<U256> {
1528 let mut word = [0u8; 32];
1529 for (idx, byte) in word.iter_mut().enumerate() {
1530 *byte = self.known_byte(idx)?;
1531 }
1532 Some(U256::from_be_bytes(word))
1533 }
1534
1535 pub(crate) fn unsigned_bits(&self) -> usize {
1536 let mut bit_widths = HashMap::default();
1537 let mut remaining = usize::MAX;
1538 self.unsigned_bits_cached(&mut bit_widths, &mut remaining).unwrap_or(256)
1539 }
1540
1541 fn unsigned_bits_cached(
1542 &self,
1543 bit_widths: &mut HashMap<Self, usize>,
1544 remaining: &mut usize,
1545 ) -> Option<usize> {
1546 if let Some(bits) = bit_widths.get(self) {
1547 return Some(*bits);
1548 }
1549
1550 let mut pending = vec![(self.clone(), false)];
1551 while let Some((expr, children_visited)) = pending.pop() {
1552 if bit_widths.contains_key(&expr) {
1553 continue;
1554 }
1555 if !children_visited {
1556 if *remaining == 0 {
1557 return None;
1558 }
1559 *remaining -= 1;
1560 pending.push((expr.clone(), true));
1561 match expr.kind() {
1562 SymExprKind::BinOp(SymBinOp::And, left, right)
1563 if right.as_const().is_some() =>
1564 {
1565 pending.push((left.clone(), false));
1566 }
1567 SymExprKind::BinOp(SymBinOp::Add | SymBinOp::Mul, left, right)
1568 | SymExprKind::Ite(_, left, right) => {
1569 pending.push((right.clone(), false));
1570 pending.push((left.clone(), false));
1571 }
1572 SymExprKind::BinOp(SymBinOp::Shl | SymBinOp::Shr, left, right)
1573 if right
1574 .as_const()
1575 .and_then(|shift| usize::try_from(shift).ok())
1576 .is_some() =>
1577 {
1578 pending.push((left.clone(), false));
1579 }
1580 SymExprKind::BinOp(SymBinOp::UDiv, left, _) => {
1581 pending.push((left.clone(), false));
1582 }
1583 SymExprKind::TernOp(_, _, _, modulus) => {
1584 pending.push((modulus.clone(), false));
1585 }
1586 _ => {}
1587 }
1588 continue;
1589 }
1590
1591 let bits = match expr.kind() {
1592 SymExprKind::Const(value) => value.bit_len().max(1),
1593 SymExprKind::BinOp(SymBinOp::And, left, right) => {
1594 if let Some(mask) = right.as_const() {
1595 bit_widths[left].min(mask.bit_len())
1596 } else {
1597 256
1598 }
1599 }
1600 SymExprKind::BinOp(SymBinOp::Add, left, right) => {
1601 bit_widths[left].max(bit_widths[right]).saturating_add(1).min(256)
1602 }
1603 SymExprKind::BinOp(SymBinOp::Mul, left, right) => {
1604 bit_widths[left].saturating_add(bit_widths[right]).min(256)
1605 }
1606 SymExprKind::BinOp(SymBinOp::Shl, left, right) => right
1607 .as_const()
1608 .and_then(|shift| usize::try_from(shift).ok())
1609 .map_or(256, |shift| bit_widths[left].saturating_add(shift).min(256)),
1610 SymExprKind::BinOp(SymBinOp::Shr, left, right) => right
1611 .as_const()
1612 .and_then(|shift| usize::try_from(shift).ok())
1613 .map_or(256, |shift| bit_widths[left].saturating_sub(shift).max(1)),
1614 SymExprKind::BinOp(SymBinOp::UDiv, left, _) => bit_widths[left],
1615 SymExprKind::TernOp(_, _, _, modulus) => bit_widths[modulus],
1616 SymExprKind::Ite(_, left, right) => bit_widths[left].max(bit_widths[right]),
1617 _ => 256,
1618 };
1619 bit_widths.insert(expr, bits);
1620 }
1621 bit_widths.get(self).copied()
1622 }
1623
1624 pub(crate) fn extracted_byte(&self, cx: &mut SymCx, index: usize) -> Self {
1625 debug_assert!(index < 32);
1626 let shift = Self::constant(cx, U256::from((31 - index) * 8));
1627 let shifted = Self::binop(cx, SymBinOp::Shr, self.clone(), shift);
1628 let mask = Self::constant(cx, U256::from(0xff));
1629 Self::binop(cx, SymBinOp::And, shifted, mask)
1630 }
1631
1632 pub(crate) fn extracted_byte_source(&self, index: usize) -> Option<Self> {
1633 let expr = self.strip_low_byte_mask();
1634 if index == 31 {
1635 return Some(expr.clone());
1636 }
1637 let SymExprKind::BinOp(SymBinOp::Shr, source, shift) = expr.kind() else { return None };
1638 let shift = shift.as_const()?;
1639 (shift == U256::from((31 - index) * 8)).then(|| source.clone())
1640 }
1641
1642 pub(crate) fn strip_low_byte_mask(&self) -> &Self {
1643 match self.kind() {
1644 SymExprKind::BinOp(SymBinOp::And, left, right)
1645 if right.as_const() == Some(U256::from(0xff)) =>
1646 {
1647 left.strip_low_byte_mask()
1648 }
1649 _ => self,
1650 }
1651 }
1652
1653 pub(crate) fn byte_term(&self, cx: &mut SymCx, index: usize) -> Option<Self> {
1654 debug_assert!(index < 32);
1655
1656 match self.kind() {
1657 SymExprKind::Const(value) => {
1658 Some(Self::constant(cx, U256::from(value.to_be_bytes::<32>()[index])))
1659 }
1660 SymExprKind::Var(_)
1661 | SymExprKind::GasLeft(_)
1662 | SymExprKind::Keccak { .. }
1663 | SymExprKind::Hash { .. } => Some(self.extracted_byte(cx, index)),
1664 SymExprKind::Not(value) => {
1665 let value = value.byte_term(cx, index)?;
1666 Some(Self::not(cx, value))
1667 }
1668 SymExprKind::Ite(cond, then_expr, else_expr) => {
1669 let then_expr = then_expr.byte_term(cx, index)?;
1670 let else_expr = else_expr.byte_term(cx, index)?;
1671 Some(Self::ite(cx, cond.clone(), then_expr, else_expr))
1672 }
1673 SymExprKind::BinOp(op, left, right) => match op {
1674 SymBinOp::And => Self::binary_byte_term(
1675 cx,
1676 left,
1677 right,
1678 index,
1679 SymBinOp::And,
1680 |byte| byte == 0xff,
1681 |byte| byte == 0,
1682 ),
1683 SymBinOp::Or => Self::binary_byte_term(
1684 cx,
1685 left,
1686 right,
1687 index,
1688 SymBinOp::Or,
1689 |byte| byte == 0,
1690 |_| false,
1691 ),
1692 SymBinOp::Xor => Self::binary_byte_term(
1693 cx,
1694 left,
1695 right,
1696 index,
1697 SymBinOp::Xor,
1698 |byte| byte == 0,
1699 |_| false,
1700 ),
1701 SymBinOp::Shl => {
1702 let shift = right.eval()?;
1703 if shift >= U256::from(256) {
1704 return Some(Self::zero(cx));
1705 }
1706 let shift = usize::try_from(shift).expect("checked byte shift");
1707 if shift % 8 != 0 {
1708 return None;
1709 }
1710 let source_index = index + shift / 8;
1711 if source_index >= 32 {
1712 Some(Self::zero(cx))
1713 } else {
1714 left.byte_term(cx, source_index)
1715 }
1716 }
1717 SymBinOp::Shr => {
1718 let shift = right.eval()?;
1719 if shift >= U256::from(256) {
1720 return Some(Self::zero(cx));
1721 }
1722 let shift = usize::try_from(shift).expect("checked byte shift");
1723 if shift % 8 != 0 {
1724 return None;
1725 }
1726 let byte_shift = shift / 8;
1727 if index < byte_shift {
1728 Some(Self::zero(cx))
1729 } else {
1730 left.byte_term(cx, index - byte_shift)
1731 }
1732 }
1733 SymBinOp::Add
1734 | SymBinOp::Sub
1735 | SymBinOp::Mul
1736 | SymBinOp::UDiv
1737 | SymBinOp::URem
1738 | SymBinOp::SDiv
1739 | SymBinOp::SRem
1740 | SymBinOp::Sar => None,
1741 },
1742 SymExprKind::TernOp(_, _, _, _) => None,
1743 }
1744 }
1745
1746 fn binary_byte_term(
1747 cx: &mut SymCx,
1748 left: &Self,
1749 right: &Self,
1750 index: usize,
1751 op: SymBinOp,
1752 identity: impl Fn(u8) -> bool,
1753 absorbing: impl Fn(u8) -> bool,
1754 ) -> Option<Self> {
1755 let left = left.byte_term(cx, index)?;
1756 let right = right.byte_term(cx, index)?;
1757 match (
1758 left.as_const().map(|value| value.to::<u8>()),
1759 right.as_const().map(|value| value.to::<u8>()),
1760 ) {
1761 (Some(left), _) if absorbing(left) => Some(Self::constant(cx, U256::from(left))),
1762 (_, Some(right)) if absorbing(right) => Some(Self::constant(cx, U256::from(right))),
1763 (Some(left), _) if identity(left) => Some(right),
1764 (_, Some(right)) if identity(right) => Some(left),
1765 _ => Some(Self::binop(cx, op, left, right)),
1766 }
1767 }
1768
1769 pub(crate) fn equality_forces_const(
1770 &self,
1771 value: U256,
1772 expr: &Self,
1773 context: &[SymBoolExpr],
1774 ) -> Option<U256> {
1775 if self == expr {
1776 return Some(value);
1777 }
1778 self.equality_forces_const_inner(value, expr, context)
1779 }
1780
1781 fn equality_forces_const_inner(
1782 &self,
1783 value: U256,
1784 expr: &Self,
1785 context: &[SymBoolExpr],
1786 ) -> Option<U256> {
1787 let mask = masked_expr_matches(self.kind(), expr)?;
1788 if !(value & !mask).is_zero() || !context_forces_masked_expr(context, expr, mask) {
1789 return None;
1790 }
1791 Some(value)
1792 }
1793
1794 pub(crate) fn nonzero_forces_const(
1795 &self,
1796 target: &Self,
1797 context: &[SymBoolExpr],
1798 ) -> Option<U256> {
1799 match self.kind() {
1800 SymExprKind::Const(_)
1801 | SymExprKind::Var(_)
1802 | SymExprKind::GasLeft(_)
1803 | SymExprKind::Keccak { .. }
1804 | SymExprKind::Hash { .. }
1805 | SymExprKind::Not(_) => None,
1806 SymExprKind::Ite(cond, then_expr, else_expr) => {
1807 if then_expr.eval().is_some_and(|value| !value.is_zero())
1808 && else_expr.eval().is_some_and(|value| value.is_zero())
1809 {
1810 cond.forces_expr_const_with_context(target, context)
1811 } else {
1812 None
1813 }
1814 }
1815 SymExprKind::BinOp(SymBinOp::Or, left, right) => {
1816 if left.eval().is_some_and(|value| value.is_zero()) {
1817 return right.nonzero_forces_const(target, context);
1818 }
1819 if right.eval().is_some_and(|value| value.is_zero()) {
1820 return left.nonzero_forces_const(target, context);
1821 }
1822 None
1823 }
1824 SymExprKind::BinOp(SymBinOp::And, left, right) => {
1825 if left.eval().is_some_and(|value| !value.is_zero()) {
1826 return right.nonzero_forces_const(target, context);
1827 }
1828 if right.eval().is_some_and(|value| !value.is_zero()) {
1829 return left.nonzero_forces_const(target, context);
1830 }
1831 None
1832 }
1833 SymExprKind::BinOp(SymBinOp::Shl | SymBinOp::Shr, value, shift)
1834 if shift.eval().is_some_and(|shift| shift.is_zero()) =>
1835 {
1836 value.nonzero_forces_const(target, context)
1837 }
1838 SymExprKind::TernOp(_, _, _, _) => None,
1839 SymExprKind::BinOp(_, _, _) => None,
1840 }
1841 }
1842
1843 pub(crate) fn is_raw_gasleft(&self) -> bool {
1844 matches!(self.kind(), SymExprKind::GasLeft(_))
1845 }
1846
1847 pub(crate) fn add_const(cx: &mut SymCx, expr: Self, value: U256) -> Self {
1848 if value.is_zero() {
1849 return expr;
1850 }
1851 match expr.kind() {
1852 SymExprKind::Const(expr) => Self::constant(cx, expr.wrapping_add(value)),
1853 _ => {
1854 let value = Self::constant(cx, value);
1855 Self::binop(cx, SymBinOp::Add, expr, value)
1856 }
1857 }
1858 }
1859
1860 pub(crate) fn constant_difference(&self, other: &Self) -> Option<U256> {
1863 let mut differences = HashMap::default();
1864 let mut remaining = MAX_CONSTANT_DIFFERENCE_VISITS;
1865 self.constant_difference_cached(other, &mut differences, &mut remaining)
1866 }
1867
1868 fn constant_difference_cached(
1869 &self,
1870 other: &Self,
1871 differences: &mut HashMap<(Self, Self), Option<U256>>,
1872 remaining: &mut usize,
1873 ) -> Option<U256> {
1874 if self == other {
1875 return Some(U256::ZERO);
1876 }
1877 let key = (self.clone(), other.clone());
1878 if let Some(difference) = differences.get(&key) {
1879 return *difference;
1880 }
1881 *remaining = remaining.checked_sub(1)?;
1882
1883 let difference = match (self.kind(), other.kind()) {
1884 (SymExprKind::Const(left), SymExprKind::Const(right)) => {
1885 Some(left.wrapping_sub(*right))
1886 }
1887 (
1888 SymExprKind::Ite(left_condition, left_then, left_else),
1889 SymExprKind::Ite(right_condition, right_then, right_else),
1890 ) if left_condition == right_condition => {
1891 let then_difference =
1892 left_then.constant_difference_cached(right_then, differences, remaining)?;
1893 let else_difference =
1894 left_else.constant_difference_cached(right_else, differences, remaining)?;
1895 (then_difference == else_difference).then_some(then_difference)
1896 }
1897 (SymExprKind::BinOp(SymBinOp::Add, value, constant), _)
1898 if let Some(constant) = constant.as_const() =>
1899 {
1900 value
1901 .constant_difference_cached(other, differences, remaining)
1902 .map(|difference| difference.wrapping_add(constant))
1903 }
1904 (SymExprKind::BinOp(SymBinOp::Sub, value, constant), _)
1905 if let Some(constant) = constant.as_const() =>
1906 {
1907 value
1908 .constant_difference_cached(other, differences, remaining)
1909 .map(|difference| difference.wrapping_sub(constant))
1910 }
1911 (_, SymExprKind::BinOp(SymBinOp::Add, value, constant))
1912 if let Some(constant) = constant.as_const() =>
1913 {
1914 self.constant_difference_cached(value, differences, remaining)
1915 .map(|difference| difference.wrapping_sub(constant))
1916 }
1917 (_, SymExprKind::BinOp(SymBinOp::Sub, value, constant))
1918 if let Some(constant) = constant.as_const() =>
1919 {
1920 self.constant_difference_cached(value, differences, remaining)
1921 .map(|difference| difference.wrapping_add(constant))
1922 }
1923 _ => None,
1924 };
1925 differences.insert(key, difference);
1926 difference
1927 }
1928
1929 pub(crate) fn visit<B>(
1931 &self,
1932 visitor: &mut impl FnMut(&Self) -> ControlFlow<B>,
1933 ) -> ControlFlow<B> {
1934 visitor(self)?;
1935 match self.kind() {
1936 SymExprKind::Const(_) | SymExprKind::Var(_) | SymExprKind::GasLeft(_) => {}
1937 SymExprKind::Keccak { len, bytes, .. } => {
1938 len.visit(visitor)?;
1939 for byte in bytes.iter() {
1940 byte.visit(visitor)?;
1941 }
1942 }
1943 SymExprKind::Hash { bytes, .. } => {
1944 for byte in bytes.iter() {
1945 byte.visit(visitor)?;
1946 }
1947 }
1948 SymExprKind::Not(value) => value.visit(visitor)?,
1949 SymExprKind::BinOp(_, left, right) => {
1950 left.visit(visitor)?;
1951 right.visit(visitor)?;
1952 }
1953 SymExprKind::TernOp(_, left, right, modulus) => {
1954 left.visit(visitor)?;
1955 right.visit(visitor)?;
1956 modulus.visit(visitor)?;
1957 }
1958 SymExprKind::Ite(cond, left, right) => {
1959 cond.visit_exprs(visitor)?;
1960 left.visit(visitor)?;
1961 right.visit(visitor)?;
1962 }
1963 }
1964 ControlFlow::Continue(())
1965 }
1966
1967 pub(crate) fn visit_bool(&self, visitor: impl FnMut(&Self) -> bool) -> bool {
1969 visit_unique(Vec::new(), vec![self.clone()], visitor)
1970 }
1971
1972 pub(crate) fn fold(
1976 &self,
1977 cx: &mut SymCx,
1978 folder: &mut impl FnMut(&mut SymCx, Self) -> Self,
1979 ) -> Self {
1980 let mut folded = ExpressionFoldCache::default();
1981 self.fold_cached(cx, folder, &mut folded)
1982 }
1983
1984 pub(in crate::runtime::expr) fn fold_cached<'a>(
1985 &'a self,
1986 cx: &mut SymCx,
1987 folder: &mut impl FnMut(&mut SymCx, Self) -> Self,
1988 folded: &mut ExpressionFoldCache<'a>,
1989 ) -> Self {
1990 if let Some(expr) = folded.words.get(self) {
1991 return expr.clone();
1992 }
1993
1994 let expr = match self.kind() {
1995 SymExprKind::Const(_) | SymExprKind::Var(_) | SymExprKind::GasLeft(_) => self.clone(),
1996 SymExprKind::Keccak { name, len, bytes } => {
1997 let len = len.fold_cached(cx, folder, folded);
1998 let bytes = bytes.iter().map(|byte| byte.fold_cached(cx, folder, folded)).collect();
1999 Self::keccak_symbol(cx, *name, len, bytes)
2000 }
2001 SymExprKind::Hash { name, algorithm, bytes } => {
2002 let bytes = bytes.iter().map(|byte| byte.fold_cached(cx, folder, folded)).collect();
2003 Self::hash_symbol(cx, *name, algorithm, bytes)
2004 }
2005 SymExprKind::Not(value) => {
2006 let value = value.fold_cached(cx, folder, folded);
2007 Self::not(cx, value)
2008 }
2009 SymExprKind::BinOp(op, left, right) => {
2010 let left = left.fold_cached(cx, folder, folded);
2011 let right = right.fold_cached(cx, folder, folded);
2012 Self::binop(cx, *op, left, right)
2013 }
2014 SymExprKind::TernOp(op, left, right, modulus) => {
2015 let left = left.fold_cached(cx, folder, folded);
2016 let right = right.fold_cached(cx, folder, folded);
2017 let modulus = modulus.fold_cached(cx, folder, folded);
2018 Self::ternop(cx, *op, left, right, modulus)
2019 }
2020 SymExprKind::Ite(condition, then_expr, else_expr) => {
2021 let condition = condition.fold_exprs_cached(cx, folder, folded);
2022 let then_expr = then_expr.fold_cached(cx, folder, folded);
2023 let else_expr = else_expr.fold_cached(cx, folder, folded);
2024 Self::ite(cx, condition, then_expr, else_expr)
2025 }
2026 };
2027 let expr = folder(cx, expr);
2028 folded.words.insert(self, expr.clone());
2029 expr
2030 }
2031
2032 pub(in crate::runtime::expr) fn write_smt(&self, cx: &SymCx, out: &mut String) {
2033 match self.kind() {
2034 SymExprKind::Const(value) => {
2035 let _ = write!(out, "(_ bv{value} 256)");
2036 }
2037 SymExprKind::Var(symbol)
2038 | SymExprKind::GasLeft(symbol)
2039 | SymExprKind::Keccak { name: symbol, .. }
2040 | SymExprKind::Hash { name: symbol, .. } => out.push_str(cx.symbol_name(*symbol)),
2041 SymExprKind::Not(value) => {
2042 out.push_str("(bvnot ");
2043 value.write_smt(cx, out);
2044 out.push(')');
2045 }
2046 SymExprKind::BinOp(op, left, right) => {
2047 let _ = write!(out, "({} ", op.smt());
2048 left.write_smt(cx, out);
2049 out.push(' ');
2050 right.write_smt(cx, out);
2051 out.push(')');
2052 }
2053 SymExprKind::TernOp(op, left, right, modulus) => {
2054 write_smt_wide_modular_arithmetic(cx, out, op.smt(), left, right, modulus);
2055 }
2056 SymExprKind::Ite(cond, left, right) => {
2057 out.push_str("(ite ");
2058 cond.write_smt(cx, out);
2059 out.push(' ');
2060 left.write_smt(cx, out);
2061 out.push(' ');
2062 right.write_smt(cx, out);
2063 out.push(')');
2064 }
2065 }
2066 }
2067}
2068
2069const MAX_BRANCHLESS_REWRITE_NODES: usize = 256;
2072const MAX_BRANCHLESS_REWRITE_UNFOLDED_NODES: usize = 8 * 1024;
2073
2074struct UnfoldedNodeCounter {
2075 expr_nodes: HashMap<SymExpr, usize>,
2076 bool_nodes: HashMap<SymBoolExpr, usize>,
2077 remaining_unique_nodes: usize,
2078}
2079
2080impl UnfoldedNodeCounter {
2081 fn new() -> Self {
2082 Self {
2083 expr_nodes: HashMap::default(),
2084 bool_nodes: HashMap::default(),
2085 remaining_unique_nodes: MAX_BRANCHLESS_REWRITE_NODES,
2086 }
2087 }
2088
2089 fn expr_nodes(&mut self, expr: &SymExpr) -> Option<usize> {
2092 if let Some(nodes) = self.expr_nodes.get(expr) {
2093 return Some(*nodes);
2094 }
2095 if self.remaining_unique_nodes == 0 {
2096 return None;
2097 }
2098 self.remaining_unique_nodes -= 1;
2099
2100 let nodes = match expr.kind() {
2101 SymExprKind::Const(_) | SymExprKind::Var(_) | SymExprKind::GasLeft(_) => 1,
2102 SymExprKind::Keccak { len, bytes, .. } => {
2103 let mut nodes = 1usize.checked_add(self.expr_nodes(len)?)?;
2104 for byte in bytes.iter() {
2105 nodes = nodes.checked_add(self.expr_nodes(byte)?)?;
2106 }
2107 nodes
2108 }
2109 SymExprKind::Hash { bytes, .. } => {
2110 let mut nodes = 1usize;
2111 for byte in bytes.iter() {
2112 nodes = nodes.checked_add(self.expr_nodes(byte)?)?;
2113 }
2114 nodes
2115 }
2116 SymExprKind::Not(value) => 1usize.checked_add(self.expr_nodes(value)?)?,
2117 SymExprKind::BinOp(_, left, right) => {
2118 1usize.checked_add(self.expr_nodes(left)?)?.checked_add(self.expr_nodes(right)?)?
2119 }
2120 SymExprKind::TernOp(_, left, right, modulus) => 1usize
2121 .checked_add(self.expr_nodes(left)?)?
2122 .checked_add(self.expr_nodes(right)?)?
2123 .checked_add(self.expr_nodes(modulus)?)?,
2124 SymExprKind::Ite(condition, then_expr, else_expr) => 1usize
2125 .checked_add(self.bool_nodes(condition)?)?
2126 .checked_add(self.expr_nodes(then_expr)?)?
2127 .checked_add(self.expr_nodes(else_expr)?)?,
2128 };
2129 if nodes > MAX_BRANCHLESS_REWRITE_UNFOLDED_NODES {
2130 return None;
2131 }
2132 self.expr_nodes.insert(expr.clone(), nodes);
2133 Some(nodes)
2134 }
2135
2136 fn bool_nodes(&mut self, expr: &SymBoolExpr) -> Option<usize> {
2137 if let Some(nodes) = self.bool_nodes.get(expr) {
2138 return Some(*nodes);
2139 }
2140 if self.remaining_unique_nodes == 0 {
2141 return None;
2142 }
2143 self.remaining_unique_nodes -= 1;
2144
2145 let nodes = match expr.kind() {
2146 SymBoolExprKind::Const(_) => 1,
2147 SymBoolExprKind::Not(value) => 1usize.checked_add(self.bool_nodes(value)?)?,
2148 SymBoolExprKind::And(values) => {
2149 let mut nodes = 1usize;
2150 for value in values.iter() {
2151 nodes = nodes.checked_add(self.bool_nodes(value)?)?;
2152 }
2153 nodes
2154 }
2155 SymBoolExprKind::Cmp(_, left, right) => {
2156 1usize.checked_add(self.expr_nodes(left)?)?.checked_add(self.expr_nodes(right)?)?
2157 }
2158 };
2159 if nodes > MAX_BRANCHLESS_REWRITE_UNFOLDED_NODES {
2160 return None;
2161 }
2162 self.bool_nodes.insert(expr.clone(), nodes);
2163 Some(nodes)
2164 }
2165}
2166
2167fn write_smt_wide_modular_arithmetic(
2168 cx: &SymCx,
2169 out: &mut String,
2170 op: &'static str,
2171 left: &SymExpr,
2172 right: &SymExpr,
2173 modulus: &SymExpr,
2174) {
2175 out.push_str("(ite (= ");
2180 modulus.write_smt(cx, out);
2181 out.push_str(" (_ bv0 256)) (_ bv0 256) ((_ extract 255 0) (bvurem (");
2182 out.push_str(op);
2183 out.push_str(" ((_ zero_extend 256) ");
2184 left.write_smt(cx, out);
2185 out.push_str(") ((_ zero_extend 256) ");
2186 right.write_smt(cx, out);
2187 out.push_str(")) ((_ zero_extend 256) ");
2188 modulus.write_smt(cx, out);
2189 out.push_str("))))");
2190}
2191
2192#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2193pub(crate) enum SymTernOp {
2194 AddMod,
2195 MulMod,
2196}
2197
2198impl SymTernOp {
2199 pub(crate) const fn smt(self) -> &'static str {
2200 match self {
2201 Self::AddMod => "bvadd",
2202 Self::MulMod => "bvmul",
2203 }
2204 }
2205
2206 pub(crate) fn eval(self, left: U256, right: U256, modulus: U256) -> U256 {
2207 if modulus.is_zero() {
2208 return U256::ZERO;
2209 }
2210 match self {
2211 Self::AddMod => left.add_mod(right, modulus),
2212 Self::MulMod => left.mul_mod(right, modulus),
2213 }
2214 }
2215}
2216
2217#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2218pub(crate) enum SymBinOp {
2219 Add,
2220 Sub,
2221 Mul,
2222 UDiv,
2223 URem,
2224 SDiv,
2225 SRem,
2226 And,
2227 Or,
2228 Xor,
2229 Shl,
2230 Shr,
2231 Sar,
2232}
2233
2234impl SymBinOp {
2235 pub(crate) const fn smt(self) -> &'static str {
2236 match self {
2237 Self::Add => "bvadd",
2238 Self::Sub => "bvsub",
2239 Self::Mul => "bvmul",
2240 Self::UDiv => "bvudiv",
2241 Self::URem => "bvurem",
2242 Self::SDiv => "bvsdiv",
2243 Self::SRem => "bvsrem",
2244 Self::And => "bvand",
2245 Self::Or => "bvor",
2246 Self::Xor => "bvxor",
2247 Self::Shl => "bvshl",
2248 Self::Shr => "bvlshr",
2249 Self::Sar => "bvashr",
2250 }
2251 }
2252
2253 pub(crate) fn eval(self, left: U256, right: U256) -> U256 {
2254 match self {
2255 Self::Add => left.wrapping_add(right),
2256 Self::Sub => left.wrapping_sub(right),
2257 Self::Mul => left.wrapping_mul(right),
2258 Self::UDiv => {
2259 if right.is_zero() {
2260 U256::ZERO
2261 } else {
2262 left / right
2263 }
2264 }
2265 Self::URem => {
2266 if right.is_zero() {
2267 U256::ZERO
2268 } else {
2269 left % right
2270 }
2271 }
2272 Self::SDiv => i256_div(left, right),
2273 Self::SRem => i256_mod(left, right),
2274 Self::And => left & right,
2275 Self::Or => left | right,
2276 Self::Xor => left ^ right,
2277 Self::Shl => {
2278 if right >= U256::from(256) {
2279 U256::ZERO
2280 } else {
2281 left << usize::try_from(right).expect("checked word shift")
2282 }
2283 }
2284 Self::Shr => {
2285 if right >= U256::from(256) {
2286 U256::ZERO
2287 } else {
2288 left >> usize::try_from(right).expect("checked word shift")
2289 }
2290 }
2291 Self::Sar => {
2292 if right >= U256::from(256) {
2293 left.arithmetic_shr(256)
2294 } else {
2295 left.arithmetic_shr(usize::try_from(right).expect("checked word shift"))
2296 }
2297 }
2298 }
2299 }
2300}
2301
2302pub(crate) fn keccak_word(cx: &mut SymCx, bytes: Vec<SymExpr>) -> SymExpr {
2303 let len = bytes.len();
2304 let len = SymExpr::constant(cx, U256::from(len));
2305 keccak_word_with_len(cx, bytes, len)
2306}
2307
2308pub(crate) fn keccak_word_with_len(cx: &mut SymCx, bytes: Vec<SymExpr>, len: SymExpr) -> SymExpr {
2309 if let Some(len) = len.as_const()
2310 && let Ok(len) = usize::try_from(len)
2311 && len <= bytes.len()
2312 && let Ok(concrete) = concrete_expr_bytes(&bytes[..len], "symbolic keccak input")
2313 {
2314 let hash = Into::<U256>::into(keccak256(concrete));
2315 if len == 64 {
2316 cx.record_concrete_keccak_preimage(hash, bytes[..len].to_vec().into());
2317 }
2318 return SymExpr::constant(cx, hash);
2319 }
2320
2321 let exprs = bytes;
2322 let identity = ExpressionDigests::identity(std::iter::once(&len).chain(&exprs));
2323 let name = stable_symbol(cx, "keccak", identity.as_slice());
2324 SymExpr::keccak_symbol(cx, name, len, exprs)
2325}
2326
2327pub(crate) fn symbolic_hash_word_with_len(
2328 cx: &mut SymCx,
2329 algorithm: &'static str,
2330 bytes: Vec<SymExpr>,
2331 len: SymExpr,
2332) -> SymExpr {
2333 let exprs = bytes;
2334 let identity = ExpressionDigests::identity(std::iter::once(&len).chain(&exprs));
2335 let name = stable_symbol(cx, algorithm, identity.as_slice());
2336 let mut identity = Vec::with_capacity(exprs.len() + 1);
2337 identity.push(len);
2338 identity.extend(exprs);
2339 SymExpr::hash_symbol(cx, name, algorithm, identity)
2340}
2341
2342pub(crate) fn create2_address_word(
2343 cx: &mut SymCx,
2344 state: &mut PathState,
2345 creator: Address,
2346 salt: SymExpr,
2347 initcode: &SymCode,
2348) -> Result<(SymExpr, Address), SymbolicError> {
2349 match (salt.as_const(), initcode.concrete_bytes(cx, "symbolic CREATE2 initcode")) {
2350 (Some(salt), Ok(initcode)) => {
2351 let address = creator.create2_from_code(salt.to_be_bytes::<32>(), &initcode);
2352 Ok((SymExpr::constant(cx, address_word(address)), address))
2353 }
2354 (None, Ok(initcode)) => {
2355 let initcode_hash = keccak256(&initcode);
2356 let word = symbolic_create2_address_word(
2357 cx,
2358 state,
2359 format!("address:{creator:?}"),
2360 salt,
2361 format!("hash:{initcode_hash:?}"),
2362 );
2363 let address = state.world.symbolic_address_slot(word.clone());
2364 Ok((word, address))
2365 }
2366 (_, Err(SymbolicError::Unsupported("symbolic CREATE2 initcode"))) => {
2367 let initcode_bytes = initcode.read_byte_exprs(cx, 0, initcode.len());
2368 let word = symbolic_create2_address_word(
2369 cx,
2370 state,
2371 format!("address:{creator:?}"),
2372 salt,
2373 format!("initcode:{:?}", ExpressionDigests::identity(&initcode_bytes)),
2374 );
2375 let address = state.world.symbolic_address_slot(word.clone());
2376 Ok((word, address))
2377 }
2378 (_, Err(err)) => Err(err),
2379 }
2380}
2381
2382pub(crate) fn compute_create2_address_word(
2383 cx: &mut SymCx,
2384 state: &mut PathState,
2385 deployer: SymExpr,
2386 salt: SymExpr,
2387 init_code_hash: SymExpr,
2388) -> Result<SymExpr, SymbolicError> {
2389 let deployer_concrete = state.constrained_word(cx, &deployer).map(word_to_address);
2390 let salt_concrete = state.constrained_word(cx, &salt);
2391 let init_code_hash_concrete = state.constrained_word(cx, &init_code_hash);
2392
2393 if let (Some(deployer), Some(salt), Some(init_code_hash)) =
2394 (deployer_concrete, salt_concrete, init_code_hash_concrete)
2395 {
2396 let init_code_hash = B256::from(init_code_hash);
2397 let address = deployer.create2(B256::from(salt), init_code_hash);
2398 return Ok(SymExpr::constant(cx, address_word(address)));
2399 }
2400
2401 let deployer_identity = deployer_identity(deployer_concrete, &deployer);
2402 let init_code_hash_identity = init_code_hash_concrete
2403 .map(|init_code_hash| {
2404 let init_code_hash = B256::from(init_code_hash);
2405 format!("hash:{init_code_hash:?}")
2406 })
2407 .unwrap_or_else(|| {
2408 format!("hash_expr:{:?}", ExpressionDigests::identity([&init_code_hash]))
2409 });
2410
2411 Ok(symbolic_create2_address_word(cx, state, deployer_identity, salt, init_code_hash_identity))
2412}
2413
2414pub(crate) fn compute_create_address_word(
2415 cx: &mut SymCx,
2416 state: &mut PathState,
2417 deployer: SymExpr,
2418 nonce: SymExpr,
2419) -> Result<SymExpr, SymbolicError> {
2420 let deployer_concrete = state.constrained_word(cx, &deployer).map(word_to_address);
2421 let nonce_concrete = state.constrained_word(cx, &nonce);
2422
2423 if let (Some(deployer), Some(nonce)) = (deployer_concrete, nonce_concrete) {
2424 let Ok(nonce) = u64::try_from(nonce) else {
2425 return Err(SymbolicError::Unsupported("symbolic vm.computeCreateAddress nonce"));
2426 };
2427 return Ok(SymExpr::constant(cx, address_word(deployer.create(nonce))));
2428 }
2429
2430 let deployer_identity = deployer_identity(deployer_concrete, &deployer);
2431 Ok(symbolic_create_address_word(cx, state, deployer_identity, nonce))
2432}
2433
2434pub(crate) fn symbolic_create_address_word(
2435 cx: &mut SymCx,
2436 state: &mut PathState,
2437 creator_identity: String,
2438 nonce: SymExpr,
2439) -> SymExpr {
2440 let nonce = ExpressionDigests::identity([&nonce]);
2441 let name =
2442 stable_symbol(cx, "create_address", format!("{creator_identity}:{nonce:?}").as_bytes());
2443 let word = SymExpr::get_var(cx, name);
2444 state.constraints.push(SymBoolExpr::cmp_word_const(cx, SymCmpOp::Ult, &word, U256::ONE << 160));
2445 word
2446}
2447
2448pub(crate) fn symbolic_create2_address_word(
2449 cx: &mut SymCx,
2450 state: &mut PathState,
2451 creator_identity: String,
2452 salt: SymExpr,
2453 initcode_identity: String,
2454) -> SymExpr {
2455 let name = stable_symbol(
2456 cx,
2457 "create2_address",
2458 format!(
2459 "{creator_identity}:{:?}:{initcode_identity}",
2460 ExpressionDigests::identity([&salt])
2461 )
2462 .as_bytes(),
2463 );
2464 let word = SymExpr::get_var(cx, name);
2465 state.constraints.push(SymBoolExpr::cmp_word_const(cx, SymCmpOp::Ult, &word, U256::ONE << 160));
2466 word
2467}
2468
2469fn deployer_identity(concrete: Option<Address>, deployer: &SymExpr) -> String {
2473 concrete
2474 .map(|deployer| format!("address:{deployer:?}"))
2475 .unwrap_or_else(|| format!("address_expr:{:?}", ExpressionDigests::identity([deployer])))
2476}