From 1a45bcff4042f8dd387c815144e99f561f0b1ee8 Mon Sep 17 00:00:00 2001 From: brock elmore Date: Sat, 3 Aug 2024 10:26:31 -0700 Subject: [PATCH 1/3] new env mechanism --- Cargo.lock | 1 + crates/graph/Cargo.toml | 2 + crates/graph/src/graph_elements.rs | 4 + crates/graph/src/nodes/block.rs | 1 + crates/graph/src/nodes/context/variables.rs | 29 ++- crates/graph/src/nodes/env_ctx.rs | 155 +++++++++++ crates/graph/src/nodes/func_ty.rs | 15 ++ crates/graph/src/nodes/mod.rs | 3 + crates/graph/src/var_type.rs | 1 + crates/pyrometer/src/builtin_fns.rs | 25 ++ crates/pyrometer/tests/test_data/env.sol | 84 +++--- .../tests/test_data/remapping_import.sol | 12 +- crates/pyrometer/tests/test_data/todo.sol | 8 - crates/shared/src/flattened.rs | 46 +++- .../src/context_builder/flattened.rs | 240 ++++++++++++------ crates/solc-expressions/src/env.rs | 63 ++++- .../src/func_call/func_caller.rs | 9 +- .../solc-expressions/src/func_call/helper.rs | 25 +- .../src/func_call/internal_call.rs | 4 +- .../src/func_call/intrinsic_call/block.rs | 32 ++- .../intrinsic_call/intrinsic_caller.rs | 4 +- .../src/func_call/intrinsic_call/msg.rs | 4 +- .../src/func_call/modifier.rs | 1 + .../src/member_access/member_trait.rs | 20 +- crates/solc-expressions/src/variable.rs | 4 +- 25 files changed, 637 insertions(+), 155 deletions(-) create mode 100644 crates/graph/src/nodes/env_ctx.rs diff --git a/Cargo.lock b/Cargo.lock index b14cd185..000c82e5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -916,6 +916,7 @@ dependencies = [ "ethers-core", "hex", "itertools", + "keccak-hash", "lazy_static", "petgraph", "pretty_assertions", diff --git a/crates/graph/Cargo.toml b/crates/graph/Cargo.toml index af3dff3a..6293f9c1 100644 --- a/crates/graph/Cargo.toml +++ b/crates/graph/Cargo.toml @@ -22,6 +22,8 @@ tracing-subscriber.workspace = true itertools = "0.10.5" lazy_static = "1.4.0" +keccak-hash = "0.10.0" + [dev-dependencies] pretty_assertions = "1.4.0" diff --git a/crates/graph/src/graph_elements.rs b/crates/graph/src/graph_elements.rs index d9b06887..95b8702d 100644 --- a/crates/graph/src/graph_elements.rs +++ b/crates/graph/src/graph_elements.rs @@ -100,6 +100,8 @@ pub enum Node { Concrete(Concrete), /// The `msg` global in solidity Msg(Box), + /// Local function call environmental data + EnvCtx(EnvCtx), /// The `block` global in solidity Block(Box), /// A yul-based function @@ -395,6 +397,8 @@ pub enum ContextEdge { // Range analysis /// Unused Range, + + Env, } #[derive(Default)] diff --git a/crates/graph/src/nodes/block.rs b/crates/graph/src/nodes/block.rs index 0e3c27f6..64d4bdfe 100644 --- a/crates/graph/src/nodes/block.rs +++ b/crates/graph/src/nodes/block.rs @@ -70,5 +70,6 @@ pub struct Block { pub timestamp: Option, /// The block's blobhash pub blobhash: Vec, + /// Blob base fee pub blobbasefee: Option, } diff --git a/crates/graph/src/nodes/context/variables.rs b/crates/graph/src/nodes/context/variables.rs index aa019d3e..c6e24e4e 100644 --- a/crates/graph/src/nodes/context/variables.rs +++ b/crates/graph/src/nodes/context/variables.rs @@ -1,7 +1,8 @@ use crate::{ - nodes::{ContextNode, ContextVarNode, ExprRet, VarNode}, + nodes::{ContextNode, ContextVarNode, EnvCtxNode, ExprRet, VarNode}, AnalyzerBackend, ContextEdge, Edge, GraphBackend, Node, TypeNode, }; +use petgraph::Direction; use shared::GraphError; use petgraph::visit::EdgeRef; @@ -95,6 +96,32 @@ impl ContextNode { .copied() } + pub fn env_or_recurse( + &self, + analyzer: &impl GraphBackend, + ) -> Result, GraphError> { + if let Some(env) = analyzer + .graph() + .edges_directed(self.0.into(), Direction::Incoming) + .find(|e| matches!(e.weight(), Edge::Context(ContextEdge::Env))) + .map(|e| e.source()) + { + return Ok(Some(env.into())); + } + + if let Some(parent) = self.ancestor_in_fn(analyzer, self.associated_fn(analyzer)?)? { + if let Some(in_parent) = parent.env_or_recurse(analyzer)? { + return Ok(Some(in_parent)); + } + } + + if let Some(parent) = self.underlying(analyzer)?.continuation_of() { + parent.env_or_recurse(analyzer) + } else { + Ok(None) + } + } + /// Gets a variable by name or recurses up the relevant scopes/contexts until it is found pub fn var_by_name_or_recurse( &self, diff --git a/crates/graph/src/nodes/env_ctx.rs b/crates/graph/src/nodes/env_ctx.rs new file mode 100644 index 00000000..a423799d --- /dev/null +++ b/crates/graph/src/nodes/env_ctx.rs @@ -0,0 +1,155 @@ +use crate::nodes::Msg; +use crate::{ + nodes::{Concrete, ContextNode, ContextVarNode}, + range::elem::Elem, + AnalyzerBackend, AsDotStr, ContextEdge, Edge, GraphBackend, Node, +}; +use solang_parser::pt::Loc; + +use shared::{GraphError, NodeIdx, RangeArena}; + +#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] +pub struct EnvCtxNode(pub usize); + +impl EnvCtxNode { + pub fn underlying<'a>( + &self, + analyzer: &'a impl GraphBackend, + ) -> Result<&'a EnvCtx, GraphError> { + match analyzer.node(*self) { + Node::EnvCtx(st) => Ok(st), + Node::Unresolved(ident) => Err(GraphError::UnknownVariable(format!( + "Could not find environment context: {}", + ident.name + ))), + e => Err(GraphError::NodeConfusion(format!( + "Node type confusion: expected node to be EnvCtx but it was: {e:?}" + ))), + } + } + + pub fn member_access( + &self, + analyzer: &impl GraphBackend, + name: &str, + ) -> Result, GraphError> { + Ok(self.underlying(analyzer)?.get(name)) + } +} + +impl AsDotStr for EnvCtxNode { + fn as_dot_str( + &self, + analyzer: &impl GraphBackend, + _arena: &mut RangeArena>, + ) -> String { + format!("env_ctx {{ {:?} }}", self.underlying(analyzer).unwrap()) + } +} + +impl From for NodeIdx { + fn from(val: EnvCtxNode) -> Self { + val.0.into() + } +} + +impl From for EnvCtxNode { + fn from(idx: NodeIdx) -> Self { + EnvCtxNode(idx.index()) + } +} + +#[derive(Debug, Clone, Default, Eq, PartialEq)] +pub struct EnvCtx { + pub this: Option, + pub data: Option, + pub sender: Option, + pub sig: Option, + pub value: Option, + pub origin: Option, + pub gasprice: Option, + pub gaslimit: Option, +} + +impl From for Node { + fn from(e: EnvCtx) -> Node { + Node::EnvCtx(e) + } +} + +impl EnvCtx { + pub fn from_msg( + analyzer: &mut impl AnalyzerBackend, + msg: &Msg, + loc: Loc, + ctx: ContextNode, + ) -> Result { + let data_var = msg.context_var_from_str("data", loc, ctx, analyzer)?; + let sender_var = msg.context_var_from_str("sender", loc, ctx, analyzer)?; + let sig_var = msg.context_var_from_str("sig", loc, ctx, analyzer)?; + let value_var = msg.context_var_from_str("value", loc, ctx, analyzer)?; + let origin_var = msg.context_var_from_str("origin", loc, ctx, analyzer)?; + let gasprice_var = msg.context_var_from_str("gasprice", loc, ctx, analyzer)?; + let gaslimit_var = msg.context_var_from_str("gaslimit", loc, ctx, analyzer)?; + let data_node = analyzer.add_node(data_var); + let sender_node = analyzer.add_node(sender_var); + let sig_node = analyzer.add_node(sig_var); + let value_node = analyzer.add_node(value_var); + let origin_node = analyzer.add_node(origin_var); + let gasprice_node = analyzer.add_node(gasprice_var); + let gaslimit_node = analyzer.add_node(gaslimit_var); + + Ok(EnvCtx { + this: None, + data: Some(data_node.into()), + sender: Some(sender_node.into()), + sig: Some(sig_node.into()), + value: Some(value_node.into()), + origin: Some(origin_node.into()), + gasprice: Some(gasprice_node.into()), + gaslimit: Some(gaslimit_node.into()), + }) + } + + pub fn set(&mut self, name: &str, val: impl Into) { + match name { + "this" => self.this = Some(val.into()), + "data" => self.data = Some(val.into()), + "sender" => self.sender = Some(val.into()), + "sig" => self.sig = Some(val.into()), + "value" => self.value = Some(val.into()), + "origin" => self.origin = Some(val.into()), + "gasprice" => self.gasprice = Some(val.into()), + "gaslimit" | "gas" => self.gaslimit = Some(val.into()), + _ => unreachable!(), + } + } + + pub fn get(&self, name: &str) -> Option { + match name { + "this" => self.this, + "data" => self.data, + "sender" => self.sender, + "sig" => self.sig, + "value" => self.value, + "origin" => self.origin, + "gasprice" => self.gasprice, + "gaslimit" | "gas" => self.gaslimit, + _ => None, + } + } + + pub fn get_name(name: &str) -> Option { + match name { + "this" => Some("this".to_string()), + "data" => Some("msg.data".to_string()), + "sender" => Some("msg.sender".to_string()), + "sig" => Some("msg.sig".to_string()), + "value" => Some("msg.value".to_string()), + "origin" => Some("tx.origin".to_string()), + "gasprice" => Some("tx.gasprice".to_string()), + "gaslimit" | "gas" => Some("gasleft()".to_string()), + _ => None, + } + } +} diff --git a/crates/graph/src/nodes/func_ty.rs b/crates/graph/src/nodes/func_ty.rs index b9359a42..90c81097 100644 --- a/crates/graph/src/nodes/func_ty.rs +++ b/crates/graph/src/nodes/func_ty.rs @@ -6,6 +6,7 @@ use crate::{ range::elem::Elem, AnalyzerBackend, AsDotStr, ContextEdge, Edge, GraphBackend, Node, SolcRange, VarType, }; +use ethers_core::types::H256; use shared::{GraphError, NodeIdx, RangeArena, Search, StorageLocation}; @@ -44,6 +45,20 @@ impl FunctionNode { } } + pub fn sig(&self, analyzer: &impl GraphBackend) -> Result, GraphError> { + if !self.is_public_or_ext(analyzer)? { + Ok(None) + } else { + let name = self.name(analyzer)?; + println!("name: {name}"); + let mut out = [0; 32]; + keccak_hash::keccak_256(name.as_bytes(), &mut out); + let mut sig = [0; 32]; + (0..4).for_each(|j| sig[j] = out[j]); + Ok(Some(Concrete::Bytes(4, H256(sig)))) + } + } + pub fn add_gas_cost( &mut self, analyzer: &mut impl GraphBackend, diff --git a/crates/graph/src/nodes/mod.rs b/crates/graph/src/nodes/mod.rs index e380b441..050bd2d4 100644 --- a/crates/graph/src/nodes/mod.rs +++ b/crates/graph/src/nodes/mod.rs @@ -45,3 +45,6 @@ pub use debug_reconstruction::*; mod yul_func; pub use yul_func::*; + +mod env_ctx; +pub use env_ctx::*; diff --git a/crates/graph/src/var_type.rs b/crates/graph/src/var_type.rs index 37487e97..a4895323 100644 --- a/crates/graph/src/var_type.rs +++ b/crates/graph/src/var_type.rs @@ -248,6 +248,7 @@ impl VarType { | Node::Entry | Node::Context(..) | Node::Msg(_) + | Node::EnvCtx(_) | Node::Block(_) | Node::YulFunction(..) => None, } diff --git a/crates/pyrometer/src/builtin_fns.rs b/crates/pyrometer/src/builtin_fns.rs index 519cc300..98bf594b 100644 --- a/crates/pyrometer/src/builtin_fns.rs +++ b/crates/pyrometer/src/builtin_fns.rs @@ -218,6 +218,15 @@ pub fn builtin_fns() -> AHashMap { Loc::Builtin, )))], ), + builtin_fn!( + name: Some(Identifier { + loc: Loc::Builtin, + name: "blobhash".to_string(), + }), + attributes: vec![FunctionAttribute::Visibility(Visibility::Internal(Some( + Loc::Builtin, + )))], + ), builtin_fn!( name: Some(Identifier { loc: Loc::Builtin, @@ -633,6 +642,22 @@ pub fn builtin_fns_inputs( name: None, }], ), + ( + "blobhash", + vec![FunctionParam { + loc: Loc::Builtin, + ty: analyzer.builtin_or_add(Builtin::Uint(64)), + order: 0, + storage: Some(StorageLocation::Memory(Loc::Implicit)), + name: None, + }], + vec![FunctionReturn { + loc: Loc::Builtin, + ty: analyzer.builtin_or_add(Builtin::Bytes(32)), + storage: None, + name: None, + }], + ), ( "abi.decode", vec![FunctionParam { diff --git a/crates/pyrometer/tests/test_data/env.sol b/crates/pyrometer/tests/test_data/env.sol index c8b3065b..f08168fa 100644 --- a/crates/pyrometer/tests/test_data/env.sol +++ b/crates/pyrometer/tests/test_data/env.sol @@ -6,46 +6,48 @@ contract Env { return msg.sender; } - function msg_data() public pure returns (bytes memory) { - return msg.data; - } + // function msg_data() public pure returns (bytes memory) { + // return msg.data; + // } - function testBlock() public payable { - /* - blockhash(uint blockNumber) returns (bytes32): hash of the given block - only works for 256 most recent blocks - blobhash(uint index) returns (bytes32): versioned hash of the index-th blob associated with the current transaction. A versioned hash consists of a single byte representing the version (currently 0x01), followed by the last 31 bytes of the SHA256 hash of the KZG commitment (EIP-4844). - block.basefee (uint): current block’s base fee (EIP-3198 and EIP-1559) - block.blobbasefee (uint): current block’s blob base fee (EIP-7516 and EIP-4844) - block.chainid (uint): current chain id - block.coinbase (address payable): current block miner’s address - block.difficulty (uint): current block difficulty (EVM < Paris). For other EVM versions it behaves as a deprecated alias for block.prevrandao that will be removed in the next breaking release - block.gaslimit (uint): current block gaslimit - block.number (uint): current block number - block.prevrandao (uint): random number provided by the beacon chain (EVM >= Paris) (see EIP-4399 ) - block.timestamp (uint): current block timestamp in seconds since Unix epoch - gasleft() returns (uint256): remaining gas - msg.data (bytes): complete calldata - msg.sender (address): sender of the message (current call) - msg.sig (bytes4): first four bytes of the calldata (i.e. function identifier) - msg.value (uint): number of wei sent with the message - tx.gasprice (uint): gas price of the transaction - tx.origin (address): sender of the transaction (full call chain) - */ - bytes32 a = blockhash(1); - uint c = block.basefee; - uint e = block.chainid; - address payable f = block.coinbase; - uint g = block.difficulty; - uint h = block.gaslimit; - uint i = block.number; - uint j = block.prevrandao; - uint k = block.timestamp; - uint l = gasleft(); - bytes memory m = msg.data; - address n = msg.sender; - bytes4 o = msg.sig; - uint p = msg.value; - uint q = tx.gasprice; - address r = tx.origin; - } + // function testBlock() public payable { + // /* + // blockhash(uint blockNumber) returns (bytes32): hash of the given block - only works for 256 most recent blocks + // blobhash(uint index) returns (bytes32): versioned hash of the index-th blob associated with the current transaction. A versioned hash consists of a single byte representing the version (currently 0x01), followed by the last 31 bytes of the SHA256 hash of the KZG commitment (EIP-4844). + // block.basefee (uint): current block’s base fee (EIP-3198 and EIP-1559) + // block.blobbasefee (uint): current block’s blob base fee (EIP-7516 and EIP-4844) + // block.chainid (uint): current chain id + // block.coinbase (address payable): current block miner’s address + // block.difficulty (uint): current block difficulty (EVM < Paris). For other EVM versions it behaves as a deprecated alias for block.prevrandao that will be removed in the next breaking release + // block.gaslimit (uint): current block gaslimit + // block.number (uint): current block number + // block.prevrandao (uint): random number provided by the beacon chain (EVM >= Paris) (see EIP-4399 ) + // block.timestamp (uint): current block timestamp in seconds since Unix epoch + // gasleft() returns (uint256): remaining gas + // msg.data (bytes): complete calldata + // msg.sender (address): sender of the message (current call) + // msg.sig (bytes4): first four bytes of the calldata (i.e. function identifier) + // msg.value (uint): number of wei sent with the message + // tx.gasprice (uint): gas price of the transaction + // tx.origin (address): sender of the transaction (full call chain) + // */ + // bytes32 a = blockhash(1); + // uint c = block.basefee; + // uint e = block.chainid; + // address payable f = block.coinbase; + // uint g = block.difficulty; + // uint h = block.gaslimit; + // uint i = block.number; + // uint j = block.prevrandao; + // uint k = block.timestamp; + // uint l = gasleft(); + // bytes memory m = msg.data; + // address n = msg.sender; + // bytes4 o = msg.sig; + // uint p = msg.value; + // uint q = tx.gasprice; + // address r = tx.origin; + // bytes32 b = blobhash(1); + // uint d = block.blobbasefee; + // } } diff --git a/crates/pyrometer/tests/test_data/remapping_import.sol b/crates/pyrometer/tests/test_data/remapping_import.sol index 0115658b..bc001820 100644 --- a/crates/pyrometer/tests/test_data/remapping_import.sol +++ b/crates/pyrometer/tests/test_data/remapping_import.sol @@ -3,9 +3,9 @@ pragma solidity ^0.8.0; import "@relative/relative_import.sol"; -contract RemappingImport { - function deploy() public { - Env env = Env(address(100)); - env.msg_sender(); - } -} +// contract RemappingImport { +// function deploy() public { +// Env env = Env(address(100)); +// env.msg_sender(); +// } +// } diff --git a/crates/pyrometer/tests/test_data/todo.sol b/crates/pyrometer/tests/test_data/todo.sol index 73d33e99..e01da815 100644 --- a/crates/pyrometer/tests/test_data/todo.sol +++ b/crates/pyrometer/tests/test_data/todo.sol @@ -2,14 +2,6 @@ pragma solidity ^0.8.0; contract Todo { - // will live in env.sol when added - function env() public view { - bytes32 b = blobhash(1); - uint d = block.blobbasefee; - b; - d; - } - // this will live in loops.sol when fixed function perform_break_literal() public pure { for (uint256 i = 0; i < 10; i++) { diff --git a/crates/shared/src/flattened.rs b/crates/shared/src/flattened.rs index 3104400e..e63a6168 100644 --- a/crates/shared/src/flattened.rs +++ b/crates/shared/src/flattened.rs @@ -3,7 +3,7 @@ use solang_parser::pt::{Expression, Loc, NamedArgument, Type, YulExpression}; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ExprFlag { - FunctionName(usize, bool, bool), + FunctionName(usize, usize, bool, bool), New, Negate, Requirement, @@ -44,7 +44,7 @@ pub enum FlatExpr { TestCommand(Loc, &'static str), NamedArgument(Loc, &'static str), - FunctionCallName(usize, bool, bool), + FunctionCallName(usize, usize, bool, bool), Requirement(Loc), Super(Loc, &'static str), @@ -61,9 +61,9 @@ pub enum FlatExpr { ArraySlice(Loc, bool, bool), ArrayLiteral(Loc, usize), MemberAccess(Loc, &'static str), - FunctionCall(Loc, usize), - FunctionCallBlock(Loc), - NamedFunctionCall(Loc, usize), + FunctionCall(Loc, usize, usize), + FunctionCallBlock(Loc, usize), + NamedFunctionCall(Loc, usize, usize), Not(Loc), Negate(Loc), Delete(Loc), @@ -168,8 +168,8 @@ impl std::fmt::Display for FlatExpr { ArrayTy(..) => write!(f, "[]"), ArrayIndexAccess(..) => write!(f, "[(..)]"), MemberAccess(_, field) => write!(f, ".{field}"), - FunctionCall(_, n) => write!(f, "({})", "_,".repeat(*n)), - NamedFunctionCall(_, _) => write!(f, "(..)"), + FunctionCall(_, n, _) => write!(f, "({})", "_,".repeat(*n)), + NamedFunctionCall(_, _, _) => write!(f, "(..)"), Not(_) => write!(f, "~"), Negate(_) => write!(f, "-"), Delete(_) => write!(f, "delete "), @@ -462,10 +462,34 @@ impl TryFrom<&Expression> for FlatExpr { MemberAccess(loc, _, name) => { FlatExpr::MemberAccess(*loc, string_to_static(name.name.clone())) } - FunctionCall(loc, _, input_exprs) => FlatExpr::FunctionCall(*loc, input_exprs.len()), - FunctionCallBlock(loc, _, _) => FlatExpr::FunctionCallBlock(*loc), - NamedFunctionCall(loc, _, input_exprs) => { - FlatExpr::NamedFunctionCall(*loc, input_exprs.len()) + FunctionCall(loc, call, input_exprs) => { + if let FunctionCallBlock(_, _, args) = &**call { + if let solang_parser::pt::Statement::Args(_, args) = &**args { + FlatExpr::FunctionCall(*loc, input_exprs.len(), args.len()) + } else { + unreachable!() + } + } else { + FlatExpr::FunctionCall(*loc, input_exprs.len(), 0) + } + } + FunctionCallBlock(loc, _, ref args) => { + if let solang_parser::pt::Statement::Args(_, args) = &**args { + FlatExpr::FunctionCallBlock(*loc, args.len()) + } else { + unreachable!() + } + } + NamedFunctionCall(loc, call, input_exprs) => { + if let FunctionCallBlock(_, _, args) = &**call { + if let solang_parser::pt::Statement::Args(_, args) = &**args { + FlatExpr::NamedFunctionCall(*loc, input_exprs.len(), args.len()) + } else { + unreachable!() + } + } else { + FlatExpr::NamedFunctionCall(*loc, input_exprs.len(), 0) + } } Not(loc, ..) => FlatExpr::Not(*loc), Delete(loc, ..) => FlatExpr::Delete(*loc), diff --git a/crates/solc-expressions/src/context_builder/flattened.rs b/crates/solc-expressions/src/context_builder/flattened.rs index 79611126..d89c200e 100644 --- a/crates/solc-expressions/src/context_builder/flattened.rs +++ b/crates/solc-expressions/src/context_builder/flattened.rs @@ -1,4 +1,4 @@ -use graph::elem::RangeDyn; +use graph::{elem::RangeDyn, nodes::EnvCtx}; use std::collections::BTreeMap; use crate::{ @@ -16,7 +16,7 @@ use graph::{ elem::{Elem, RangeConcrete, RangeExpr, RangeOp}, nodes::{ BuiltInNode, Builtin, Concrete, ConcreteNode, Context, ContextNode, ContextVar, - ContextVarNode, ContractNode, ExprRet, FunctionNode, KilledKind, StructNode, + ContextVarNode, ContractNode, ExprRet, FunctionNode, KilledKind, Msg, StructNode, TmpConstruction, YulFunction, }, AnalyzerBackend, ContextEdge, Edge, Node, SolcRange, TypeNode, VarType, @@ -91,11 +91,15 @@ pub trait Flatten: )); } } - Args(loc, _args) => { - self.push_expr(FlatExpr::Todo( - *loc, - "Args statements are currently unsupported", - )); + Args(loc, args) => { + // statements are left to right + args.iter().for_each(|arg| { + self.traverse_expression(&arg.expr, unchecked); + }); + + args.iter().for_each(|arg| { + self.push_expr(FlatExpr::from(arg)); + }); } If(loc, if_expr, true_body, maybe_false_body) => { // 1. Add conditional expressions @@ -627,7 +631,7 @@ pub trait Flatten: self.traverse_expression(func_expr, unchecked); self.push_expr(FlatExpr::New(*new_loc)); - self.push_expr(FlatExpr::FunctionCall(*func_loc, input_exprs.len())); + self.push_expr(FlatExpr::FunctionCall(*func_loc, input_exprs.len(), 0)); } NamedFunctionCall(loc, func_expr, input_args) => { input_args.iter().rev().for_each(|arg| { @@ -639,7 +643,7 @@ pub trait Flatten: input_args.iter().for_each(|arg| { self.push_expr(FlatExpr::from(arg)); }); - self.push_expr(FlatExpr::NamedFunctionCall(*loc, input_args.len())); + self.push_expr(FlatExpr::NamedFunctionCall(*loc, input_args.len(), 0)); } _ => { // add error @@ -835,23 +839,59 @@ pub trait Flatten: } // Function calls FunctionCallBlock(loc, func_expr, call_block) => { - self.traverse_statement(call_block, unchecked); self.traverse_expression(func_expr, unchecked); - self.push_expr(FlatExpr::FunctionCallBlock(*loc)); + let start_len = self.expr_stack().len(); + self.traverse_statement(call_block, unchecked); + let mut call_args: Vec = + self.expr_stack_mut().drain(start_len..).collect(); + let fn_name = self.expr_stack_mut().pop().unwrap(); + let num_args = call_args.len(); + let call_names = call_args.split_off(num_args / 2); + self.expr_stack_mut().extend(call_args); + self.push_expr(fn_name); + self.expr_stack_mut().extend(call_names); + self.push_expr(FlatExpr::FunctionCallBlock(*loc, num_args / 2)); } NamedFunctionCall(loc, func_expr, input_args) => { - input_args.iter().rev().for_each(|arg| { - self.traverse_expression(&arg.expr, unchecked); - }); - + let mut call_block_n = 0; self.traverse_expression(func_expr, unchecked); - match self.expr_stack_mut().pop().unwrap() { + let expr = self.expr_stack_mut().pop().unwrap(); + match expr { FlatExpr::Super(loc, name) => { - self.push_expr(FlatExpr::FunctionCallName(input_args.len(), true, true)); + input_args.iter().rev().for_each(|arg| { + self.traverse_expression(&arg.expr, unchecked); + }); + self.push_expr(FlatExpr::FunctionCallName(input_args.len(), 0, true, true)); self.push_expr(FlatExpr::Variable(loc, name)); } + FlatExpr::FunctionCallBlock(_, n) => { + call_block_n = n; + let len = self.expr_stack().len().saturating_sub(n); + let args = self.expr_stack_mut().split_off(len); + let prev_kind = self.expr_stack_mut().pop().unwrap(); + input_args.iter().rev().for_each(|arg| { + self.traverse_expression(&arg.expr, unchecked); + }); + self.push_expr(FlatExpr::FunctionCallName( + input_args.len(), + n, + false, + true, + )); + self.push_expr(prev_kind); + self.expr_stack_mut().extend(args); + self.push_expr(expr); + } other => { - self.push_expr(FlatExpr::FunctionCallName(input_args.len(), false, true)); + input_args.iter().rev().for_each(|arg| { + self.traverse_expression(&arg.expr, unchecked); + }); + self.push_expr(FlatExpr::FunctionCallName( + input_args.len(), + call_block_n, + false, + true, + )); self.push_expr(other); } } @@ -859,7 +899,11 @@ pub trait Flatten: input_args.iter().for_each(|arg| { self.push_expr(FlatExpr::from(arg)); }); - self.push_expr(FlatExpr::NamedFunctionCall(*loc, input_args.len())); + self.push_expr(FlatExpr::NamedFunctionCall( + *loc, + input_args.len(), + call_block_n, + )); } FunctionCall(loc, func_expr, input_exprs) => match &**func_expr { Variable(Identifier { name, .. }) if matches!(&**name, "require" | "assert") => { @@ -875,42 +919,49 @@ pub trait Flatten: } _ => { // func(inputs) - input_exprs.iter().rev().for_each(|expr| { - self.traverse_expression(expr, unchecked); - }); - self.traverse_expression(func_expr, unchecked); // For clarity we make these variables let mut is_super = false; let named_args = false; + let mut call_block_n = 0; let num_inputs = input_exprs.len(); - match self.expr_stack_mut().pop().unwrap() { + let kind = self.expr_stack_mut().pop().unwrap(); + match kind { FlatExpr::Super(loc, name) => { + input_exprs.iter().rev().for_each(|expr| { + self.traverse_expression(expr, unchecked); + }); is_super = true; self.push_expr(FlatExpr::FunctionCallName( - num_inputs, is_super, named_args, + num_inputs, 0, is_super, named_args, )); self.push_expr(FlatExpr::Variable(loc, name)); } - // mem @ FlatExpr::MemberAccess(..) => { - // // member.name(inputs) -> name(member, inputs) so we need - // // to make sure the member is passed as an input - // num_inputs += 1; - // self.push_expr(FlatExpr::FunctionCallName( - // num_inputs, is_super, named_args, - // )); - // self.push_expr(mem); - // } + FlatExpr::FunctionCallBlock(_, n) => { + call_block_n = n; + let prev_kind = self.expr_stack_mut().pop().unwrap(); + input_exprs.iter().rev().for_each(|expr| { + self.traverse_expression(expr, unchecked); + }); + self.push_expr(FlatExpr::FunctionCallName( + num_inputs, n, is_super, named_args, + )); + self.push_expr(prev_kind); + self.push_expr(kind); + } other => { + input_exprs.iter().rev().for_each(|expr| { + self.traverse_expression(expr, unchecked); + }); self.push_expr(FlatExpr::FunctionCallName( - num_inputs, is_super, named_args, + num_inputs, 0, is_super, named_args, )); self.push_expr(other); } } - self.push_expr(FlatExpr::FunctionCall(*loc, num_inputs)); + self.push_expr(FlatExpr::FunctionCall(*loc, num_inputs, call_block_n)); } }, // member @@ -946,6 +997,10 @@ pub trait Flatten: let res = func.add_params_to_ctx(ctx, self).into_expr_err(loc); self.add_if_err(res); + let msg = self.msg().underlying(self).unwrap().clone(); + let env = EnvCtx::from_msg(self, &msg, loc, ctx).into_expr_err(loc); + let env = self.add_if_err(env); + let res = self.func_call_inner( arena, true, // entry_call @@ -956,6 +1011,7 @@ pub trait Flatten: &[], None, // alt function name &None, // mod state + env, ); let _ = self.add_if_err(res); } @@ -1070,8 +1126,8 @@ pub trait Flatten: match next { Todo(loc, err_str) => Err(ExprErr::Todo(loc, err_str.to_string())), // Flag expressions - FunctionCallName(n, is_super, named_args) => { - ctx.set_expr_flag(self, ExprFlag::FunctionName(n, is_super, named_args)); + FunctionCallName(n, msg_n, is_super, named_args) => { + ctx.set_expr_flag(self, ExprFlag::FunctionName(n, msg_n, is_super, named_args)); Ok(()) } Negate(_) => { @@ -1161,8 +1217,8 @@ pub trait Flatten: // Function calling MemberAccess(..) => self.interp_member_access(arena, ctx, stack, next, parse_idx), - FunctionCall(..) => self.interp_func_call(arena, ctx, next, None), - FunctionCallBlock(_) => todo!(), + FunctionCall(..) => self.interp_func_call(arena, ctx, stack, next, None, parse_idx), + FunctionCallBlock(..) => Ok(()), NamedArgument(..) => Ok(()), NamedFunctionCall(..) => { self.interp_named_func_call(arena, ctx, stack, next, parse_idx) @@ -1369,17 +1425,26 @@ pub trait Flatten: unreachable!() }; - let member = ctx - .pop_n_latest_exprs(1, loc, self) - .into_expr_err(loc)? - .swap_remove(0); - // If the member access points to a library function, we need to keep the // member on the stack match ctx.take_expr_flag(self) { - Some(ExprFlag::FunctionName(n, super_call, named_args)) => { + Some(ExprFlag::FunctionName(n, msg_n, super_call, named_args)) => { + let mut member_and_inputs = ctx + .pop_n_latest_exprs(n + msg_n + 1, loc, self) + .into_expr_err(loc)?; + + let member = member_and_inputs.remove(member_and_inputs.len().saturating_sub(1)); + + member_and_inputs.into_iter().rev().for_each(|input| { + ctx.push_expr(input, self).unwrap(); + }); + let maybe_names = if named_args { - let start = parse_idx + 1; + let mut start = parse_idx + 1; + if msg_n > 0 { + // skip past s & FunctionCallBlock + start += msg_n + 1; + }; Some(self.get_named_args(stack, start, n)) } else { None @@ -1406,10 +1471,10 @@ pub trait Flatten: if was_lib_func { ctx.push_expr(member, self).into_expr_err(loc)?; match stack.get_mut(ctx.parse_idx(self)) { - Some(FlatExpr::FunctionCall(_, ref mut n)) => { + Some(FlatExpr::FunctionCall(_, ref mut n, _)) => { *n += 1; } - Some(FlatExpr::NamedFunctionCall(_, ref mut n)) => { + Some(FlatExpr::NamedFunctionCall(_, ref mut n, _)) => { *n += 1; } Some(_) | None => {} @@ -1437,6 +1502,10 @@ pub trait Flatten: } } _ => { + let member = ctx + .pop_n_latest_exprs(1, loc, self) + .into_expr_err(loc)? + .swap_remove(0); let was_lib_func = self.member_access(arena, ctx, member.clone(), name, loc)?; if was_lib_func { todo!("Got a library function without a function call?"); @@ -1930,7 +1999,7 @@ pub trait Flatten: }; match ctx.take_expr_flag(self) { - Some(ExprFlag::FunctionName(n, super_call, named_args)) => { + Some(ExprFlag::FunctionName(n, msg_n, super_call, named_args)) => { let maybe_names = if named_args { let start = parse_idx + 1; Some(self.get_named_args(stack, start, n)) @@ -2154,14 +2223,14 @@ pub trait Flatten: func_call: FlatExpr, parse_idx: usize, ) -> Result<(), ExprErr> { - let FlatExpr::NamedFunctionCall(_, n) = func_call else { + let FlatExpr::NamedFunctionCall(_, n, _) = func_call else { unreachable!() }; let names_start = parse_idx.saturating_sub(n); let names = self.get_named_args(stack, names_start, n); - self.interp_func_call(arena, ctx, func_call, Some(names)) + self.interp_func_call(arena, ctx, stack, func_call, Some(names), parse_idx) } fn get_named_args( @@ -2170,6 +2239,7 @@ pub trait Flatten: start: usize, n: usize, ) -> Vec<&'static str> { + println!("{n}, {:?}", &stack[start..start + n]); stack[start..start + n] .iter() .map(|named_arg| { @@ -2185,24 +2255,55 @@ pub trait Flatten: &mut self, arena: &mut RangeArena>, ctx: ContextNode, + stack: &mut Vec, func_call: FlatExpr, input_names: Option>, + parse_idx: usize, ) -> Result<(), ExprErr> { - let (loc, n) = match func_call { - FlatExpr::FunctionCall(loc, n) => (loc, n), - FlatExpr::NamedFunctionCall(loc, n) => (loc, n), + let (loc, n, call_block_inputs) = match func_call { + FlatExpr::FunctionCall(loc, n, call_block_inputs) => (loc, n, call_block_inputs), + FlatExpr::NamedFunctionCall(loc, n, call_block_inputs) => (loc, n, call_block_inputs), _ => unreachable!(), }; - let func_and_inputs = ctx - .pop_n_latest_exprs(n + 1, loc, self) + let mut func_and_inputs = ctx + .pop_n_latest_exprs(n + 1 + call_block_inputs, loc, self) .into_expr_err(loc)?; - let func = func_and_inputs - .first() - .unwrap() - .expect_single() - .into_expr_err(loc)?; + let mut inputs = func_and_inputs.split_off(1); + let [func] = into_sized(func_and_inputs); + let func = func.expect_single().into_expr_err(loc)?; + let call_args = inputs.split_off(n); + + let env = if !call_args.is_empty() { + let msg = self.msg().underlying(self).unwrap().clone(); + let mut env_ctx = EnvCtx::from_msg(self, &msg, loc, ctx).into_expr_err(loc)?; + let names = self.get_named_args( + stack, + parse_idx.saturating_sub( + // self + + named call block inputs + 1 + input_names.as_ref().map(|i| i.len()).unwrap_or(0) + call_block_inputs, + ), + call_block_inputs, + ); + names + .iter() + .rev() + .enumerate() + .try_for_each(|(i, name)| { + let input = call_args[i].expect_single().unwrap(); + let mut var = ContextVarNode::from(input).underlying(self)?.clone(); + var.name = EnvCtx::get_name(name).unwrap(); + var.display_name = var.name.clone(); + let var_node = self.add_node(var); + env_ctx.set(name, var_node); + Ok(()) + }) + .into_expr_err(loc)?; + Some(env_ctx) + } else { + None + }; let is_new_call = match ctx.peek_expr_flag(self) { Some(ExprFlag::New) => { @@ -2247,13 +2348,10 @@ pub trait Flatten: } else { let mut tmp_inputs = vec![]; tmp_inputs.resize(n, ExprRet::Null); - func_and_inputs[1..] - .iter() - .enumerate() - .for_each(|(i, ret)| { - let target_idx = mapping[&i]; - tmp_inputs[target_idx] = ret.clone(); - }); + inputs.iter().enumerate().for_each(|(i, ret)| { + let target_idx = mapping[&i]; + tmp_inputs[target_idx] = ret.clone(); + }); ret = Ok(Some(tmp_inputs)); } } @@ -2261,7 +2359,7 @@ pub trait Flatten: } else { Ok(None) }; - res?.unwrap_or(func_and_inputs[1..].to_vec()) + res?.unwrap_or(inputs) } else { vec![] }; @@ -2300,7 +2398,7 @@ pub trait Flatten: // its a builtin function call self.call_builtin(arena, ctx, &s.name(self).into_expr_err(loc)?, inputs, loc) } else { - self.func_call(arena, ctx, loc, &inputs, s, None, None) + self.func_call(arena, ctx, loc, &inputs, s, None, None, env) } } VarType::BuiltIn(bn, _) => { diff --git a/crates/solc-expressions/src/env.rs b/crates/solc-expressions/src/env.rs index 6c77f12c..b475660f 100644 --- a/crates/solc-expressions/src/env.rs +++ b/crates/solc-expressions/src/env.rs @@ -1,4 +1,6 @@ use crate::{func_call::helper::CallerHelper, func_call::modifier::ModifierCaller}; +use petgraph::visit::EdgeRef; +use petgraph::Direction; use graph::{ elem::Elem, @@ -17,12 +19,14 @@ pub trait Env: AnalyzerBackend + Sized { arena: &mut RangeArena>, ident: &Identifier, ctx: ContextNode, + loc: Loc, ) -> Result, ExprErr> { match &*ident.name { "msg" | "tx" => { + let env = ctx.env_or_recurse(self).into_expr_err(loc)?.unwrap(); ctx.add_gas_cost(self, shared::gas::BIN_OP_GAS) .into_expr_err(ident.loc)?; - ctx.push_expr(ExprRet::Single(self.msg().into()), self) + ctx.push_expr(ExprRet::Single(env.into()), self) .into_expr_err(ident.loc)?; Ok(Some(())) } @@ -109,6 +113,25 @@ pub trait Env: AnalyzerBackend + Sized { return Ok(ExprRet::Single(cvar)); } } + "blobbasefee" => { + if let Some(d) = self.block().underlying(self).into_expr_err(loc)?.basefee { + let c = Concrete::from(d); + (self.add_node(c).into(), "block.blobbasefee".to_string()) + } else { + let node = self.builtin_or_add(Builtin::Uint(256)); + let mut var = ContextVar::new_from_builtin(loc, node.into(), self) + .into_expr_err(loc)?; + var.name = "block.blobbasefee".to_string(); + var.display_name = "block.blobbasefee".to_string(); + var.is_tmp = false; + var.is_symbolic = true; + var.storage = Some(StorageLocation::Block(loc)); + let cvar = self.add_node(var); + ctx.add_var(cvar.into(), self).into_expr_err(loc)?; + self.add_edge(cvar, ctx, Edge::Context(ContextEdge::Variable)); + return Ok(ExprRet::Single(cvar)); + } + } "chainid" => { if let Some(d) = self.block().underlying(self).into_expr_err(loc)?.chainid { let c = Concrete::from(d); @@ -280,7 +303,14 @@ pub trait Env: AnalyzerBackend + Sized { } else { let (node, name) = match ident_name { "data" => { - if let Some(d) = self.msg().underlying(self).into_expr_err(loc)?.data.clone() { + if let Some(existing) = ctx + .var_by_name_or_recurse(self, "msg.data") + .into_expr_err(loc)? + { + return Ok(ExprRet::Single(existing.into())); + } else if let Some(d) = + self.msg().underlying(self).into_expr_err(loc)?.data.clone() + { let c = Concrete::from(d); (self.add_node(c).into(), "msg.data".to_string()) } else { @@ -300,7 +330,12 @@ pub trait Env: AnalyzerBackend + Sized { } } "sender" => { - if let Some(d) = self.msg().underlying(self).into_expr_err(loc)?.sender { + if let Some(existing) = ctx + .var_by_name_or_recurse(self, "msg.sender") + .into_expr_err(loc)? + { + return Ok(ExprRet::Single(existing.into())); + } else if let Some(d) = self.msg().underlying(self).into_expr_err(loc)?.sender { let c = Concrete::from(d); (self.add_node(c).into(), "msg.sender".to_string()) } else { @@ -319,7 +354,12 @@ pub trait Env: AnalyzerBackend + Sized { } } "sig" => { - if let Some(d) = self.msg().underlying(self).into_expr_err(loc)?.sig { + if let Some(existing) = ctx + .var_by_name_or_recurse(self, "msg.sig") + .into_expr_err(loc)? + { + return Ok(ExprRet::Single(existing.into())); + } else if let Some(d) = self.msg().underlying(self).into_expr_err(loc)?.sig { let c = Concrete::from(d); (self.add_node(c).into(), "msg.sig".to_string()) } else { @@ -338,7 +378,12 @@ pub trait Env: AnalyzerBackend + Sized { } } "value" => { - if let Some(d) = self.msg().underlying(self).into_expr_err(loc)?.value { + if let Some(existing) = ctx + .var_by_name_or_recurse(self, "msg.value") + .into_expr_err(loc)? + { + return Ok(ExprRet::Single(existing.into())); + } else if let Some(d) = self.msg().underlying(self).into_expr_err(loc)?.value { let c = Concrete::from(d); (self.add_node(c).into(), "msg.value".to_string()) } else { @@ -395,7 +440,13 @@ pub trait Env: AnalyzerBackend + Sized { } } "gaslimit" => { - if let Some(d) = self.msg().underlying(self).into_expr_err(loc)?.gaslimit { + if let Some(existing) = ctx + .var_by_name_or_recurse(self, "msg.gaslimit") + .into_expr_err(loc)? + { + return Ok(ExprRet::Single(existing.into())); + } else if let Some(d) = self.msg().underlying(self).into_expr_err(loc)?.gaslimit + { let c = Concrete::from(d); (self.add_node(c).into(), "".to_string()) } else { diff --git a/crates/solc-expressions/src/func_call/func_caller.rs b/crates/solc-expressions/src/func_call/func_caller.rs index b6396d09..581f3f3a 100644 --- a/crates/solc-expressions/src/func_call/func_caller.rs +++ b/crates/solc-expressions/src/func_call/func_caller.rs @@ -9,7 +9,7 @@ use crate::{ use graph::{ elem::Elem, nodes::{ - Concrete, Context, ContextNode, ContextVar, ContextVarNode, ExprRet, FunctionNode, + Concrete, Context, ContextNode, ContextVar, ContextVarNode, EnvCtx, ExprRet, FunctionNode, FunctionParamNode, ModifierState, SubContextKind, }, AnalyzerBackend, ContextEdge, Edge, GraphBackend, Node, @@ -36,6 +36,7 @@ pub trait FuncCaller: func: FunctionNode, func_call_str: Option<&str>, modifier_state: Option, + msg: Option, ) -> Result<(), ExprErr> { let params = func.params(self); let input_paths = input_paths.clone().flatten(); @@ -62,6 +63,7 @@ pub trait FuncCaller: ¶ms, func_call_str, &modifier_state, + msg.clone(), ) }) } @@ -97,6 +99,7 @@ pub trait FuncCaller: ¶ms, func_call_str, &modifier_state, + msg.clone(), ) }) } else { @@ -121,6 +124,7 @@ pub trait FuncCaller: ¶ms, func_call_str, &modifier_state, + msg.clone(), ) }), e => todo!("here: {:?}", e), @@ -140,6 +144,7 @@ pub trait FuncCaller: params: &[FunctionParamNode], func_call_str: Option<&str>, modifier_state: &Option, + env: Option, ) -> Result<(), ExprErr> { tracing::trace!( "Calling function: {} in context: {}", @@ -164,6 +169,8 @@ pub trait FuncCaller: self.create_call_ctx(ctx, loc, func_node, modifier_state.clone())? }; + self.add_env(callee_ctx, func_node, env, loc)?; + // handle remapping of variable names and bringing variables into the new context let renamed_inputs = self.map_inputs_to_params(arena, loc, entry_call, params, inputs, callee_ctx)?; diff --git a/crates/solc-expressions/src/func_call/helper.rs b/crates/solc-expressions/src/func_call/helper.rs index 66bf9670..8bc9794d 100644 --- a/crates/solc-expressions/src/func_call/helper.rs +++ b/crates/solc-expressions/src/func_call/helper.rs @@ -4,7 +4,7 @@ use crate::{member_access::ListAccess, variable::Variable}; use graph::{ elem::Elem, nodes::{ - CallFork, Concrete, Context, ContextNode, ContextVar, ContextVarNode, ExprRet, + CallFork, Concrete, Context, ContextNode, ContextVar, ContextVarNode, EnvCtx, ExprRet, FunctionNode, FunctionParamNode, ModifierState, SubContextKind, }, AnalyzerBackend, ContextEdge, Edge, Node, Range, VarType, @@ -18,6 +18,29 @@ use std::collections::BTreeMap; impl CallerHelper for T where T: AnalyzerBackend + Sized {} /// Helper trait for performing function calls pub trait CallerHelper: AnalyzerBackend + Sized { + fn add_env( + &mut self, + callee_ctx: ContextNode, + func: FunctionNode, + env: Option, + loc: Loc, + ) -> Result<(), ExprErr> { + if let Some(mut env) = env { + if func.is_public_or_ext(self).into_expr_err(loc)? { + if let Some(sig) = func.sig(self).into_expr_err(loc)? { + let sig_var = self.add_concrete_var(callee_ctx, sig, loc)?; + env.sig = Some(sig_var); + } + } + + let env_ctx = self.add_node(env); + self.add_edge(env_ctx, callee_ctx, Edge::Context(ContextEdge::Env)); + } + + // todo!() + Ok(()) + } + /// Maps inputs to function parameters such that if there is a renaming i.e. `a(uint256 x)` is called via `a(y)`, /// we map `y -> x` for future lookups fn map_inputs_to_params( diff --git a/crates/solc-expressions/src/func_call/internal_call.rs b/crates/solc-expressions/src/func_call/internal_call.rs index cc1eeb1c..158823f9 100644 --- a/crates/solc-expressions/src/func_call/internal_call.rs +++ b/crates/solc-expressions/src/func_call/internal_call.rs @@ -6,7 +6,9 @@ use crate::{ }; use graph::{ - nodes::{BuiltInNode, ContextNode, ContextVarNode, ContractNode, FunctionNode, StructNode}, + nodes::{ + BuiltInNode, ContextNode, ContextVarNode, ContractNode, ExprRet, FunctionNode, StructNode, + }, AnalyzerBackend, GraphBackend, Node, TypeNode, VarType, }; use shared::{ExprErr, GraphError, NodeIdx}; diff --git a/crates/solc-expressions/src/func_call/intrinsic_call/block.rs b/crates/solc-expressions/src/func_call/intrinsic_call/block.rs index d92ed744..50acacde 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/block.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/block.rs @@ -1,8 +1,12 @@ +use crate::variable::Variable; use graph::{ - nodes::{Builtin, ContextNode, ContextVar, ExprRet}, + elem::{Elem, RangeDyn}, + nodes::{Builtin, Concrete, ContextNode, ContextVar, ContextVarNode, ExprRet}, AnalyzerBackend, }; -use shared::{ExprErr, IntoExprErr}; +use shared::{ExprErr, IntoExprErr, RangeArena}; + +use ethers_core::types::H256; use solang_parser::pt::{Expression, Loc}; @@ -13,6 +17,7 @@ pub trait BlockCaller: AnalyzerBackend + S /// Perform a `block` function call fn block_call( &mut self, + arena: &mut RangeArena>, ctx: ContextNode, func_name: &str, inputs: ExprRet, @@ -32,6 +37,29 @@ pub trait BlockCaller: AnalyzerBackend + S .into_expr_err(loc)?; Ok(()) } + "blobhash" => { + let _input = inputs.expect_single().into_expr_err(loc)?; + let mut var = ContextVar::new_from_builtin( + loc, + self.builtin_or_add(Builtin::Bytes(32)).into(), + self, + ) + .into_expr_err(loc)?; + let mut range = var.ty.take_range().unwrap(); + // https://docs.soliditylang.org/en/latest/units-and-global-variables.html#block-and-transaction-properties + // we set the first byte to 0x01 per solidity docs + let mut min = [0; 32]; + min[0] = 1; + let mut max = [u8::MAX; 32]; + max[0] = 1; + range.min = Elem::from(Concrete::Bytes(32, H256(min))); + range.max = Elem::from(Concrete::Bytes(32, H256(max))); + var.ty.set_range(range).into_expr_err(loc)?; + let cvar = ContextVarNode::from(self.add_node(var)); + ctx.push_expr(ExprRet::Single(cvar.0.into()), self) + .into_expr_err(loc)?; + Ok(()) + } _ => Err(ExprErr::FunctionNotFound( loc, format!( diff --git a/crates/solc-expressions/src/func_call/intrinsic_call/intrinsic_caller.rs b/crates/solc-expressions/src/func_call/intrinsic_call/intrinsic_caller.rs index 0d8f7763..5f890626 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/intrinsic_caller.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/intrinsic_caller.rs @@ -83,6 +83,7 @@ pub trait IntrinsicFuncCaller: constructor, None, None, + None )?; self.apply_to_edges(ctx, loc, arena, &|analyzer, _arena, ctx, loc| { let var = match ContextVar::maybe_from_user_ty(analyzer, loc, ty_idx) { @@ -114,6 +115,7 @@ pub trait IntrinsicFuncCaller: constructor, None, None, + None )?; analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, _arena, ctx, loc| { let var = match ContextVar::maybe_from_user_ty(analyzer, loc, ty_idx) { @@ -177,7 +179,7 @@ pub trait IntrinsicFuncCaller: // array "push" | "pop" => self.array_call_inner(arena, ctx, name, inputs, loc), // block - "blockhash" => self.block_call(ctx, name, inputs, loc), + "blockhash" | "blobhash" => self.block_call(arena, ctx, name, inputs, loc), // dynamic sized builtins "concat" => self.dyn_builtin_call(arena, ctx, name, inputs, loc), // msg diff --git a/crates/solc-expressions/src/func_call/intrinsic_call/msg.rs b/crates/solc-expressions/src/func_call/intrinsic_call/msg.rs index 1d5b58af..d8a00812 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/msg.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/msg.rs @@ -14,12 +14,14 @@ pub trait MsgCaller: AnalyzerBackend + Siz fn msg_call(&mut self, ctx: ContextNode, func_name: &str, loc: Loc) -> Result<(), ExprErr> { match func_name { "gasleft" => { - let var = ContextVar::new_from_builtin( + let mut var = ContextVar::new_from_builtin( loc, self.builtin_or_add(Builtin::Uint(64)).into(), self, ) .into_expr_err(loc)?; + var.name = "gasleft()".to_string(); + var.display_name = "gasleft()".to_string(); let cvar = self.add_node(var); ctx.push_expr(ExprRet::Single(cvar), self) .into_expr_err(loc)?; diff --git a/crates/solc-expressions/src/func_call/modifier.rs b/crates/solc-expressions/src/func_call/modifier.rs index 8e69f3e0..bc9e3997 100644 --- a/crates/solc-expressions/src/func_call/modifier.rs +++ b/crates/solc-expressions/src/func_call/modifier.rs @@ -81,6 +81,7 @@ pub trait ModifierCaller: mod_node, None, Some(mod_state.clone()), + None, ) }) } diff --git a/crates/solc-expressions/src/member_access/member_trait.rs b/crates/solc-expressions/src/member_access/member_trait.rs index 46571c79..66134a07 100644 --- a/crates/solc-expressions/src/member_access/member_trait.rs +++ b/crates/solc-expressions/src/member_access/member_trait.rs @@ -1,10 +1,12 @@ -use crate::{BuiltinAccess, ContractAccess, EnumAccess, Env, ListAccess, StructAccess}; +use crate::{ + variable::Variable, BuiltinAccess, ContractAccess, EnumAccess, Env, ListAccess, StructAccess, +}; use graph::{ elem::Elem, nodes::{ BuiltInNode, Concrete, ConcreteNode, ContextNode, ContextVar, ContextVarNode, ContractNode, - EnumNode, ExprRet, FunctionNode, StructNode, TyNode, + EnumNode, EnvCtxNode, ExprRet, FunctionNode, KilledKind, StructNode, TyNode, }, AnalyzerBackend, Node, TypeNode, VarType, }; @@ -119,6 +121,20 @@ pub trait MemberAccess: Node::Builtin(ref _b) => { self.builtin_member_access(ctx, BuiltInNode::from(member_idx), name, false, loc) } + Node::EnvCtx(_) => { + if let Some(var) = EnvCtxNode::from(member_idx) + .member_access(self, name) + .into_expr_err(loc)? + { + let res = self.advance_var_in_ctx(var.into(), loc, ctx).unwrap(); + Ok((ExprRet::Single(res.0.into()), false)) + } else { + Err(ExprErr::ParseError( + loc, + format!("No enviroment variable: {name}"), + )) + } + } e => Err(ExprErr::Todo( loc, format!("Member access on type: {e:?} is not yet supported"), diff --git a/crates/solc-expressions/src/variable.rs b/crates/solc-expressions/src/variable.rs index d7ae27af..521d80ba 100644 --- a/crates/solc-expressions/src/variable.rs +++ b/crates/solc-expressions/src/variable.rs @@ -49,7 +49,7 @@ pub trait Variable: AnalyzerBackend + Size }, ) } else if ident.name == "_" { - self.env_variable(arena, ident, target_ctx)?; + self.env_variable(arena, ident, target_ctx, ident.loc)?; Ok(()) } else if let Some(cvar) = ctx .var_by_name_or_recurse(self, &ident.name) @@ -73,7 +73,7 @@ pub trait Variable: AnalyzerBackend + Size // } else { // self.variable(ident, parent_ctx, Some(target_ctx)) // } - } else if (self.env_variable(arena, ident, target_ctx)?).is_some() { + } else if (self.env_variable(arena, ident, target_ctx, ident.loc)?).is_some() { Ok(()) } else if let Some(idxs) = self.user_types().get(&ident.name).cloned() { tracing::trace!("Getting variable via user_types"); From 9bb7933d9e672c958cc5a5dd14b7e4b05dc3b3df Mon Sep 17 00:00:00 2001 From: brock elmore Date: Thu, 8 Aug 2024 16:14:29 -1000 Subject: [PATCH 2/3] persistent env --- crates/graph/src/nodes/context/typing.rs | 27 ---------- crates/graph/src/nodes/context/variables.rs | 10 ++-- crates/graph/src/nodes/context/versioning.rs | 18 +++++++ crates/graph/src/nodes/env_ctx.rs | 34 ++++++++++++ crates/graph/src/nodes/func_ty.rs | 1 - crates/shared/src/flattened.rs | 16 +++--- .../src/context_builder/flattened.rs | 52 +++++++++++++++---- .../src/func_call/func_caller.rs | 21 +++++--- .../solc-expressions/src/func_call/helper.rs | 3 +- .../func_call/intrinsic_call/constructors.rs | 8 ++- .../intrinsic_call/intrinsic_caller.rs | 7 +-- .../src/func_call/modifier.rs | 1 + .../src/member_access/member_trait.rs | 39 ++++++++++---- 13 files changed, 161 insertions(+), 76 deletions(-) diff --git a/crates/graph/src/nodes/context/typing.rs b/crates/graph/src/nodes/context/typing.rs index 0c64b7ee..56a37a5f 100644 --- a/crates/graph/src/nodes/context/typing.rs +++ b/crates/graph/src/nodes/context/typing.rs @@ -70,31 +70,4 @@ impl ContextNode { pub fn is_ext_fn(&self, analyzer: &impl GraphBackend) -> Result { Ok(self.underlying(analyzer)?.is_ext_fn_call()) } - - /// Checks whether a function is external to the current context - pub fn is_fn_ext( - &self, - fn_node: FunctionNode, - analyzer: &mut impl AnalyzerBackend, - ) -> Result { - match fn_node.maybe_associated_contract(analyzer) { - None => Ok(false), - Some(fn_ctrt) => { - if let Some(self_ctrt) = self - .associated_fn(analyzer)? - .maybe_associated_contract(analyzer) - { - Ok(Some(self_ctrt) != Some(fn_ctrt) - && !self_ctrt - .underlying(analyzer)? - .inherits - .iter() - .filter_map(|i| i.as_ref()) - .any(|inherited| *inherited == fn_ctrt)) - } else { - Ok(false) - } - } - } - } } diff --git a/crates/graph/src/nodes/context/variables.rs b/crates/graph/src/nodes/context/variables.rs index c6e24e4e..30ca2643 100644 --- a/crates/graph/src/nodes/context/variables.rs +++ b/crates/graph/src/nodes/context/variables.rs @@ -98,7 +98,7 @@ impl ContextNode { pub fn env_or_recurse( &self, - analyzer: &impl GraphBackend, + analyzer: &mut impl AnalyzerBackend, ) -> Result, GraphError> { if let Some(env) = analyzer .graph() @@ -109,14 +109,12 @@ impl ContextNode { return Ok(Some(env.into())); } - if let Some(parent) = self.ancestor_in_fn(analyzer, self.associated_fn(analyzer)?)? { + if let Some(parent) = self.ancestor_in_call(analyzer)? { if let Some(in_parent) = parent.env_or_recurse(analyzer)? { return Ok(Some(in_parent)); + } else { + Ok(None) } - } - - if let Some(parent) = self.underlying(analyzer)?.continuation_of() { - parent.env_or_recurse(analyzer) } else { Ok(None) } diff --git a/crates/graph/src/nodes/context/versioning.rs b/crates/graph/src/nodes/context/versioning.rs index c49a74ab..5ae66a80 100644 --- a/crates/graph/src/nodes/context/versioning.rs +++ b/crates/graph/src/nodes/context/versioning.rs @@ -70,6 +70,24 @@ impl ContextNode { } } + /// Get the first ancestor context that is in the same function + pub fn ancestor_in_call( + &self, + analyzer: &mut impl AnalyzerBackend, + ) -> Result, GraphError> { + let mut curr_ancestor = self.underlying(analyzer)?.parent_ctx(); + while let Some(parent) = curr_ancestor { + let is_ext = parent.is_ext_fn(analyzer)?; + if is_ext { + curr_ancestor = parent.underlying(analyzer)?.parent_ctx(); + } else { + return Ok(curr_ancestor); + } + } + + Ok(None) + } + /// Returns all forks associated with the context pub fn calls(&self, analyzer: &impl GraphBackend) -> Result, GraphError> { let descendents = self.descendents(analyzer)?; diff --git a/crates/graph/src/nodes/env_ctx.rs b/crates/graph/src/nodes/env_ctx.rs index a423799d..370059a5 100644 --- a/crates/graph/src/nodes/env_ctx.rs +++ b/crates/graph/src/nodes/env_ctx.rs @@ -28,13 +28,47 @@ impl EnvCtxNode { } } + pub fn underlying_mut<'a>( + &self, + analyzer: &'a mut impl GraphBackend, + ) -> Result<&'a mut EnvCtx, GraphError> { + match analyzer.node_mut(*self) { + Node::EnvCtx(st) => Ok(st), + Node::Unresolved(ident) => Err(GraphError::UnknownVariable(format!( + "Could not find environment context: {}", + ident.name + ))), + e => Err(GraphError::NodeConfusion(format!( + "Node type confusion: expected node to be EnvCtx but it was: {e:?}" + ))), + } + } + pub fn member_access( &self, analyzer: &impl GraphBackend, name: &str, ) -> Result, GraphError> { + tracing::trace!("env access: {name}"); Ok(self.underlying(analyzer)?.get(name)) } + + pub fn members(&self, analyzer: &impl GraphBackend) -> Result, GraphError> { + let underlying = self.underlying(analyzer)?; + Ok(vec![ + underlying.this, + underlying.data, + underlying.sender, + underlying.sig, + underlying.value, + underlying.origin, + underlying.gasprice, + underlying.gaslimit, + ] + .into_iter() + .flatten() + .collect()) + } } impl AsDotStr for EnvCtxNode { diff --git a/crates/graph/src/nodes/func_ty.rs b/crates/graph/src/nodes/func_ty.rs index 90c81097..9f99be7a 100644 --- a/crates/graph/src/nodes/func_ty.rs +++ b/crates/graph/src/nodes/func_ty.rs @@ -50,7 +50,6 @@ impl FunctionNode { Ok(None) } else { let name = self.name(analyzer)?; - println!("name: {name}"); let mut out = [0; 32]; keccak_hash::keccak_256(name.as_bytes(), &mut out); let mut sig = [0; 32]; diff --git a/crates/shared/src/flattened.rs b/crates/shared/src/flattened.rs index e63a6168..06883271 100644 --- a/crates/shared/src/flattened.rs +++ b/crates/shared/src/flattened.rs @@ -61,9 +61,9 @@ pub enum FlatExpr { ArraySlice(Loc, bool, bool), ArrayLiteral(Loc, usize), MemberAccess(Loc, &'static str), - FunctionCall(Loc, usize, usize), + FunctionCall(Loc, bool, usize, usize), FunctionCallBlock(Loc, usize), - NamedFunctionCall(Loc, usize, usize), + NamedFunctionCall(Loc, bool, usize, usize), Not(Loc), Negate(Loc), Delete(Loc), @@ -168,8 +168,8 @@ impl std::fmt::Display for FlatExpr { ArrayTy(..) => write!(f, "[]"), ArrayIndexAccess(..) => write!(f, "[(..)]"), MemberAccess(_, field) => write!(f, ".{field}"), - FunctionCall(_, n, _) => write!(f, "({})", "_,".repeat(*n)), - NamedFunctionCall(_, _, _) => write!(f, "(..)"), + FunctionCall(_, _, n, _) => write!(f, "({})", "_,".repeat(*n)), + NamedFunctionCall(_, _, _, _) => write!(f, "(..)"), Not(_) => write!(f, "~"), Negate(_) => write!(f, "-"), Delete(_) => write!(f, "delete "), @@ -465,12 +465,12 @@ impl TryFrom<&Expression> for FlatExpr { FunctionCall(loc, call, input_exprs) => { if let FunctionCallBlock(_, _, args) = &**call { if let solang_parser::pt::Statement::Args(_, args) = &**args { - FlatExpr::FunctionCall(*loc, input_exprs.len(), args.len()) + FlatExpr::FunctionCall(*loc, false, input_exprs.len(), args.len()) } else { unreachable!() } } else { - FlatExpr::FunctionCall(*loc, input_exprs.len(), 0) + FlatExpr::FunctionCall(*loc, false, input_exprs.len(), 0) } } FunctionCallBlock(loc, _, ref args) => { @@ -483,12 +483,12 @@ impl TryFrom<&Expression> for FlatExpr { NamedFunctionCall(loc, call, input_exprs) => { if let FunctionCallBlock(_, _, args) = &**call { if let solang_parser::pt::Statement::Args(_, args) = &**args { - FlatExpr::NamedFunctionCall(*loc, input_exprs.len(), args.len()) + FlatExpr::NamedFunctionCall(*loc, false, input_exprs.len(), args.len()) } else { unreachable!() } } else { - FlatExpr::NamedFunctionCall(*loc, input_exprs.len(), 0) + FlatExpr::NamedFunctionCall(*loc, false, input_exprs.len(), 0) } } Not(loc, ..) => FlatExpr::Not(*loc), diff --git a/crates/solc-expressions/src/context_builder/flattened.rs b/crates/solc-expressions/src/context_builder/flattened.rs index d89c200e..6ad02450 100644 --- a/crates/solc-expressions/src/context_builder/flattened.rs +++ b/crates/solc-expressions/src/context_builder/flattened.rs @@ -631,7 +631,12 @@ pub trait Flatten: self.traverse_expression(func_expr, unchecked); self.push_expr(FlatExpr::New(*new_loc)); - self.push_expr(FlatExpr::FunctionCall(*func_loc, input_exprs.len(), 0)); + self.push_expr(FlatExpr::FunctionCall( + *func_loc, + false, + input_exprs.len(), + 0, + )); } NamedFunctionCall(loc, func_expr, input_args) => { input_args.iter().rev().for_each(|arg| { @@ -643,7 +648,12 @@ pub trait Flatten: input_args.iter().for_each(|arg| { self.push_expr(FlatExpr::from(arg)); }); - self.push_expr(FlatExpr::NamedFunctionCall(*loc, input_args.len(), 0)); + self.push_expr(FlatExpr::NamedFunctionCall( + *loc, + false, + input_args.len(), + 0, + )); } _ => { // add error @@ -901,6 +911,7 @@ pub trait Flatten: }); self.push_expr(FlatExpr::NamedFunctionCall( *loc, + false, input_args.len(), call_block_n, )); @@ -961,7 +972,12 @@ pub trait Flatten: } } - self.push_expr(FlatExpr::FunctionCall(*loc, num_inputs, call_block_n)); + self.push_expr(FlatExpr::FunctionCall( + *loc, + false, + num_inputs, + call_block_n, + )); } }, // member @@ -1012,6 +1028,7 @@ pub trait Flatten: None, // alt function name &None, // mod state env, + false, // not ext ); let _ = self.add_if_err(res); } @@ -1471,14 +1488,24 @@ pub trait Flatten: if was_lib_func { ctx.push_expr(member, self).into_expr_err(loc)?; match stack.get_mut(ctx.parse_idx(self)) { - Some(FlatExpr::FunctionCall(_, ref mut n, _)) => { + Some(FlatExpr::FunctionCall(_, _, ref mut n, _)) => { *n += 1; } - Some(FlatExpr::NamedFunctionCall(_, ref mut n, _)) => { + Some(FlatExpr::NamedFunctionCall(_, _, ref mut n, _)) => { *n += 1; } Some(_) | None => {} } + } else { + match stack.get_mut(ctx.parse_idx(self)) { + Some(FlatExpr::FunctionCall(_, ref mut ext, _, _)) => { + *ext = true; + } + Some(FlatExpr::NamedFunctionCall(_, ref mut ext, _, _)) => { + *ext = true; + } + Some(_) | None => {} + } } let as_var = @@ -2223,7 +2250,7 @@ pub trait Flatten: func_call: FlatExpr, parse_idx: usize, ) -> Result<(), ExprErr> { - let FlatExpr::NamedFunctionCall(_, n, _) = func_call else { + let FlatExpr::NamedFunctionCall(_, _, n, _) = func_call else { unreachable!() }; @@ -2260,9 +2287,11 @@ pub trait Flatten: input_names: Option>, parse_idx: usize, ) -> Result<(), ExprErr> { - let (loc, n, call_block_inputs) = match func_call { - FlatExpr::FunctionCall(loc, n, call_block_inputs) => (loc, n, call_block_inputs), - FlatExpr::NamedFunctionCall(loc, n, call_block_inputs) => (loc, n, call_block_inputs), + let (loc, ext, n, call_block_inputs) = match func_call { + FlatExpr::FunctionCall(loc, ext, n, call_block_inputs) + | FlatExpr::NamedFunctionCall(loc, ext, n, call_block_inputs) => { + (loc, ext, n, call_block_inputs) + } _ => unreachable!(), }; @@ -2302,7 +2331,8 @@ pub trait Flatten: .into_expr_err(loc)?; Some(env_ctx) } else { - None + let msg = self.msg().underlying(self).unwrap().clone(); + Some(EnvCtx::from_msg(self, &msg, loc, ctx).into_expr_err(loc)?) }; let is_new_call = match ctx.peek_expr_flag(self) { @@ -2398,7 +2428,7 @@ pub trait Flatten: // its a builtin function call self.call_builtin(arena, ctx, &s.name(self).into_expr_err(loc)?, inputs, loc) } else { - self.func_call(arena, ctx, loc, &inputs, s, None, None, env) + self.func_call(arena, ctx, loc, &inputs, s, None, None, env, ext) } } VarType::BuiltIn(bn, _) => { diff --git a/crates/solc-expressions/src/func_call/func_caller.rs b/crates/solc-expressions/src/func_call/func_caller.rs index 581f3f3a..29e0f041 100644 --- a/crates/solc-expressions/src/func_call/func_caller.rs +++ b/crates/solc-expressions/src/func_call/func_caller.rs @@ -37,6 +37,7 @@ pub trait FuncCaller: func_call_str: Option<&str>, modifier_state: Option, msg: Option, + is_ext: bool, ) -> Result<(), ExprErr> { let params = func.params(self); let input_paths = input_paths.clone().flatten(); @@ -64,6 +65,7 @@ pub trait FuncCaller: func_call_str, &modifier_state, msg.clone(), + is_ext, ) }) } @@ -100,6 +102,7 @@ pub trait FuncCaller: func_call_str, &modifier_state, msg.clone(), + is_ext, ) }) } else { @@ -125,6 +128,7 @@ pub trait FuncCaller: func_call_str, &modifier_state, msg.clone(), + is_ext, ) }), e => todo!("here: {:?}", e), @@ -145,17 +149,18 @@ pub trait FuncCaller: func_call_str: Option<&str>, modifier_state: &Option, env: Option, + is_ext: bool, ) -> Result<(), ExprErr> { tracing::trace!( "Calling function: {} in context: {}", func_node.name(self).unwrap(), ctx.path(self) ); - if !entry_call { - if let Ok(true) = self.apply(arena, ctx, loc, func_node, params, inputs, &mut vec![]) { - return Ok(()); - } - } + // if !entry_call { + // if let Ok(true) = self.apply(arena, ctx, loc, func_node, params, inputs, &mut vec![]) { + // return Ok(()); + // } + // } // pseudocode: // 1. Create context for the call @@ -166,10 +171,12 @@ pub trait FuncCaller: let callee_ctx = if entry_call { ctx } else { - self.create_call_ctx(ctx, loc, func_node, modifier_state.clone())? + self.create_call_ctx(ctx, loc, func_node, modifier_state.clone(), is_ext)? }; - self.add_env(callee_ctx, func_node, env, loc)?; + if entry_call || is_ext { + self.add_env(callee_ctx, func_node, env, loc)?; + } // handle remapping of variable names and bringing variables into the new context let renamed_inputs = diff --git a/crates/solc-expressions/src/func_call/helper.rs b/crates/solc-expressions/src/func_call/helper.rs index 8bc9794d..4017e3eb 100644 --- a/crates/solc-expressions/src/func_call/helper.rs +++ b/crates/solc-expressions/src/func_call/helper.rs @@ -37,7 +37,6 @@ pub trait CallerHelper: AnalyzerBackend + self.add_edge(env_ctx, callee_ctx, Edge::Context(ContextEdge::Env)); } - // todo!() Ok(()) } @@ -187,8 +186,8 @@ pub trait CallerHelper: AnalyzerBackend + loc: Loc, func_node: FunctionNode, modifier_state: Option, + fn_ext: bool, ) -> Result { - let fn_ext = curr_ctx.is_fn_ext(func_node, self).into_expr_err(loc)?; if fn_ext { curr_ctx .add_gas_cost(self, shared::gas::EXT_FUNC_CALL_GAS) diff --git a/crates/solc-expressions/src/func_call/intrinsic_call/constructors.rs b/crates/solc-expressions/src/func_call/intrinsic_call/constructors.rs index 7726eb28..a61f8f75 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/constructors.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/constructors.rs @@ -1,6 +1,7 @@ use crate::{assign::Assign, func_call::helper::CallerHelper}; use graph::nodes::Builtin; +use graph::SolcRange; use graph::{ elem::*, nodes::{Concrete, ContextNode, ContextVar, ContextVarNode, ContractNode, ExprRet, StructNode}, @@ -84,7 +85,7 @@ pub trait ConstructorCaller: _arena: &mut RangeArena>, ctx: ContextNode, con_node: ContractNode, - _input: ExprRet, + input: ExprRet, loc: Loc, ) -> Result<(), ExprErr> { // construct a new contract @@ -100,7 +101,12 @@ pub trait ConstructorCaller: )) } }; + // TODO: add to address map let contract_cvar = ContextVarNode::from(self.add_node(Node::ContextVar(var))); + let e = Elem::from(input.expect_single().into_expr_err(loc)?); + contract_cvar + .set_range(self, From::from(e)) + .into_expr_err(loc)?; ctx.push_expr(ExprRet::Single(contract_cvar.into()), self) .into_expr_err(loc) } diff --git a/crates/solc-expressions/src/func_call/intrinsic_call/intrinsic_caller.rs b/crates/solc-expressions/src/func_call/intrinsic_call/intrinsic_caller.rs index 5f890626..d17b9950 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/intrinsic_caller.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/intrinsic_caller.rs @@ -83,7 +83,8 @@ pub trait IntrinsicFuncCaller: constructor, None, None, - None + None, + true )?; self.apply_to_edges(ctx, loc, arena, &|analyzer, _arena, ctx, loc| { let var = match ContextVar::maybe_from_user_ty(analyzer, loc, ty_idx) { @@ -104,7 +105,6 @@ pub trait IntrinsicFuncCaller: .into_expr_err(loc) }) } else { - self.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { // call the constructor analyzer.func_call( @@ -115,7 +115,8 @@ pub trait IntrinsicFuncCaller: constructor, None, None, - None + None, + true )?; analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, _arena, ctx, loc| { let var = match ContextVar::maybe_from_user_ty(analyzer, loc, ty_idx) { diff --git a/crates/solc-expressions/src/func_call/modifier.rs b/crates/solc-expressions/src/func_call/modifier.rs index bc9e3997..2e9ea797 100644 --- a/crates/solc-expressions/src/func_call/modifier.rs +++ b/crates/solc-expressions/src/func_call/modifier.rs @@ -82,6 +82,7 @@ pub trait ModifierCaller: None, Some(mod_state.clone()), None, + false, ) }) } diff --git a/crates/solc-expressions/src/member_access/member_trait.rs b/crates/solc-expressions/src/member_access/member_trait.rs index 66134a07..c49d8821 100644 --- a/crates/solc-expressions/src/member_access/member_trait.rs +++ b/crates/solc-expressions/src/member_access/member_trait.rs @@ -1,12 +1,12 @@ -use crate::{ - variable::Variable, BuiltinAccess, ContractAccess, EnumAccess, Env, ListAccess, StructAccess, -}; +use crate::{BuiltinAccess, ContractAccess, EnumAccess, Env, ListAccess, StructAccess}; +use graph::ContextEdge; +use graph::Edge; use graph::{ elem::Elem, nodes::{ BuiltInNode, Concrete, ConcreteNode, ContextNode, ContextVar, ContextVarNode, ContractNode, - EnumNode, EnvCtxNode, ExprRet, FunctionNode, KilledKind, StructNode, TyNode, + EnumNode, EnvCtxNode, ExprRet, FunctionNode, StructNode, TyNode, }, AnalyzerBackend, Node, TypeNode, VarType, }; @@ -126,13 +126,32 @@ pub trait MemberAccess: .member_access(self, name) .into_expr_err(loc)? { - let res = self.advance_var_in_ctx(var.into(), loc, ctx).unwrap(); - Ok((ExprRet::Single(res.0.into()), false)) + let full_name = var.name(self).unwrap(); + if let Some(prev_defined) = ctx + .var_by_name_or_recurse(self, &full_name) + .into_expr_err(loc)? + { + Ok(( + ExprRet::Single(prev_defined.latest_version(self).0.into()), + false, + )) + } else { + let cloned = var.latest_version(self).underlying(self).unwrap().clone(); + let new_var = self.add_node(cloned); + ctx.add_var(new_var.into(), self).into_expr_err(loc)?; + self.add_edge(new_var, ctx, Edge::Context(ContextEdge::Variable)); + let e_mut = EnvCtxNode::from(member_idx).underlying_mut(self).unwrap(); + e_mut.set(name, new_var); + Ok((ExprRet::Single(new_var), false)) + } } else { - Err(ExprErr::ParseError( - loc, - format!("No enviroment variable: {name}"), - )) + let msg_access = self.msg_access(ctx, name, loc)?; + let access = msg_access.expect_single().into_expr_err(loc)?; + ctx.add_var(access.into(), self).into_expr_err(loc)?; + self.add_edge(access, ctx, Edge::Context(ContextEdge::Variable)); + let e_mut = EnvCtxNode::from(member_idx).underlying_mut(self).unwrap(); + e_mut.set(name, access); + Ok((msg_access, false)) } } e => Err(ExprErr::Todo( From 891f00b54017d4c1aa55cfae31eb72a2d8ba7064 Mon Sep 17 00:00:00 2001 From: brock elmore Date: Thu, 8 Aug 2024 16:16:51 -1000 Subject: [PATCH 3/3] reenable test --- crates/pyrometer/tests/test_data/env.sol | 86 +++++++++---------- .../tests/test_data/remapping_import.sol | 12 +-- 2 files changed, 49 insertions(+), 49 deletions(-) diff --git a/crates/pyrometer/tests/test_data/env.sol b/crates/pyrometer/tests/test_data/env.sol index f08168fa..c6ec5efd 100644 --- a/crates/pyrometer/tests/test_data/env.sol +++ b/crates/pyrometer/tests/test_data/env.sol @@ -6,48 +6,48 @@ contract Env { return msg.sender; } - // function msg_data() public pure returns (bytes memory) { - // return msg.data; - // } + function msg_data() public pure returns (bytes memory) { + return msg.data; + } - // function testBlock() public payable { - // /* - // blockhash(uint blockNumber) returns (bytes32): hash of the given block - only works for 256 most recent blocks - // blobhash(uint index) returns (bytes32): versioned hash of the index-th blob associated with the current transaction. A versioned hash consists of a single byte representing the version (currently 0x01), followed by the last 31 bytes of the SHA256 hash of the KZG commitment (EIP-4844). - // block.basefee (uint): current block’s base fee (EIP-3198 and EIP-1559) - // block.blobbasefee (uint): current block’s blob base fee (EIP-7516 and EIP-4844) - // block.chainid (uint): current chain id - // block.coinbase (address payable): current block miner’s address - // block.difficulty (uint): current block difficulty (EVM < Paris). For other EVM versions it behaves as a deprecated alias for block.prevrandao that will be removed in the next breaking release - // block.gaslimit (uint): current block gaslimit - // block.number (uint): current block number - // block.prevrandao (uint): random number provided by the beacon chain (EVM >= Paris) (see EIP-4399 ) - // block.timestamp (uint): current block timestamp in seconds since Unix epoch - // gasleft() returns (uint256): remaining gas - // msg.data (bytes): complete calldata - // msg.sender (address): sender of the message (current call) - // msg.sig (bytes4): first four bytes of the calldata (i.e. function identifier) - // msg.value (uint): number of wei sent with the message - // tx.gasprice (uint): gas price of the transaction - // tx.origin (address): sender of the transaction (full call chain) - // */ - // bytes32 a = blockhash(1); - // uint c = block.basefee; - // uint e = block.chainid; - // address payable f = block.coinbase; - // uint g = block.difficulty; - // uint h = block.gaslimit; - // uint i = block.number; - // uint j = block.prevrandao; - // uint k = block.timestamp; - // uint l = gasleft(); - // bytes memory m = msg.data; - // address n = msg.sender; - // bytes4 o = msg.sig; - // uint p = msg.value; - // uint q = tx.gasprice; - // address r = tx.origin; - // bytes32 b = blobhash(1); - // uint d = block.blobbasefee; - // } + function testBlock() public payable { + /* + blockhash(uint blockNumber) returns (bytes32): hash of the given block - only works for 256 most recent blocks + blobhash(uint index) returns (bytes32): versioned hash of the index-th blob associated with the current transaction. A versioned hash consists of a single byte representing the version (currently 0x01), followed by the last 31 bytes of the SHA256 hash of the KZG commitment (EIP-4844). + block.basefee (uint): current block’s base fee (EIP-3198 and EIP-1559) + block.blobbasefee (uint): current block’s blob base fee (EIP-7516 and EIP-4844) + block.chainid (uint): current chain id + block.coinbase (address payable): current block miner’s address + block.difficulty (uint): current block difficulty (EVM < Paris). For other EVM versions it behaves as a deprecated alias for block.prevrandao that will be removed in the next breaking release + block.gaslimit (uint): current block gaslimit + block.number (uint): current block number + block.prevrandao (uint): random number provided by the beacon chain (EVM >= Paris) (see EIP-4399 ) + block.timestamp (uint): current block timestamp in seconds since Unix epoch + gasleft() returns (uint256): remaining gas + msg.data (bytes): complete calldata + msg.sender (address): sender of the message (current call) + msg.sig (bytes4): first four bytes of the calldata (i.e. function identifier) + msg.value (uint): number of wei sent with the message + tx.gasprice (uint): gas price of the transaction + tx.origin (address): sender of the transaction (full call chain) + */ + bytes32 a = blockhash(1); + uint c = block.basefee; + uint e = block.chainid; + address payable f = block.coinbase; + uint g = block.difficulty; + uint h = block.gaslimit; + uint i = block.number; + uint j = block.prevrandao; + uint k = block.timestamp; + uint l = gasleft(); + bytes memory m = msg.data; + address n = msg.sender; + bytes4 o = msg.sig; + uint p = msg.value; + uint q = tx.gasprice; + address r = tx.origin; + bytes32 b = blobhash(1); + uint d = block.blobbasefee; + } } diff --git a/crates/pyrometer/tests/test_data/remapping_import.sol b/crates/pyrometer/tests/test_data/remapping_import.sol index bc001820..0115658b 100644 --- a/crates/pyrometer/tests/test_data/remapping_import.sol +++ b/crates/pyrometer/tests/test_data/remapping_import.sol @@ -3,9 +3,9 @@ pragma solidity ^0.8.0; import "@relative/relative_import.sol"; -// contract RemappingImport { -// function deploy() public { -// Env env = Env(address(100)); -// env.msg_sender(); -// } -// } +contract RemappingImport { + function deploy() public { + Env env = Env(address(100)); + env.msg_sender(); + } +}