Skip to content

Commit

Permalink
selectors: fix FN on some vyper contracts, issue #7
Browse files Browse the repository at this point in the history
  • Loading branch information
cdump committed Aug 3, 2024
1 parent 577725f commit 9f02805
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 52 deletions.
40 changes: 22 additions & 18 deletions evmole/selectors.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,26 +42,30 @@ def process(vm: Vm, gas_limit: int) -> tuple[set[bytes], int]:
case (Op.MUL, _, Element('signature'), _) | (Op.MUL, _, _, Element('signature')) | (Op.SHR, _, _, Element('mulsig')):
vm.stack.peek().label = 'mulsig'

# Vyper _selector_section_dense()
case (Op.MOD, _, Element('mulsig') | Element('signature'), Element() as s1):
ma = int.from_bytes(s1.data, 'big')
if ma < 128:
for m in range(1, ma):
cloned_vm = copy.copy(vm)
cloned_vm.stack.peek().data = m.to_bytes(32, 'big')
s, g = process(cloned_vm, (gas_limit - gas_used) // ma)
selectors.update(s)
gas_used += g
if gas_used > gas_limit:
break
vm.stack.peek().data = (0).to_bytes(32, 'big')

# Vyper _selector_section_dense()/_selector_section_sparse()
# (sig MOD n_buckets) or (sig AND (n_buckets-1))
case (
(Op.SHR, _, _, Element('calldata'))
| (Op.AND, _, Element('signature'), _)
| (Op.AND, _, _, Element('signature'))
| (Op.DIV, _, Element('calldata'), _)
(Op.MOD as op, _, Element('mulsig') | Element('signature'), Element() as s1)
| (Op.AND as op, _, Element('signature'), Element() as s1)
| (Op.AND as op, _, Element() as s1, Element('signature'))
):
if op == Op.AND and s1.data == b'\x00' * 28 + b'\xff\xff\xff\xff':
vm.stack.peek().label = 'signature'
else:
ma = int.from_bytes(s1.data, 'big')
if ma < 256:
to = ma if op == Op.MOD else ma + 1
for m in range(1, to):
cloned_vm = copy.copy(vm)
cloned_vm.stack.peek().data = m.to_bytes(32, 'big')
s, g = process(cloned_vm, (gas_limit - gas_used) // ma)
selectors.update(s)
gas_used += g
if gas_used > gas_limit:
break
vm.stack.peek().data = (0).to_bytes(32, 'big')

case (Op.SHR, _, _, Element('calldata')) | (Op.DIV, _, Element('calldata'), _):
v = vm.stack.peek()
if v.data[-4:] == vm.calldata.data[:4]:
vm.stack.peek().label = 'signature'
Expand Down
55 changes: 29 additions & 26 deletions js/src/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,21 +59,37 @@ function process(vm, gasLimit) {
}
break

// Vyper _selector_section_dense()
// Vyper _selector_section_dense()/_selector_section_sparse()
// (sig MOD n_buckets) or (sig AND (n_buckets-1))
case Op.MOD:
if (r0.label === 'mulsig' || r0.label === 'signature') {
const rawMa = uint8ArrayToBigInt(r1.data)
if (rawMa < 128n) {
const ma = Number(rawMa)
vm.stack.pop()
for (let m = 1; m < ma && gasUsed < gasLimit; m++) {
const clonedVm = vm.clone()
clonedVm.stack.push_uint(BigInt(m))
const [newSelectors, gas] = process(clonedVm, Math.trunc((gasLimit - gasUsed) / ma))
newSelectors.forEach((v) => selectors.add(v))
gasUsed += gas
case Op.AND:
{
const p = vm.stack.peek()
if (
(op === Op.AND && (r0.label === 'signature' || r1.label === 'signature')) ||
(op === Op.MOD && (r0.label === 'mulsig' || r0.label === 'signature'))
) {
const otd = op === Op.AND && r1.label == 'signature' ? r0.data : r1.data
const rawMa = uint8ArrayToBigInt(otd)
if (op === Op.AND && rawMa === 0xffffffffn) {
p.label = 'signature'
} else {
if (rawMa < 256n) {
const ma = Number(rawMa)
vm.stack.pop()
const to = op === Op.MOD ? ma : ma + 1
for (let m = 1; m < to && gasUsed < gasLimit; m++) {
const clonedVm = vm.clone()
clonedVm.stack.push_uint(BigInt(m))
const [newSelectors, gas] = process(clonedVm, Math.trunc((gasLimit - gasUsed) / ma))
newSelectors.forEach((v) => selectors.add(v))
gasUsed += gas
}
vm.stack.push_uint(0n)
}
}
vm.stack.push_uint(0n)
} else if (op === Op.AND && (r0.label === 'calldata' || r1.label === 'calldata')) {
p.label = 'calldata'
}
}
break
Expand All @@ -92,19 +108,6 @@ function process(vm, gasLimit) {
}
break

case Op.AND:
{
const p = vm.stack.peek()
if (r0.label === 'signature' || r1.label === 'signature') {
if (p.data.slice(-4).every((v, i) => v === vm.calldata.data[i])) {
p.label = 'signature'
}
} else if (r0.label === 'calldata' || r1.label === 'calldata') {
p.label = 'calldata'
}
}
break

case Op.DIV:
if (r0.label === 'calldata') {
const p = vm.stack.peek()
Expand Down
22 changes: 14 additions & 8 deletions rust/src/selectors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ enum Label {
MulSig,
}

const VAL_FFFFFFFF_B: [u8; 32] = ruint::uint!(0xffffffff_U256).to_be_bytes();

fn analyze(
vm: &mut Vm<Label>,
selectors: &mut BTreeSet<Selector>,
Expand Down Expand Up @@ -45,13 +47,19 @@ fn analyze(
vm.stack.peek_mut()?.label = Some(Label::MulSig);
}

// Vyper _selector_section_dense()
StepResult{op: op::MOD, fa: Some(Element{label: Some(Label::MulSig | Label::Signature), ..}), sa: Some(ot), ..} =>
// Vyper _selector_section_dense()/_selector_section_sparse()
// (sig MOD n_buckets) or (sig AND (n_buckets-1))
StepResult{op: op @ op::MOD, fa: Some(Element{label: Some(Label::MulSig | Label::Signature), ..}), sa: Some(ot), ..}
| StepResult{op: op @ op::AND, fa: Some(Element{label: Some(Label::Signature), ..}), sa: Some(ot), ..}
| StepResult{op: op @ op::AND, sa: Some(Element{label: Some(Label::Signature), ..}), fa: Some(ot), ..} =>
{
let t: Result<u8, _> = U256::from_be_bytes(ot.data).try_into();
if let Ok(ma) = t {
if ma < 128 {
for m in 1..ma {
if op == op::AND && ot.data == VAL_FFFFFFFF_B {
vm.stack.peek_mut()?.label = Some(Label::Signature);
} else {
let ot8: Result<u8, _> = U256::from_be_bytes(ot.data).try_into();
if let Ok(ma) = ot8 {
let to = if op == op::MOD { ma } else { ma + 1 };
for m in 1..to {
let mut vm_clone = vm.clone();
vm_clone.stack.peek_mut()?.data = U256::from(m).to_be_bytes();
*gas_used += process(vm_clone, selectors, (gas_limit - *gas_used) / (ma as u32));
Expand All @@ -65,8 +73,6 @@ fn analyze(
}

StepResult{op: op::SHR, sa: Some(Element{label: Some(Label::CallData), ..}), ..}
| StepResult{op: op::AND, fa: Some(Element{label: Some(Label::Signature), ..}), ..}
| StepResult{op: op::AND, sa: Some(Element{label: Some(Label::Signature), ..}), ..}
| StepResult{op: op::DIV, fa: Some(Element{label: Some(Label::CallData), ..}), ..} =>
{
let v = vm.stack.peek_mut()?;
Expand Down

0 comments on commit 9f02805

Please sign in to comment.