Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
99 commits
Select commit Hold shift + click to select a range
1f17d01
Add a sanity check for CALLPRIVATE argument count match
robmcl4 Jan 23, 2024
8dab556
Merge pull request #14 from ucsb-seclab/sanity_check_callprivate
ruaronicola Jan 24, 2024
b61c81b
make w3 endpoint optional in run.py
ruaronicola Jan 31, 2024
2ee9d23
Merge branch 'main' of github.com:ucsb-seclab/greed into main
ruaronicola Jan 31, 2024
d549249
explicit message when there is no valid web3 provider
degrigis Feb 1, 2024
f666c85
explicit message when there is no valid web3 provider + add keep_symb…
degrigis Feb 1, 2024
52f3b66
better printing in simgrviz
degrigis Feb 1, 2024
ef98e70
Merge branch 'main' of github.com:ucsb-seclab/greed into main
degrigis Feb 1, 2024
b9518be
size_sol and offset_sol can be None here
ruaronicola Feb 5, 2024
2c58781
Merge branch 'main' of github.com:ucsb-seclab/greed into main
ruaronicola Feb 5, 2024
bac1906
fix calldatasize
ruaronicola Feb 6, 2024
afba63c
Add more type annotations to sim manager
robmcl4 Feb 8, 2024
8af5263
Add type annotation for state.trace
robmcl4 Feb 13, 2024
f9c406a
Add type annotations to plugin
robmcl4 Feb 13, 2024
854895e
calldatasize must be handled at eval time
ruaronicola Mar 8, 2024
e27d411
set sym return value in call if has concrete len
ruaronicola Mar 8, 2024
5f0d982
use ws provider for web3py
ruaronicola Mar 8, 2024
6627a12
fix calldata eval
ruaronicola Mar 8, 2024
b92f3e4
Merge branch 'main' of github.com:ucsb-seclab/greed into main
ruaronicola Mar 8, 2024
36b43d0
bump gigahorse version
ruaronicola Mar 8, 2024
469ba15
attempt fix missing libyices
ruaronicola Mar 8, 2024
bfcecbb
enforce souffle version supported by gigahorse
ruaronicola Mar 8, 2024
5fd61b1
typos
ruaronicola Mar 15, 2024
4a9a5fd
warn (not raise) on invalid args or uninitialized var
ruaronicola Mar 15, 2024
7424bd1
remove unused imports, connect to w3 only on-demand
ruaronicola Mar 15, 2024
8b9289d
remove unused imports
ruaronicola Mar 15, 2024
1ea102f
if we get here we really need web3 -- fail if not connected
ruaronicola Mar 15, 2024
b4910fa
only invalidate lambda mem cache if write index could overlap with an…
ruaronicola Mar 23, 2024
46bcf40
use findall in run.py
ruaronicola Mar 23, 2024
bd3e84e
Handle statement IDs that result from inlining
robmcl4 Apr 20, 2024
0fad5ec
Fix: add readn constraints to memory_constraints array
robmcl4 Apr 21, 2024
e2fdd3e
Adds dump to smt2 support.
robmcl4 Apr 21, 2024
4e1439f
Merge pull request #16 from ucsb-seclab/robmcl4/print-smt2
robmcl4 Apr 24, 2024
ca4f453
Adds serialize / deserialize Yices2 solver states via pickle
robmcl4 May 1, 2024
d60b8f2
run formatter
robmcl4 May 1, 2024
db17040
abstract dispose method
robmcl4 May 3, 2024
d7e41fc
Switch statement ordering to use TAC_Statement_Next relation
robmcl4 May 6, 2024
523dbd1
Merge branch 'main' into robmcl4/pickle_solver
robmcl4 May 6, 2024
0cd8f90
Merge pull request #18 from ucsb-seclab/robmcl4/pickle_solver
robmcl4 May 6, 2024
50e9ad7
oops; do not call __del__ on super...
robmcl4 May 6, 2024
42f7ad2
oops again; use hasattr to check if _value is set
robmcl4 May 6, 2024
c4e7bd5
Merge pull request #19 from ucsb-seclab/robmcl4/stmt_next
degrigis May 8, 2024
bf24889
Fix tests
robmcl4 May 10, 2024
2524672
Merge pull request #21 from ucsb-seclab/robmcl4/fix_tests
robmcl4 May 10, 2024
4485d19
upgrade github action workflow checkout version
robmcl4 May 10, 2024
8438ae4
oops; upgrade checkout one more time
robmcl4 May 10, 2024
0f0e515
upgrade gh workflow action setup-python to v5
robmcl4 May 10, 2024
b8e5149
trigger test action on PR
robmcl4 May 10, 2024
8d4fabc
Make solver stop on ctrl+c (instead of hanging...)
robmcl4 May 10, 2024
dfffb30
Merge pull request #20 from ucsb-seclab/robmcl4/ctrl_c_hung_solver
robmcl4 May 10, 2024
c9ecd76
When possible, read in chunks when using readn
robmcl4 May 11, 2024
80dd0c8
work dump: safemath func detection
robmcl4 May 23, 2024
f16a2ea
bulk test safemath
robmcl4 May 23, 2024
9dddc12
safemath detection exceptions are warnings, +shorter timeout
robmcl4 May 23, 2024
b322707
add safe sub (and signed version)
robmcl4 May 24, 2024
bb00bcc
add safe mul, mul signed, div
robmcl4 May 27, 2024
566093f
add safemath signed div
robmcl4 May 27, 2024
f8349c8
smod; tests for autopatch; bugfixes
robmcl4 May 27, 2024
05d79c4
formatting
robmcl4 May 27, 2024
8f309d0
add option to auto-patch safemath
robmcl4 May 27, 2024
e0fe3f6
small fix: typing on sim manager
robmcl4 May 28, 2024
502c61a
bugfix in yices solver
robmcl4 May 28, 2024
15e5b32
do not check for loops in safemath
robmcl4 May 29, 2024
e02a636
fix div by zero bug
robmcl4 May 30, 2024
a4f3fbb
Merge remote-tracking branch 'private/robmcl4/auto_patch_safemath' in…
robmcl4 May 30, 2024
6e66bfc
if apt not in an updated state => can break the flow
ph4ge Jun 14, 2024
27daba2
added dependencies for souffle
ph4ge Jun 14, 2024
d5f31fa
revert added dependencies because already taken care of. However, ubu…
ph4ge Jun 14, 2024
b21b1c1
Merge pull request #23 from ph4ge/main
degrigis Jun 20, 2024
5e3b64f
Merge pull request #22 from ucsb-seclab/robmcl4/readn_in_chunks
ruaronicola Jun 20, 2024
01b80bc
🍎: added has_single_sol API in solver plugin
degrigis Aug 5, 2024
1c9ca67
🍎: added MCOPY handler
degrigis Aug 6, 2024
89c53c0
Typo in core.md
bossjoker1 Oct 30, 2024
83781a2
Merge pull request #27 from bossjoker1/main
degrigis Oct 30, 2024
39c18cc
setup.sh
MingxuanYao0528 Dec 10, 2024
65e3a87
greed/state_plugins/hook
MingxuanYao0528 Dec 10, 2024
99928d0
Merge pull request #28 from MingxuanYao0528/fix-setup
degrigis Dec 11, 2024
8a5d791
Merge branch 'main' into hook
MingxuanYao0528 Dec 16, 2024
77188c8
Revert "greed/state_plugins/hook"
MingxuanYao0528 Dec 16, 2024
0c76106
greed/inspect.py
MingxuanYao0528 Dec 16, 2024
025a506
Merge pull request #29 from MingxuanYao0528/hook
degrigis Dec 17, 2024
0875624
writen to write n bytes
khvitri Dec 19, 2024
cd29d32
Merge pull request #30 from khvitri/writen
degrigis Dec 20, 2024
6bfe98a
blacklist exploration technique
MingxuanYao0528 Jan 7, 2025
a8f8ab9
Merge pull request #31 from MingxuanYao0528/blacklist
degrigis Jan 8, 2025
0f4749f
use bytes.fromhex to load hex code
syang-ng Jan 28, 2025
0981279
Merge pull request #32 from syang-ng/fix-code-loading
degrigis Feb 25, 2025
7d2a138
add support for TLOAD/TSTORE
lcfr-eth Mar 7, 2025
806f323
Merge pull request #33 from lcfr-eth/TSTORE+TLOAD
degrigis Apr 4, 2025
8dd88ee
Merge branch 'main' into robmcl4/auto_patch_safemath
robmcl4 Apr 4, 2025
ca91871
remove match statements, use if/else instead
robmcl4 Apr 5, 2025
732541d
Merge pull request #34 from ucsb-seclab/robmcl4/auto_patch_safemath
degrigis Apr 7, 2025
d091c30
tload/tstore should write to transient storage
ruaronicola Apr 14, 2025
0983170
bugfix: YicesTermBVZeroExtend not suppported in symbols_referenced_at
robmcl4 Aug 20, 2025
457cae2
add test
robmcl4 Aug 20, 2025
bd84946
Merge pull request #39 from ucsb-seclab/robmcl4/fix_bvzeroext
degrigis Aug 20, 2025
f6e6cf8
Implement PHI as an instruction handled at execution-time
robmcl4 Feb 6, 2024
00a5b80
update the PHI instruction
syang-ng Aug 8, 2025
3a44049
use the old block parsing logic
syang-ng Aug 10, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions .github/workflows/python-app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
name: Tests

