Skip to content

Commit

Permalink
Various EVM fixes (#148)
Browse files Browse the repository at this point in the history
* Fix MemEngine::write_buffer()

* Fix bytecode copying in EVM loader

* Support static flag in EVM

* Add support for EVM STATICCALL instruction

* Expose TX.STATICCALL enum value to python bindings

* Add DELEGATECALL callother handler

* EVM: support sharing storage with another runtime

* Add support for GASPRICE EVM instruction
  • Loading branch information
Boyan-MILANOV authored Sep 21, 2022
1 parent 6a7b673 commit 4140818
Show file tree
Hide file tree
Showing 13 changed files with 197 additions and 10 deletions.
2 changes: 1 addition & 1 deletion bindings/python/py_engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ static PyObject* MaatEngine_load(PyObject* self, PyObject* args, PyObject* keywo
char * arg_bytes = nullptr;
Py_ssize_t arg_bytes_len = 0;
PyBytes_AsStringAndSize(arg, &arg_bytes, &arg_bytes_len);
cmdline_args.push_back(loader::CmdlineArg(std::string(arg_bytes)));
cmdline_args.push_back(loader::CmdlineArg(std::string(arg_bytes, arg_bytes_len)));
}
else if (PyList_Check(arg))
{
Expand Down
84 changes: 82 additions & 2 deletions bindings/python/py_env_EVM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -622,6 +622,11 @@ static PyObject* EVMTransaction_get_value(PyObject* self, void* closure)
return PyValue_FromValue(as_tx_object(self).transaction->value);
}

static PyObject* EVMTransaction_get_gas_price(PyObject* self, void* closure)
{
return PyValue_FromValue(as_tx_object(self).transaction->gas_price);
}

static PyObject* EVMTransaction_get_data(PyObject* self, void* closure)
{
// TODO(boyan): factorize this code with other places where we translate
Expand Down Expand Up @@ -682,6 +687,7 @@ static PyGetSetDef EVMTransaction_getset[] = {
{"ret_offset", EVMTransaction_get_ret_offset, NULL, "Return offset", NULL},
{"ret_len", EVMTransaction_get_ret_len, NULL, "Return length", NULL},
{"value", EVMTransaction_get_value, NULL, "Value in WEI", NULL},
{"gas_price", EVMTransaction_get_gas_price, NULL, "Gas price", NULL},
{NULL}
};

Expand Down Expand Up @@ -917,15 +923,25 @@ PyObject* maat_contract(PyObject* mod, PyObject* args)
PyObject* maat_new_evm_runtime(PyObject* mod, PyObject* args)
{
PyObject *new_engine, *old_engine;
if( !PyArg_ParseTuple(args, "O!O!", get_MaatEngine_Type(), &new_engine, get_MaatEngine_Type(), &old_engine))
PyObject* py_share_storage_uid = nullptr;
std::optional<int> share_storage_uid;
if( !PyArg_ParseTuple(args, "O!O!|O", get_MaatEngine_Type(), &new_engine, get_MaatEngine_Type(), &old_engine, &share_storage_uid))
{
return NULL;
}
try
{
if (py_share_storage_uid == nullptr or py_share_storage_uid == Py_None)
share_storage_uid = std::nullopt;
else if (PyLong_Check(py_share_storage_uid))
share_storage_uid = PyLong_AsLongLong(py_share_storage_uid);
else
return PyErr_Format(PyExc_TypeError, "share_storage_uid should be None or int");

env::EVM::new_evm_runtime(
*as_engine_object(new_engine).engine,
*as_engine_object(old_engine).engine
*as_engine_object(old_engine).engine,
share_storage_uid
);
}
catch(maat::env_exception& e)
Expand Down Expand Up @@ -1035,6 +1051,67 @@ PyObject* maat_allow_symbolic_keccak(PyObject* mod, PyObject* args)
}
}

PyObject* maat_evm_get_static_flag(PyObject* mod, PyObject* args)
{
PyObject* engine;
if( !PyArg_ParseTuple(args, "O!", get_MaatEngine_Type(), &engine))
return NULL;

try
{
auto eth = env::EVM::get_ethereum(*as_engine_object(engine).engine);
if (eth == nullptr)
return PyErr_Format(PyExc_RuntimeError, "No environment for this engine");
return PyBool_FromLong(eth->static_flag);
}
catch(const std::exception& e)
{
return PyErr_Format(PyExc_RuntimeError, e.what());
}
}

PyObject* maat_evm_set_static_flag(PyObject* mod, PyObject* args)
{
PyObject* engine;
int flag = 0;
if( !PyArg_ParseTuple(args, "O!p", get_MaatEngine_Type(), &engine, &flag))
return NULL;

try
{
auto eth = env::EVM::get_ethereum(*as_engine_object(engine).engine);
if (eth == nullptr)
return PyErr_Format(PyExc_RuntimeError, "No environment for this engine");
eth->static_flag = flag;
Py_RETURN_NONE;
}
catch(const std::exception& e)
{
return PyErr_Format(PyExc_RuntimeError, e.what());
}
}

PyObject* maat_evm_set_gas_price(PyObject* mod, PyObject* args)
{
PyObject* engine;
PyObject* price;
if( !PyArg_ParseTuple(args, "O!O!", get_MaatEngine_Type(), &engine, get_Value_Type(), &price))
return NULL;

try
{
auto eth = env::EVM::get_ethereum(*as_engine_object(engine).engine);
if (eth == nullptr)
return PyErr_Format(PyExc_RuntimeError, "No environment for this engine");
eth->gas_price = *as_value_object(price).value;
Py_RETURN_NONE;
}
catch(const std::exception& e)
{
return PyErr_Format(PyExc_RuntimeError, e.what());
}
}

void init_evm(PyObject* module)
{
/* TX_END enum */
Expand Down Expand Up @@ -1072,6 +1149,9 @@ void init_evm(PyObject* module)
PyDict_SetItemString(tx_enum, "DELEGATECALL", PyLong_FromLong(
(int)env::EVM::Transaction::Type::DELEGATECALL)
);
PyDict_SetItemString(tx_enum, "STATICCALL", PyLong_FromLong(
(int)env::EVM::Transaction::Type::STATICCALL)
);
PyDict_SetItemString(tx_enum, "EOA", PyLong_FromLong(
(int)env::EVM::Transaction::Type::EOA)
);
Expand Down
3 changes: 3 additions & 0 deletions bindings/python/py_maat.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ PyMethodDef module_methods[] = {
{"increment_block_timestamp", (PyCFunction)maat_increment_block_timestamp, METH_VARARGS, "Increment the current block timestamp by an abstract value"},
{"set_evm_bytecode", (PyCFunction)maat_set_evm_bytecode, METH_VARARGS, "Set runtime bytecode for the contract associated to an engine"},
{"allow_symbolic_keccak", (PyCFunction)maat_allow_symbolic_keccak, METH_VARARGS, "Enable/disable symbolic KECCAK hashes"},
{"evm_get_static_flag", (PyCFunction)maat_evm_get_static_flag, METH_VARARGS, "Get EVM static flag"},
{"evm_set_static_flag", (PyCFunction)maat_evm_set_static_flag, METH_VARARGS, "Set EVM static flag"},
{"evm_set_gas_price", (PyCFunction)maat_evm_set_gas_price, METH_VARARGS, "Set EVM gas price"},
{NULL}
};

Expand Down
3 changes: 3 additions & 0 deletions bindings/python/python_bindings.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,9 @@ PyObject* maat_increment_block_timestamp(PyObject* mod, PyObject* args);
PyObject* maat_increment_block_number(PyObject* mod, PyObject* args);
PyObject* maat_set_evm_bytecode(PyObject* mod, PyObject* args);
PyObject* maat_allow_symbolic_keccak(PyObject* mod, PyObject* args);
PyObject* maat_evm_get_static_flag(PyObject* mod, PyObject* args);
PyObject* maat_evm_set_static_flag(PyObject* mod, PyObject* args);
PyObject* maat_evm_set_gas_price(PyObject* mod, PyObject* args);

// ====== Utils =======
// Transform a list of values into a list of python values,
Expand Down
64 changes: 64 additions & 0 deletions src/engine/callother.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,15 @@ void _check_transaction_exists(env::EVM::contract_t contract)
throw callother_exception("Trying to access transaction but no transaction is set");
}

// Helper that ensures that EVM static flag is not set
void _check_static_flag(const std::string& inst, MaatEngine& engine)
{
if (env::EVM::get_ethereum(engine)->static_flag)
throw callother_exception(
Fmt() << "Can not execute " << inst << " with static flag set in EVM" >> Fmt::to_str
);
}

void EVM_STOP_handler(MaatEngine& engine, const ir::Inst& inst, ir::ProcessedInst& pinst)
{
env::EVM::contract_t contract = env::EVM::get_contract_for_engine(engine);
Expand Down Expand Up @@ -520,6 +529,7 @@ void EVM_SLOAD_handler(MaatEngine& engine, const ir::Inst& inst, ir::ProcessedIn

void EVM_SSTORE_handler(MaatEngine& engine, const ir::Inst& inst, ir::ProcessedInst& pinst)
{
_check_static_flag("SSTORE", engine);
env::EVM::contract_t contract = env::EVM::get_contract_for_engine(engine);
contract->storage->write(
pinst.in1.value(),
Expand Down Expand Up @@ -595,6 +605,11 @@ void EVM_ENV_INFO_handler(MaatEngine& engine, const ir::Inst& inst, ir::Processe
}
break;
}
case 0x3a: // GASPRICE
{
pinst.res = env::EVM::get_ethereum(engine)->gas_price;
break;
}
case 0x3b: // EXTCODESIZE
{
Value addr = contract->stack.get(0);
Expand Down Expand Up @@ -852,6 +867,7 @@ void EVM_CREATE_handler(MaatEngine& engine, const ir::Inst& inst, ir::ProcessedI
bool is_create2 = (bool)pinst.in1.value().as_uint();
env::EVM::contract_t contract = env::EVM::get_contract_for_engine(engine);
_check_transaction_exists(contract);
_check_static_flag("CREATE", engine);

// Get parameters on stack
Value value = contract->stack.get(0);
Expand Down Expand Up @@ -939,6 +955,52 @@ void EVM_DELEGATECALL_handler(
engine._stop_after_inst(info::Stop::NONE);
}

void EVM_STATICCALL_handler(
MaatEngine& engine,
const ir::Inst& inst,
ir::ProcessedInst& pinst
){
env::EVM::contract_t contract = env::EVM::get_contract_for_engine(engine);
_check_transaction_exists(contract);

// Get parameters on stack
Value gas = contract->stack.get(0);
Value addr = extract(contract->stack.get(1), 159, 0);
Value argsOff = contract->stack.get(2);
Value argsLen = contract->stack.get(3);
Value retOff = contract->stack.get(4);
Value retLen = contract->stack.get(5);
contract->stack.pop(6);

// We don't support symbolic offsets to args data
if (not argsOff.is_concrete(*engine.vars))
throw callother_exception("STATICCALL: argsOff parameter is symbolic. Not yet supported");
if (not argsLen.is_concrete(*engine.vars))
throw callother_exception("STATICCALL: argsLen parameter is symbolic. Not yet supported");

// Read transaction data
std::vector<Value> tx_data = contract->memory.mem()._read_optimised_buffer(
argsOff.as_uint(*engine.vars),
argsLen.as_uint(*engine.vars)
);

contract->outgoing_transaction = env::EVM::Transaction(
contract->transaction->origin,
contract->address,
addr.as_number(),
Value(256, 0), // null value
tx_data,
contract->transaction->gas_price,
gas,
env::EVM::Transaction::Type::STATICCALL,
retOff,
retLen
);
// Tell the engine to stop because execution must be transfered to
// another contract
engine._stop_after_inst(info::Stop::NONE);
}

void EVM_SELFDESTRUCT_handler(MaatEngine& engine, const ir::Inst& inst, ir::ProcessedInst& pinst)
{
throw callother_exception("SELFDESTRUCT: not implemented");
Expand All @@ -947,6 +1009,7 @@ void EVM_SELFDESTRUCT_handler(MaatEngine& engine, const ir::Inst& inst, ir::Proc
void EVM_LOG_handler(MaatEngine& engine, const ir::Inst& inst, ir::ProcessedInst& pinst)
{
env::EVM::contract_t contract = env::EVM::get_contract_for_engine(engine);
_check_static_flag("LOG", engine);

int lvl = pinst.in1.value().as_uint();
Value data_start = contract->stack.get(0);
Expand Down Expand Up @@ -1014,6 +1077,7 @@ HandlerMap default_handler_map()
h.set_handler(Id::EVM_EXP, EVM_EXP_handler);
h.set_handler(Id::EVM_CALL, EVM_CALL_handler);
h.set_handler(Id::EVM_CALLCODE, EVM_CALLCODE_handler);
h.set_handler(Id::EVM_STATICCALL, EVM_STATICCALL_handler);
h.set_handler(Id::EVM_DELEGATECALL, EVM_DELEGATECALL_handler);
h.set_handler(Id::EVM_CREATE, EVM_CREATE_handler);
h.set_handler(Id::EVM_SELFDESTRUCT, EVM_SELFDESTRUCT_handler);
Expand Down
18 changes: 15 additions & 3 deletions src/env/env_EVM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -711,6 +711,8 @@ EthereumEmulator& EthereumEmulator::operator=(const EthereumEmulator& other)
keccak_helper = other.keccak_helper;
current_block_number = other.current_block_number;
current_block_timestamp = other.current_block_timestamp;
static_flag = other.static_flag;
gas_price = other.gas_price;
// We don't copy snapshots !!!
return *this;
}
Expand All @@ -723,6 +725,8 @@ EthereumEmulator& EthereumEmulator::operator=(EthereumEmulator&& other)
keccak_helper = other.keccak_helper;
current_block_number = other.current_block_number;
current_block_timestamp = other.current_block_timestamp;
static_flag = other.static_flag;
gas_price = other.gas_price;
// We don't copy snapshots !!!
return *this;
}
Expand All @@ -737,6 +741,8 @@ void EthereumEmulator::_init()
current_block_timestamp = AbstractCounter(
Value(256, 1524785992) // Thu Apr 26 23:39:52 UTC 2018
);
static_flag = false;
gas_price = Value(256, 20); // Arbitrary gas price of 20, can be changed later
}

int EthereumEmulator::add_contract(contract_t contract)
Expand Down Expand Up @@ -851,16 +857,22 @@ contract_t get_contract_for_engine(MaatEngine& engine)
return get_ethereum(engine)->get_contract_by_uid(engine.process->pid);
}

void new_evm_runtime(MaatEngine& new_engine, const MaatEngine& old_engine)
{
void new_evm_runtime(
MaatEngine& new_engine,
const MaatEngine& old_engine,
std::optional<int> share_storage_uid
){
if (
old_engine.arch->type != Arch::Type::EVM
or new_engine.arch->type != Arch::Type::EVM
)
throw env_exception("new_evm_runtime(): can't be called with an architecture other than EVM");

int new_contract_uid = get_ethereum(old_engine)->new_runtime_for_contract(old_engine.process->pid);
auto eth = get_ethereum(old_engine);
int new_contract_uid = eth->new_runtime_for_contract(old_engine.process->pid);
new_engine.process->pid = new_contract_uid;
if (share_storage_uid.has_value())
eth->get_contract_by_uid(new_contract_uid)->storage = eth->get_contract_by_uid(*share_storage_uid)->storage;
}

std::vector<uint8_t> hex_string_to_bytes(const std::vector<char>& in)
Expand Down
1 change: 1 addition & 0 deletions src/include/maat/callother.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ enum class Id
EVM_CALL,
EVM_CALLCODE,
EVM_DELEGATECALL,
EVM_STATICCALL,
EVM_CREATE,
EVM_SELFDESTRUCT,
EVM_LOG,
Expand Down
13 changes: 11 additions & 2 deletions src/include/maat/env/env_EVM.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ class Transaction: public serial::Serializable
CALL, ///< Internal message with CALL
CALLCODE, ///< Internal message with CALLCODE
DELEGATECALL, ///< Internal message with DELEGATECALL
STATICCALL, ///< Internal message with STATICCALL
CREATE, ///< Create contract from another contract
CREATE2, ///< Create contract from another contract, with a deterministic address
NONE
Expand Down Expand Up @@ -321,6 +322,8 @@ class EthereumEmulator: public EnvEmulator
KeccakHelper keccak_helper;
AbstractCounter current_block_number;
AbstractCounter current_block_timestamp;
bool static_flag;
Value gas_price;
public:
EthereumEmulator();
EthereumEmulator(const EthereumEmulator&);
Expand Down Expand Up @@ -365,8 +368,14 @@ std::vector<uint8_t> hex_string_to_bytes(const std::vector<char>& in);

/** Helper function that creates a new EVM contract runtime from an existing MaatEngine
and binds it to a new MaatEngine. Calling this function requires that `new_engine` shares
its memory (which contains the contract code) with `old_engine` */
void new_evm_runtime(MaatEngine& new_engine, const MaatEngine& old_engine);
its memory (which contains the contract code) with `old_engine`. If 'share_storage_uid' is
specified, the new runtime will not share storage with `old_engine`, but with the runtime that
has this uid instead (used solely for DELEGATECALL) */
void new_evm_runtime(
MaatEngine& new_engine,
const MaatEngine& old_engine,
std::optional<int> share_storage_uid
);

/** \} */ // doxygen group env

Expand Down
2 changes: 1 addition & 1 deletion src/ir/asm_inst.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ void IRMap::remove_insts_containing(uint64_t start, uint64_t end)
// it's O(1) *on average* with unordered_map. Since we can lookup
// millions of asm insts in a run and since self-modifying code
// is very rare we prefer using unordered_map.

for (uint64_t addr = start; addr <= end; addr++)
{
asm_insts.erase(addr);
Expand Down
2 changes: 1 addition & 1 deletion src/loader/loader_EVM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ void LoaderEVM::load(
if (arg.is_concrete())
env::EVM::_append_EVM_code(
*engine,
(uint8_t*)arg.string().c_str(),
(uint8_t*)arg.string().data(),
arg.len()
);
else
Expand Down
3 changes: 3 additions & 0 deletions src/memory/memory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2385,6 +2385,9 @@ void MemEngine::write_buffer(addr_t addr, const std::vector<Value>& buf, bool ig
std::vector<Value> next_buf;
std::vector<Value> tmp_buf2;

if (buf.empty())
return;

for (const Value& val : buf)
nb_bytes += val.size()/8;

Expand Down
1 change: 1 addition & 0 deletions src/third-party/sleigh/native/sleigh_interface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -552,6 +552,7 @@ class TranslationContext
{"evm_call", callother::Id::EVM_CALL},
{"evm_callcode", callother::Id::EVM_CALLCODE},
{"evm_delegatecall", callother::Id::EVM_DELEGATECALL},
{"evm_staticcall", callother::Id::EVM_STATICCALL},
{"evm_create", callother::Id::EVM_CREATE},
{"evm_selfdestruct", callother::Id::EVM_SELFDESTRUCT},
{"evm_log", callother::Id::EVM_LOG}
Expand Down
Loading

0 comments on commit 4140818

Please sign in to comment.