def test_arithmetic_simplify(self): cs = ConstraintSet() arr = cs.new_array(name="MEM") a = cs.new_bitvec(32, name="VARA") b = cs.new_bitvec(32, name="VARB") c = a * 2 + b self.assertEqual(translate_to_smtlib(c), "(bvadd (bvmul VARA #x00000002) VARB)") self.assertEqual( translate_to_smtlib((c + 4) - 4), "(bvsub (bvadd (bvadd (bvmul VARA #x00000002) VARB) #x00000004) #x00000004)", ) d = c + 4 s = arithmetic_simplify(d - c) self.assertIsInstance(s, Constant) self.assertEqual(s.value, 4) # size = arithmetic_simplify(size cs2 = ConstraintSet() exp = cs2.new_bitvec(32) exp |= 0 exp &= 1 exp |= 0 self.assertEqual(get_depth(exp), 4) self.assertEqual( translate_to_smtlib(exp), "(bvor (bvand (bvor BIVEC #x00000000) #x00000001) #x00000000)" ) exp = arithmetic_simplify(exp) self.assertTrue(get_depth(exp) < 4) self.assertEqual(translate_to_smtlib(exp), "(bvand BIVEC #x00000001)")
def testBasicArrayStore(self): name = "bitarray" cs = ConstraintSet() # make array of 32->8 bits array = cs.new_array(32, name=name) # make free 32bit bitvector key = cs.new_bitvec(32) # assert that the array is 'A' at key position array = array.store(key, ord("A")) # let's restrict key to be greater than 1000 cs.add(key.ugt(1000)) # 1001 position of array can be 'A' self.assertTrue(self.solver.can_be_true(cs, array.select(1001) == ord("A"))) # 1001 position of array can be 'B' self.assertTrue(self.solver.can_be_true(cs, array.select(1001) == ord("B"))) # name is correctly proxied self.assertEqual(array.name, name) with cs as temp_cs: # but if it is 'B' ... temp_cs.add(array.select(1001) == ord("B")) # then key can not be 1001 temp_cs.add(key == 1001) self.assertFalse(self.solver.check(temp_cs)) with cs as temp_cs: # If 1001 position is 'B' ... temp_cs.add(array.select(1001) == ord("B")) # then key can be 1002 for ex.. temp_cs.add(key != 1002) self.assertTrue(self.solver.check(temp_cs))
def testBasicArrayProxySymbIdx2(self): cs = ConstraintSet() array = cs.new_array(index_bits=32, value_bits=32, name="array") key = cs.new_bitvec(32, name="key") index = cs.new_bitvec(32, name="index") array[0] = 1 # Write 1 to first location array[key] = 2 # Write 2 to a symbolic (potentially any (potentially 0))location solutions = self.solver.get_all_values(cs, array[0]) # get a concrete solution for index self.assertItemsEqual(solutions, (1, 2)) solutions = self.solver.get_all_values( cs, array.get(0, 100) ) # get a concrete solution for index 0 self.assertItemsEqual(solutions, (1, 2)) solutions = self.solver.get_all_values( cs, array.get(1, 100) ) # get a concrete solution for index 1 (default 100) self.assertItemsEqual(solutions, (100, 2)) self.assertTrue( self.solver.can_be_true(cs, array[1] == 12345) ) # no default so it can be anything
def testBasicArraySlice(self): hw = bytearray(b"Hello world!") cs = ConstraintSet() # make array of 32->8 bits array = cs.new_array(32, index_max=12) array = array.write(0, hw) array_slice = array[0:2] self.assertTrue(self.solver.must_be_true(cs, array == hw)) self.assertTrue( self.solver.must_be_true(cs, array_slice[0] == array[0])) self.assertTrue( self.solver.must_be_true(cs, array_slice[0:2][1] == array[1])) array_slice[0] = ord("A") self.assertTrue( self.solver.must_be_true(cs, array_slice[0] == ord("A"))) self.assertTrue( self.solver.must_be_true(cs, array_slice[0:2][1] == array[1])) self.assertTrue(self.solver.must_be_true(cs, array == hw)) # Testing some slicing combinations self.assertRaises( IndexError, lambda i: translate_to_smtlib(array_slice[0:1000][i]), 1002) self.assertTrue( self.solver.must_be_true(cs, array_slice[0:1000][0] == ord("A"))) self.assertTrue( self.solver.must_be_true(cs, array_slice[0:1000][1] == array[1])) self.assertTrue( self.solver.must_be_true( cs, array_slice[0:1000][:2][1] == array[:2][1])) self.assertTrue( self.solver.must_be_true(cs, array_slice[0:1000][:2][0] == ord("A")))
def testBasicArray256(self): cs = ConstraintSet() # make array of 32->8 bits array = cs.new_array(32, value_bits=256) # make free 32bit bitvector key = cs.new_bitvec(32) # assert that the array is 111...111 at key position cs.add(array[key] == 11111111111111111111111111111111111111111111) # let's restrict key to be greater than 1000 cs.add(key.ugt(1000)) with cs as temp_cs: # 1001 position of array can be 111...111 temp_cs.add(array[1001] == 11111111111111111111111111111111111111111111) self.assertTrue(self.solver.check(temp_cs)) with cs as temp_cs: # 1001 position of array can also be 222...222 temp_cs.add(array[1001] == 22222222222222222222222222222222222222222222) self.assertTrue(self.solver.check(temp_cs)) with cs as temp_cs: # but if it is 222...222 ... temp_cs.add(array[1001] == 22222222222222222222222222222222222222222222) # then key can not be 1001 temp_cs.add(key == 1001) self.assertFalse(self.solver.check(temp_cs)) with cs as temp_cs: # If 1001 position is 222...222 ... temp_cs.add(array[1001] == 22222222222222222222222222222222222222222222) # then key can be 1002 for ex.. temp_cs.add(key == 1002) self.assertTrue(self.solver.check(temp_cs))
def test_visitors(self): solver = Z3Solver.instance() cs = ConstraintSet() arr = cs.new_array(name="MEM") a = cs.new_bitvec(32, name="VAR") self.assertEqual(get_depth(a), 1) cond = Operators.AND(a < 200, a > 100) arr[0] = ord("a") arr[1] = ord("b") self.assertEqual(get_depth(cond), 3) self.assertEqual(get_depth(arr[a + 1]), 4) self.assertEqual( translate_to_smtlib(arr[a + 1]), "(select (store (store MEM #x00000000 #x61) #x00000001 #x62) (bvadd VAR #x00000001))", ) arr[3] = arr[a + 1] aux = arr[a + Operators.ZEXTEND(arr[a], 32)] self.assertEqual(get_depth(aux), 9) self.maxDiff = 1500 self.assertEqual( translate_to_smtlib(aux), "(select (store (store (store MEM #x00000000 #x61) #x00000001 #x62) #x00000003 (select (store (store MEM #x00000000 #x61) #x00000001 #x62) (bvadd VAR #x00000001))) (bvadd VAR ((_ zero_extend 24) (select (store (store (store MEM #x00000000 #x61) #x00000001 #x62) #x00000003 (select (store (store MEM #x00000000 #x61) #x00000001 #x62) (bvadd VAR #x00000001))) VAR))))", ) values = arr[0:2] self.assertEqual(len(values), 2) self.assertItemsEqual(solver.get_all_values(cs, values[0]), [ord("a")]) self.assertItemsEqual(solver.get_all_values(cs, values[1]), [ord("b")]) arr[1:3] = "cd" values = arr[0:3] self.assertEqual(len(values), 3) self.assertItemsEqual(solver.get_all_values(cs, values[0]), [ord("a")]) self.assertItemsEqual(solver.get_all_values(cs, values[1]), [ord("c")]) self.assertItemsEqual(solver.get_all_values(cs, values[2]), [ord("d")]) self.assertEqual( pretty_print(aux, depth=2), "ArraySelect\n ArrayStore\n ...\n BitVecAdd\n ...\n") self.assertEqual(pretty_print(Operators.EXTRACT(a, 0, 8), depth=1), "BitVecExtract{0:7}\n ...\n") self.assertEqual(pretty_print(a, depth=2), "VAR\n") x = BitVecConstant(32, 100, taint=("important", )) y = BitVecConstant(32, 200, taint=("stuff", )) z = constant_folder(x + y) self.assertItemsEqual(z.taint, ("important", "stuff")) self.assertEqual(z.value, 300) self.assertRaises(Exception, translate_to_smtlib, 1) self.assertEqual( translate_to_smtlib(simplify(Operators.ZEXTEND(a, 32))), "VAR") self.assertEqual( translate_to_smtlib( simplify(Operators.EXTRACT(Operators.EXTRACT(a, 0, 8), 0, 8))), "((_ extract 7 0) VAR)", )
def testBasicArraySymbIdx(self): cs = ConstraintSet() array = cs.new_array(index_bits=32, value_bits=32, name="array") key = cs.new_bitvec(32, name="key") index = cs.new_bitvec(32, name="index") array[key] = 1 # Write 1 to a single location cs.add(array.get(index, default=0) != 0) # Constrain index so it selects that location cs.add(index != key) # key and index are the same there is only one slot in 1 self.assertFalse(self.solver.check(cs))
def test_serialize_bytes_symbolic(self): cs = ConstraintSet() buf = cs.new_array(index_max=17) ret = ABI.serialize('bytes', buf) # does the offset field look right? self.assertTrue(solver.must_be_true(cs, ret[0:32] == bytearray(b'\x00'*31 + b'\x20'))) # does the size field look right? self.assertTrue(solver.must_be_true(cs, ret[32:64] == bytearray(b'\x00'*31 + b'\x11'))) # does the data field look right? self.assertTrue(solver.must_be_true(cs, ret[64:64+32] == buf + bytearray(b'\x00'*15)))
def testBasicArrayProxySymbIdx(self): cs = ConstraintSet() array = cs.new_array(index_bits=32, value_bits=32, name="array", default=0) key = cs.new_bitvec(32, name="key") index = cs.new_bitvec(32, name="index") array[key] = 1 # Write 1 to a single location cs.add(array.get(index) != 0) # Constrain index so it selects that location a_index = self.solver.get_value(cs, index) # get a concrete solution for index cs.add(array.get(a_index) != 0) # now storage must have something at that location cs.add(a_index != index) # remove it from the solutions # It should not be another solution for index self.assertFalse(self.solver.check(cs))
def testBasicPickle(self): import pickle cs = ConstraintSet() #make array of 32->8 bits array = cs.new_array(32) #make free 32bit bitvector key = cs.new_bitvec(32) #assert that the array is 'A' at key position array = array.store(key, ord('A')) #let's restrict key to be greater than 1000 cs.add(key.ugt(1000)) cs = pickle.loads(pickle.dumps(cs)) self.assertTrue(self.solver.check(cs))
def testBasicArrayConcatSlice(self): hw = bytearray(b"Hello world!") cs = ConstraintSet() # make array of 32->8 bits array = cs.new_array(32, index_max=12) array = array.write(0, hw) self.assertTrue(self.solver.must_be_true(cs, array == hw)) self.assertTrue(self.solver.must_be_true(cs, array.read(0, 12) == hw)) self.assertTrue( self.solver.must_be_true(cs, array.read(6, 6) == hw[6:12])) self.assertTrue( self.solver.must_be_true( cs, bytearray(b"Hello ") + array.read(6, 6) == hw)) self.assertTrue( self.solver.must_be_true( cs, bytearray(b"Hello ") + array.read(6, 5) + bytearray(b"!") == hw)) self.assertTrue( self.solver.must_be_true( cs, array.read(0, 1) + bytearray(b"ello ") + array.read(6, 5) + bytearray(b"!") == hw, )) self.assertTrue(len(array[1:2]) == 1) self.assertTrue(len(array[0:12]) == 12) results = [] for c in array[6:11]: results.append(c) self.assertTrue(len(results) == 5)
def test_arithmetic_simplify_extract(self): cs = ConstraintSet() arr = cs.new_array(name='MEM') a = cs.new_bitvec(32, name='VARA') b = Operators.CONCAT(32, Operators.EXTRACT(a, 24, 8), Operators.EXTRACT(a, 16, 8), Operators.EXTRACT(a, 8, 8), Operators.EXTRACT(a, 0, 8)) self.assertEqual( translate_to_smtlib(b), '(concat ((_ extract 31 24) VARA) ((_ extract 23 16) VARA) ((_ extract 15 8) VARA) ((_ extract 7 0) VARA))' ) self.assertEqual(translate_to_smtlib(simplify(b)), 'VARA') c = Operators.CONCAT(16, Operators.EXTRACT(a, 16, 8), Operators.EXTRACT(a, 8, 8)) self.assertEqual( translate_to_smtlib(c), '(concat ((_ extract 23 16) VARA) ((_ extract 15 8) VARA))') self.assertEqual(translate_to_smtlib(simplify(c)), '((_ extract 23 8) VARA)')
def testBasicArray(self): cs = ConstraintSet() # make array of 32->8 bits array = cs.new_array(32) # make free 32bit bitvector key = cs.new_bitvec(32) # assert that the array is 'A' at key position # By default an smtlib can contain any value cs.add(array[key] == ord("A")) # let's restrict key to be greater than 1000 cs.add(key.ugt(1000)) with cs as temp_cs: # 1001 position of array can be 'A' temp_cs.add(array[1001] == ord("A")) self.assertTrue(self.solver.check(temp_cs)) with cs as temp_cs: # 1001 position of array can also be 'B' temp_cs.add(array[1001] == ord("B")) self.assertTrue(self.solver.check(temp_cs)) with cs as temp_cs: # but if it is 'B' ... temp_cs.add(array[1001] == ord("B")) # then key can not be 1001 temp_cs.add(key == 1001) self.assertFalse(self.solver.check(temp_cs)) with cs as temp_cs: # If 1001 position is 'B' ... temp_cs.add(array[1001] == ord("B")) # then key can be 1000 for ex.. temp_cs.add(key == 1002) self.assertTrue(self.solver.check(temp_cs))
def test_serialize_bytesM_symbolic(self): cs = ConstraintSet() buf = cs.new_array(index_max=17) ret = ABI.serialize('bytes32', buf) self.assertEqual(solver.minmax(cs, ret[0]), (0, 255)) self.assertEqual(solver.minmax(cs, ret[17]), (0, 0))
def test_return2(self): """ Testcase taken from https://github.com/ethereum/tests File: return2.json sha256sum: 25972361a5871003f44467255a656b9e7ba3762a5cfe02b56a0197318d375b9a Code: PUSH1 0x37 PUSH1 0x0 MSTORE8 PUSH1 0x0 MLOAD PUSH1 0x0 SSTORE PUSH1 0x21 PUSH1 0x0 RETURN """ def solve(val): return self._solve(constraints, val) constraints = ConstraintSet() blocknumber = constraints.new_bitvec(256, name='blocknumber') constraints.add(blocknumber == 0) timestamp = constraints.new_bitvec(256, name='timestamp') constraints.add(timestamp == 1) difficulty = constraints.new_bitvec(256, name='difficulty') constraints.add(difficulty == 256) coinbase = constraints.new_bitvec(256, name='coinbase') constraints.add( coinbase == 244687034288125203496486448490407391986876152250) gaslimit = constraints.new_bitvec(256, name='gaslimit') constraints.add(gaslimit == 10000000) world = evm.EVMWorld(constraints, blocknumber=blocknumber, timestamp=timestamp, difficulty=difficulty, coinbase=coinbase, gaslimit=gaslimit) acc_addr = 0xcd1722f3947def4cf144679da39c4c32bdc35681 acc_code = unhexlify('603760005360005160005560216000f3') acc_balance = constraints.new_bitvec( 256, name='balance_0xcd1722f3947def4cf144679da39c4c32bdc35681') constraints.add(acc_balance == 23) acc_nonce = constraints.new_bitvec( 256, name='nonce_0xcd1722f3947def4cf144679da39c4c32bdc35681') constraints.add(acc_nonce == 0) world.create_account(address=acc_addr, balance=acc_balance, code=acc_code, nonce=acc_nonce) address = 0xcd1722f3947def4cf144679da39c4c32bdc35681 caller = 0xf572e5295c57f15886f9b263e2f6d2d6c7b5ec6 price = constraints.new_bitvec(256, name='price') constraints.add(price == 100000000000000) value = constraints.new_bitvec(256, name='value') constraints.add(value == 23) gas = constraints.new_bitvec(256, name='gas') constraints.add(gas == 100000) data = constraints.new_array(index_max=1) constraints.add(data == b'\xaa') # open a fake tx, no funds send world._open_transaction('CALL', address, price, data, caller, value, gas=gas) # This variable might seem redundant in some tests - don't forget it is auto generated # and there are cases in which we need it ;) result, returndata = self._test_run(world) # World sanity checks - those should not change, right? self.assertEqual(solve(world.block_number()), 0) self.assertEqual(solve(world.block_gaslimit()), 10000000) self.assertEqual(solve(world.block_timestamp()), 1) self.assertEqual(solve(world.block_difficulty()), 256) self.assertEqual(solve(world.block_coinbase()), 0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba) # Add post checks for account 0xcd1722f3947def4cf144679da39c4c32bdc35681 # check nonce, balance, code self.assertEqual( solve(world.get_nonce(0xcd1722f3947def4cf144679da39c4c32bdc35681)), 0) self.assertEqual( solve( world.get_balance(0xcd1722f3947def4cf144679da39c4c32bdc35681)), 23) self.assertEqual( world.get_code(0xcd1722f3947def4cf144679da39c4c32bdc35681), unhexlify('603760005360005160005560216000f3')) # check storage self.assertEqual( solve( world.get_storage_data( 0xcd1722f3947def4cf144679da39c4c32bdc35681, 0x00)), 0x3700000000000000000000000000000000000000000000000000000000000000) # check outs self.assertEqual( returndata, unhexlify( '370000000000000000000000000000000000000000000000000000000000000000' )) # check logs logs = [ Log(unhexlify('{:040x}'.format(l.address)), l.topics, solve(l.memlog)) for l in world.logs ] data = rlp.encode(logs) self.assertEqual( sha3.keccak_256(data).hexdigest(), '1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347') # test used gas self.assertEqual(solve(world.current_vm.gas), 79970)
def test_TestNameRegistrator(self): """ Testcase taken from https://github.com/ethereum/tests File: TestNameRegistrator.json sha256sum: dd000f5977416de19410170b8a7acb5060011594fd97c81f307f015cd5bdd51e Code: PUSH1 0x0 CALLDATALOAD SLOAD ISZERO PUSH1 0x9 JUMPI STOP JUMPDEST PUSH1 0x20 CALLDATALOAD PUSH1 0x0 CALLDATALOAD SSTORE """ def solve(val): return self._solve(constraints, val) constraints = ConstraintSet() blocknumber = constraints.new_bitvec(256, name='blocknumber') constraints.add(blocknumber == 0) timestamp = constraints.new_bitvec(256, name='timestamp') constraints.add(timestamp == 1) difficulty = constraints.new_bitvec(256, name='difficulty') constraints.add(difficulty == 256) coinbase = constraints.new_bitvec(256, name='coinbase') constraints.add( coinbase == 244687034288125203496486448490407391986876152250) gaslimit = constraints.new_bitvec(256, name='gaslimit') constraints.add(gaslimit == 1000000) world = evm.EVMWorld(constraints, blocknumber=blocknumber, timestamp=timestamp, difficulty=difficulty, coinbase=coinbase, gaslimit=gaslimit) acc_addr = 0xf572e5295c57f15886f9b263e2f6d2d6c7b5ec6 acc_code = unhexlify('6000355415600957005b60203560003555') acc_balance = constraints.new_bitvec( 256, name='balance_0xf572e5295c57f15886f9b263e2f6d2d6c7b5ec6') constraints.add(acc_balance == 100000000000000000000000) acc_nonce = constraints.new_bitvec( 256, name='nonce_0xf572e5295c57f15886f9b263e2f6d2d6c7b5ec6') constraints.add(acc_nonce == 0) world.create_account(address=acc_addr, balance=acc_balance, code=acc_code, nonce=acc_nonce) address = 0xf572e5295c57f15886f9b263e2f6d2d6c7b5ec6 caller = 0xcd1722f3947def4cf144679da39c4c32bdc35681 price = constraints.new_bitvec(256, name='price') constraints.add(price == 100000000000000) value = constraints.new_bitvec(256, name='value') constraints.add(value == 1000000000000000000) gas = constraints.new_bitvec(256, name='gas') constraints.add(gas == 100000) data = constraints.new_array(index_max=64) constraints.add( data == b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfa\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfa' ) # open a fake tx, no funds send world._open_transaction('CALL', address, price, data, caller, value, gas=gas) # This variable might seem redundant in some tests - don't forget it is auto generated # and there are cases in which we need it ;) result, returndata = self._test_run(world) # World sanity checks - those should not change, right? self.assertEqual(solve(world.block_number()), 0) self.assertEqual(solve(world.block_gaslimit()), 1000000) self.assertEqual(solve(world.block_timestamp()), 1) self.assertEqual(solve(world.block_difficulty()), 256) self.assertEqual(solve(world.block_coinbase()), 0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba) # Add post checks for account 0xf572e5295c57f15886f9b263e2f6d2d6c7b5ec6 # check nonce, balance, code self.assertEqual( solve(world.get_nonce(0xf572e5295c57f15886f9b263e2f6d2d6c7b5ec6)), 0) self.assertEqual( solve( world.get_balance(0xf572e5295c57f15886f9b263e2f6d2d6c7b5ec6)), 100000000000000000000000) self.assertEqual( world.get_code(0xf572e5295c57f15886f9b263e2f6d2d6c7b5ec6), unhexlify('6000355415600957005b60203560003555')) # check storage self.assertEqual( solve( world.get_storage_data( 0xf572e5295c57f15886f9b263e2f6d2d6c7b5ec6, 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa )), 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa) # check outs self.assertEqual(returndata, unhexlify('')) # check logs logs = [ Log(unhexlify('{:040x}'.format(l.address)), l.topics, solve(l.memlog)) for l in world.logs ] data = rlp.encode(logs) self.assertEqual( sha3.keccak_256(data).hexdigest(), '1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347') # test used gas self.assertEqual(solve(world.current_vm.gas), 79915)