on:
pull_request:
workflow_dispatch:
schedule:
- cron: "0 0 * * *"
Expand All @@ -15,7 +16,7 @@ jobs:
outputs:
should_run: ${{ steps.should_run.outputs.should_run }}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: print latest_commit
run: echo ${{ github.sha }}
- id: should_run
Expand All @@ -28,9 +29,9 @@ jobs:
if: ${{ needs.check_date.outputs.should_run != 'false' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Set up Python 3.8
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: 3.8
- uses: actions/cache@v3
Expand Down
7 changes: 7 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"python.testing.pytestArgs": [
"tests"
],
"python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true
}
4 changes: 2 additions & 2 deletions docs/docs/core.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ A state offers many API for its inspection:

```python
>>> entry_state.curr_stmt # print the current statement ready for execution
>>> entry_state.revert # weather the state reverted or not
>>> entry_state.revert # whether the state reverted or not
>>> entry_state.instruction_count # how many instructions have been executed up to this point
>>> entry_state.pc # current program counter
>>> entry_state.registers # overview of all the virtual registers defined during the execution up to this point
Expand Down Expand Up @@ -162,4 +162,4 @@ The default solver employed by greed is [Yices2](https://github.com/SRI-CSL/yice
We made this decision based on the impressive results obtained in the latest SMT solving competition, and based on the results showed by Frank et al. in [ETHBMC](https://www.usenix.org/system/files/sec20fall_frank_prepub_0.pdf).
That being said, greed offers a modular architecure and implementing support for a new solver backend is quite straigthforward.

By default, we give unlimited time to the solver to solve the constraints. However, it can be sometimes helpful to timeout after a certain amount of time. You can do this by using the option `SOLVER_TIMEOUT`. Keep in mind that, when the timeout fires, the state will be reported as `errored`.
By default, we give unlimited time to the solver to solve the constraints. However, it can be sometimes helpful to timeout after a certain amount of time. You can do this by using the option `SOLVER_TIMEOUT`. Keep in mind that, when the timeout fires, the state will be reported as `errored`.
79 changes: 26 additions & 53 deletions greed/TAC/TAC_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from ast import literal_eval
from collections import defaultdict
from typing import Dict, List, Mapping, Tuple
import networkx as nx

from eth_utils import keccak

Expand All @@ -18,28 +19,42 @@

log = logging.getLogger(__name__)


class TAC_parser:
"""
This class parses the TAC facts generated by Gigahorse and builds the CFG.
"""
factory: Factory
target_dir: str

phimap: Mapping[str, str]
statement_to_blocks_map: Mapping[str, List[str]]


def __init__(self, factory: Factory, target_dir: str):
self.factory = factory
self.target_dir = target_dir

self.phimap = None
self.statement_to_blocks_map = None

@staticmethod
def stmt_sort_key(stmt_id: str) -> int:
return int(stmt_id.split('0x')[1].split('_')[0], base=16)
"""
Statements may be identified by a number of different formats. This function
returns a key that can be used to sort them in a consistent way.

Possible formats:
- 0x12345
- 0x12345_0x456
- 0x12345S0x456
"""
if '_' in stmt_id:
assert 'S' not in stmt_id
return int(stmt_id.split('_')[1], base=16)
elif 'S' in stmt_id:
assert '_' not in stmt_id
return int(stmt_id.split('0x')[1].split('S')[0], base=16)
else:
return int(stmt_id.split('0x')[1], base=16)


def parse_statements(self) -> Dict[str, TAC_Statement]:
# Load facts
Expand Down Expand Up @@ -87,7 +102,7 @@ def parse_statements(self) -> Dict[str, TAC_Statement]:
# parse all statements block after block
statements: Dict[str, TAC_Statement] = dict()
for block_id in itertools.chain(*tac_function_blocks.values()):
for stmt_id in sorted(tac_block_stmts[block_id], key=TAC_parser.stmt_sort_key):
for stmt_id in tac_block_stmts[block_id]:
opcode = tac_op[stmt_id]
raw_uses = [var for var, _ in sorted(tac_uses[stmt_id], key=lambda x: x[1])]
raw_defs = [var for var, _ in sorted(tac_defs[stmt_id], key=lambda x: x[1])]
Expand All @@ -101,7 +116,7 @@ def parse_statements(self) -> Dict[str, TAC_Statement]:
# Adding metadata for CALL statements
if stmt_id in fixed_calls:
log.debug(f"Setting {statement} as fixed call to {fixed_calls[stmt_id]}")
statement.set_likeyl_known_target_func(fixed_calls[stmt_id])
statement.set_likely_known_target_func(fixed_calls[stmt_id])

if not tac_block_stmts[block_id]:
# Gigahorse sometimes creates empty basic blocks. If so, inject a NOP statement
Expand All @@ -112,46 +127,6 @@ def parse_statements(self) -> Dict[str, TAC_Statement]:
fake_exit_stmt = TAC_Stop(block_id='fake_exit', stmt_id='fake_exit')
statements['fake_exit'] = fake_exit_stmt

# parse phi-map and re-write statements
# build phi-map (as in gigahorse decompiler)
phimap = dict()
for stmt in statements.values():
if stmt.__internal_name__ != 'PHI':
continue
#phimap[stmt.res1_var] = stmt.res1_var
for v in stmt.arg_vars:
if v in phimap:
phimap[stmt.res1_var] = phimap[v]
continue
phimap[v] = stmt.res1_var

# propagate phi map
fixpoint = False
while not fixpoint:
fixpoint = True
for v_old, v_new in phimap.items():
# (phimap[v_old] != phimap[v_new]) --> means "not already at local fixpoint"
if v_new in phimap and (phimap[v_old] != phimap[v_new]):
phimap[v_old] = phimap[v_new]
fixpoint = False

self.phimap = phimap

# rewrite statements according to PHI map
for stmt in statements.values():
if stmt.__internal_name__ == "PHI":
# remove all phi statements
statements[stmt.id] = TAC_Nop(block_id=stmt.block_id, stmt_id=stmt.id)
continue
# rewrite other statements according to the phi map
stmt.arg_vars = [phimap.get(v, v) for v in stmt.arg_vars]
stmt.arg_vals = {phimap.get(v, v): val for v, val in stmt.arg_vals.items()}
stmt.res_vars = [phimap.get(v, v) for v in stmt.res_vars]
stmt.res_vals = {phimap.get(v, v): val for v, val in stmt.res_vals.items()}

# re-process args
stmt.process_args()

return statements

def parse_blocks(self) -> Dict[str, Block]:
Expand Down Expand Up @@ -188,6 +163,7 @@ def parse_blocks(self) -> Dict[str, Block]:

return blocks


def parse_functions(self) -> Dict[str, TAC_Function]:
# Load facts
tac_block_function = load_csv_map(f"{self.target_dir}/InFunction.csv")
Expand Down Expand Up @@ -229,7 +205,7 @@ def parse_functions(self) -> Dict[str, TAC_Function]:
# rewrite aliases according to PHI map
translate_alias = lambda alias: 'v' + alias.replace('0x', '')
for function in functions.values():
function.arguments = [self.phimap.get(translate_alias(a), translate_alias(a)) for a in function.arguments]
function.arguments = [translate_alias(a) for a in function.arguments]

return functions

Expand All @@ -239,7 +215,7 @@ def parse_blocks_in_loop(self):

def parse_induction_variables(self):
induction_variables = load_csv_multimap(f"{self.target_dir}/InductionVariable.csv", reverse=True)
induction_variables = {x: {self.phimap.get('v' + _y.replace('0x', ''), 'v' + _y.replace('0x', '')) for _y in y}
induction_variables = {x: {'v' + _y.replace('0x', '') for _y in y}
for x, y in induction_variables.items()}
return induction_variables

Expand All @@ -249,7 +225,6 @@ def parse_induction_variable_starts_at_const(self):
for _x, _y, _z in values:
y = _y[1:-1].split(", ")[1]
y = 'v' + y.replace('0x', '')
y = self.phimap.get(y, y)
starts_at_const[_x][y] = literal_eval(_z) # seems to not have a consistent base (either 10 or 16)
return starts_at_const

Expand All @@ -259,7 +234,6 @@ def parse_induction_variable_increases_by_const(self):
for _x, _y, _z in values:
y = _y[1:-1].split(", ")[1]
y = 'v' + y.replace('0x', '')
y = self.phimap.get(y, y)
increases_by_const[_x][y] = literal_eval(_z) # seems to not have a consistent base (either 10 or 16)
return increases_by_const

Expand All @@ -269,8 +243,7 @@ def parse_induction_variable_upper_bounds(self):
for _x, _y, _z in values:
y = _y[1:-1].split(", ")[1]
y = 'v' + y.replace('0x', '')
y = self.phimap.get(y, y)
upper_bounds[_x][y] = self.phimap.get('v' + _z.replace('0x', ''), 'v' + _z.replace('0x', ''))
upper_bounds[_x][y] = 'v' + _z.replace('0x', '')
return upper_bounds

def parse_guarding_slots(self):
Expand Down Expand Up @@ -330,4 +303,4 @@ def parse_recovered_abi(self):
if proto:
f.name = proto

return abi
return abi
8 changes: 5 additions & 3 deletions greed/TAC/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

from greed.solver.shortcuts import *
from greed.state import SymbolicEVMState
from greed.utils.exceptions import VMException

log = logging.getLogger(__name__)

Expand Down Expand Up @@ -118,9 +117,12 @@ def set_arg_val(self, state: SymbolicEVMState):
if arg_val is not None:
log.warning(f"Uninitialized var {var} RECOVERED WITH CACHED CONSTANT VALUE")
else:
raise VMException(f"Uninitialized var {var}")
# raise VMException(f"Uninitialized var {var}")
log.warning(f"Uninitialized var {var} - VERY LIKELY TO CAUSE PROBLEMS")
val = state.registers.get(var, None) if arg_val is None else arg_val
state.registers[var] = val
# avoid assigning the same value to the register
if arg_val is not None:
state.registers[var] = val
self.arg_vals[var] = val
object.__setattr__(self, "arg{}_val".format(i + 1), val)

Expand Down
21 changes: 11 additions & 10 deletions greed/TAC/flow_ops.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import logging

from greed import options
from greed import utils
from greed.TAC.base import TAC_Statement
from greed.solver.shortcuts import *
from greed.state import SymbolicEVMState
Expand Down Expand Up @@ -112,22 +111,26 @@ def _handle(self, state: SymbolicEVMState, gas_val=None, address_val=None, value
state.returndata['instruction_count'] = state.instruction_count

if is_concrete(address_val) and bv_unsigned_value(address_val) == 0:
logging.info("Calling into burn contract")
log.info("Calling into burn contract")
if is_concrete(olen):
# If we have a concrete len for the return value we set the output memory to symbolic data
for i in range(bv_unsigned_value(olen)):
state.memory[BV_Add(ostart, BVV(i, 256))] = BVS(f'EXT_{state.instruction_count}_{i}_{state.xid}', 8)
elif is_concrete(address_val) and bv_unsigned_value(address_val) >= 1 and bv_unsigned_value(address_val) <= 8:
# This is a pre-compiled contract
# --> https://www.evm.codes/precompiled?fork=arrowGlacier
if bv_unsigned_value(address_val) == 1:
# ECRecover precompiled contract
# FIXME
logging.info("Calling precompiled ecRecover contract")
log.info("Calling precompiled ecRecover contract")
raise VMSymbolicError(f"Precompiled contract [ecRecover] not implemented")
elif bv_unsigned_value(address_val) == 2:
# SHA256 precompiled contract
# FIXME
logging.info("Calling precompiled SHA2-256 contract")
log.info("Calling precompiled SHA2-256 contract")
raise VMSymbolicError(f"Precompiled contract [SHA2-256] not implemented")
elif bv_unsigned_value(address_val) == 4:
logging.info("Calling precompiled identity contract")
log.info("Calling precompiled identity contract")
istart = argsOffset_val
ilen = argsSize_val
state.memory.memcopy(ostart, state.memory.copy(state), istart, ilen)
Expand All @@ -139,10 +142,8 @@ def _handle(self, state: SymbolicEVMState, gas_val=None, address_val=None, value
# If we have a concrete len for the return value we set the output memory to symbolic data
for i in range(bv_unsigned_value(olen)):
state.memory[BV_Add(ostart, BVV(i, 256))] = BVS(f'EXT_{state.instruction_count}_{i}_{state.xid}', 8)
log_address_val = bv_unsigned_value(address_val) if is_concrete(address_val) else "<SYMBOLIC>"

if log_address_val != "<SYMBOLIC>":
logging.info(f"Calling contract {hex(log_address_val)} ({state.instruction_count}_{state.xid})")
log_address_val = hex(bv_unsigned_value(address_val)) if is_concrete(address_val) else "<SYMBOLIC>"
log.info(f"Calling contract {log_address_val} ({state.instruction_count}_{state.xid})")
else:
# FIXME: maybe consider a MAX_RETURN_SIZE option and use similar strategy used in SHA3
raise VMSymbolicError("Unsupported symbolic retSize_val in CALL")
Expand All @@ -155,7 +156,7 @@ def _handle(self, state: SymbolicEVMState, gas_val=None, address_val=None, value
state.set_next_pc()
return [state]

def set_likeyl_known_target_func(self, target_function):
def set_likely_known_target_func(self, target_function):
self.likely_known_target = target_function

class TAC_Call(TAC_BaseCall):
Expand Down
Loading