# Input vars X_short = BitVec('X', type_bits) Y_short = BitVec('Y', type_bits) # Z3's overflow and underflow conditions actual_overflow = Not(BVSubNoOverflow(X_short, Y_short)) actual_underflow = Not(BVSubNoUnderflow(X_short, Y_short, True)) # cast to full n_bits values X = BVSignedUpCast(X_short, n_bits) Y = BVSignedUpCast(Y_short, n_bits) diff = SUB(X, Y) # Constants maxValue = BVSignedMax(type_bits, n_bits) minValue = BVSignedMin(type_bits, n_bits) # Overflow and underflow checks in YulUtilFunction::overflowCheckedIntSubFunction if type_bits == 256: underflow_check = AND(ISZERO(SLT(Y, 0)), SGT(diff, X)) overflow_check = AND(SLT(Y, 0), SLT(diff, X)) else: underflow_check = SLT(diff, minValue) overflow_check = SGT(diff, maxValue) type_bits += 8 rule.check(actual_underflow, underflow_check != 0) rule.check(actual_overflow, overflow_check != 0)
""" # Approximation with 16-bit base types. n_bits = 16 type_bits = 8 while type_bits <= n_bits: rule = Rule() # Input vars X_short = BitVec('X', type_bits) Y_short = BitVec('Y', type_bits) # Z3's overflow condition actual_overflow = Not(BVMulNoOverflow(X_short, Y_short, False)) # cast to full n_bits values X = BVUnsignedUpCast(X_short, n_bits) Y = BVUnsignedUpCast(Y_short, n_bits) # Constants maxValue = BVUnsignedMax(type_bits, n_bits) # Overflow check in YulUtilFunction::overflowCheckedIntMulFunction overflow_check = AND(ISZERO(ISZERO(X)), GT(Y, DIV(maxValue, X))) rule.check(overflow_check != 0, actual_overflow) type_bits *= 2
from opcodes import SIGNEXTEND, AND from rule import Rule from z3 import BitVec, BitVecVal, ULT """ Rule: AND(A, SIGNEXTEND(B, X)) -> AND(A, X) given B < WordSize / 8 - 1 AND A & (1 << ((B + 1) * 8) - 1) == A """ n_bits = 128 # Input vars X = BitVec('X', n_bits) A = BitVec('A', n_bits) B = BitVec('B', n_bits) rule = Rule() # Requirements rule.require(ULT(B, BitVecVal(n_bits // 8 - 1, n_bits))) rule.require((A & ((BitVecVal(1, n_bits) << ((B + 1) * 8)) - 1)) == A) rule.check(AND(A, SIGNEXTEND(B, X)), AND(A, X))
from opcodes import AND from rule import Rule from z3 import BitVec, BitVecVal """ Rule: AND(AND(X, Y), Y) -> AND(X, Y) AND(Y, AND(X, Y)) -> AND(X, Y) AND(AND(Y, X), Y) -> AND(Y, X) AND(Y, AND(Y, X)) -> AND(Y, X) Requirements: """ rule = Rule() n_bits = 256 # Input vars X = BitVec('X', n_bits) Y = BitVec('Y', n_bits) # Constants BitWidth = BitVecVal(n_bits, n_bits) # Requirements # Non optimized result nonopt_1 = AND(AND(X, Y), Y) nonopt_2 = AND(Y, AND(X, Y)) nonopt_3 = AND(AND(Y, X), Y) nonopt_4 = AND(Y, AND(Y, X))
from opcodes import AND, ISZERO, MOD, SUB from rule import Rule from util import BVUnsignedMax from z3 import BitVec, BitVecVal, If """ Checking conversion of exp(-1, X) to sub(isZero(and(X, 1)), and(X, 1)) """ rule = Rule() n_bits = 256 X = BitVec('X', n_bits) exp_neg_one = If( MOD(X, 2) == 0, BitVecVal(1, n_bits), BVUnsignedMax(n_bits, n_bits)) rule.check(SUB(ISZERO(AND(X, 1)), AND(X, 1)), exp_neg_one)
SHL(B, AND(A, X)) -> AND(SHL(B, X), A << B) Requirements: B < BitWidth """ rule = Rule() n_bits = 128 # Input vars X = BitVec('X', n_bits) A = BitVec('A', n_bits) B = BitVec('B', n_bits) # Constants BitWidth = BitVecVal(n_bits, n_bits) # Requirements rule.require(ULT(B, BitWidth)) # Non optimized result nonopt_1 = SHL(B, AND(X, A)) nonopt_2 = SHL(B, AND(A, X)) # Optimized result Mask = SHL(B, A) opt = AND(SHL(B, X), Mask) rule.check(nonopt_1, opt) rule.check(nonopt_2, opt)
n_bits = 64 # Input vars X = BitVec('X', n_bits) A = BitVec('A', n_bits) B = BitVec('B', n_bits) # Constants BitWidth = BitVecVal(n_bits, n_bits) # Requirements rule.require(ULT(A, BitWidth)) rule.require(ULT(B, BitWidth)) # Non optimized result nonopt = SHR(B, SHL(A, X)) # Optimized result Mask = SHR(B, SHL(A, Int2BV(IntVal(-1), n_bits))) opt = If( UGT(A, B), AND(SHL(A - B, X), Mask), If( UGT(B, A), AND(SHR(B - A, X), Mask), AND(X, Mask) ) ) rule.check(nonopt, opt)
X_short = BitVec('X', type_bits) Y_short = BitVec('Y', type_bits) # Z3's overflow and underflow conditions actual_overflow = Not(BVMulNoOverflow(X_short, Y_short, True)) actual_underflow = Not(BVMulNoUnderflow(X_short, Y_short)) # cast to full n_bits values X = BVSignedUpCast(X_short, n_bits) Y = BVSignedUpCast(Y_short, n_bits) # Constants maxValue = BVSignedMax(type_bits, n_bits) minValue = BVSignedMin(type_bits, n_bits) # Overflow and underflow checks in YulUtilFunction::overflowCheckedIntMulFunction overflow_check_1 = AND(AND(SGT(X, 0), SGT(Y, 0)), GT(X, DIV(maxValue, Y))) underflow_check_1 = AND(AND(SGT(X, 0), SLT(Y, 0)), SLT(Y, SDIV(minValue, X))) underflow_check_2 = AND(AND(SLT(X, 0), SGT(Y, 0)), SLT(X, SDIV(minValue, Y))) overflow_check_2 = AND(AND(SLT(X, 0), SLT(Y, 0)), SLT(X, SDIV(maxValue, Y))) rule.check(actual_overflow, Or(overflow_check_1 != 0, overflow_check_2 != 0)) rule.check(actual_underflow, Or(underflow_check_1 != 0, underflow_check_2 != 0)) type_bits *= 2
# Input vars X_short = BitVec('X', type_bits) Y_short = BitVec('Y', type_bits) # Z3's overflow and underflow conditions actual_overflow = Not(BVAddNoOverflow(X_short, Y_short, True)) actual_underflow = Not(BVAddNoUnderflow(X_short, Y_short)) # cast to full n_bits values X = BVSignedUpCast(X_short, n_bits) Y = BVSignedUpCast(Y_short, n_bits) sum_ = ADD(X, Y) # Constants maxValue = BVSignedMax(type_bits, n_bits) minValue = BVSignedMin(type_bits, n_bits) # Overflow and underflow checks in YulUtilFunction::overflowCheckedIntAddFunction if type_bits == 256: overflow_check = AND(ISZERO(SLT(X, 0)), SLT(sum_, Y)) underflow_check = AND(SLT(X, 0), ISZERO(SLT(sum_, Y))) else: overflow_check = SGT(sum_, maxValue) underflow_check = SLT(sum_, minValue) type_bits += 8 rule.check(actual_overflow, overflow_check != 0) rule.check(actual_underflow, underflow_check != 0)
type_bits = 8 while type_bits <= n_bits: rule = Rule() # Input vars X_short = BitVec('X', type_bits) Y_short = BitVec('Y', type_bits) # Z3's overflow and underflow conditions actual_overflow = Not(BVAddNoOverflow(X_short, Y_short, True)) actual_underflow = Not(BVAddNoUnderflow(X_short, Y_short)) # cast to full n_bits values X = BVSignedUpCast(X_short, n_bits) Y = BVSignedUpCast(Y_short, n_bits) # Constants maxValue = BVSignedMax(type_bits, n_bits) minValue = BVSignedMin(type_bits, n_bits) # Overflow and underflow checks in YulUtilFunction::overflowCheckedIntAddFunction overflow_check = AND(ISZERO(SLT(X, 0)), SGT(Y, SUB(maxValue, X))) underflow_check = AND(SLT(X, 0), SLT(Y, SUB(minValue, X))) rule.check(actual_overflow, overflow_check != 0) rule.check(actual_underflow, underflow_check != 0) type_bits *= 2
Overflow checked signed integer division. """ n_bits = 256 type_bits = 8 while type_bits <= n_bits: rule = Rule() # Input vars X_short = BitVec('X', type_bits) Y_short = BitVec('Y', type_bits) # Z3's overflow conditions actual_overflow = Not(BVSDivNoOverflow(X_short, Y_short)) # cast to full n_bits values X = BVSignedUpCast(X_short, n_bits) Y = BVSignedUpCast(Y_short, n_bits) # Constants minValue = BVSignedMin(type_bits, n_bits) # Overflow check in YulUtilFunction::overflowCheckedIntDivFunction overflow_check = AND(EQ(X, minValue), EQ(Y, SUB(0, 1))) rule.check(actual_overflow, overflow_check != 0) type_bits *= 2
from opcodes import AND, OR from rule import Rule from z3 import BitVec """ Rule: AND(OR(AND(X, A), Y), B) -> OR(AND(X, A & B), AND(Y, B)) """ rule = Rule() # bit width is irrelevant n_bits = 128 # Input vars X = BitVec('X', n_bits) Y = BitVec('Y', n_bits) A = BitVec('A', n_bits) B = BitVec('B', n_bits) # Non optimized result, explicit form nonopt = AND(OR(AND(X, A), Y), B) # Optimized result opt = OR(AND(X, A & B), AND(Y, B)) rule.check(nonopt, opt) # Now the forms as they are constructod in the code. for inner in [AND(X, A), AND(A, X)]: for second in [OR(inner, Y), OR(Y, inner)]: rule.check(AND(second, B), opt)
type_bits = 8 while type_bits <= n_bits: rule = Rule() # Input vars X_short = BitVec('X', type_bits) Y_short = BitVec('Y', type_bits) # Z3's overflow and underflow conditions actual_overflow = Not(BVSubNoOverflow(X_short, Y_short)) actual_underflow = Not(BVSubNoUnderflow(X_short, Y_short, True)) # cast to full n_bits values X = BVSignedUpCast(X_short, n_bits) Y = BVSignedUpCast(Y_short, n_bits) # Constants maxValue = BVSignedMax(type_bits, n_bits) minValue = BVSignedMin(type_bits, n_bits) # Overflow and underflow checks in YulUtilFunction::overflowCheckedIntSubFunction underflow_check = AND(ISZERO(SLT(Y, 0)), SLT(X, ADD(minValue, Y))) overflow_check = AND(SLT(Y, 0), SGT(X, ADD(maxValue, Y))) rule.check(actual_underflow, underflow_check != 0) rule.check(actual_overflow, overflow_check != 0) type_bits *= 2
# Input vars X_short = BitVec('X', type_bits) Y_short = BitVec('Y', type_bits) # Z3's overflow and underflow conditions actual_overflow = Not(BVMulNoOverflow(X_short, Y_short, True)) actual_underflow = Not(BVMulNoUnderflow(X_short, Y_short)) # cast to full n_bits values X = BVSignedUpCast(X_short, n_bits) Y = BVSignedUpCast(Y_short, n_bits) product_raw = MUL(X, Y) #remove any overflown bits product = BVSignedCleanupFunction(product_raw, type_bits) # Constants min_value = BVSignedMin(type_bits, n_bits) # Overflow and underflow checks in YulUtilFunction::overflowCheckedIntMulFunction if type_bits > n_bits / 2: sol_overflow_check_1 = ISZERO(OR(ISZERO(X), EQ(Y, SDIV(product, X)))) if type_bits == n_bits: sol_overflow_check_2 = AND(SLT(X, 0), EQ(Y, min_value)) sol_overflow_check = Or(sol_overflow_check_1 != 0, sol_overflow_check_2 != 0) else: sol_overflow_check = (sol_overflow_check_1 != 0) else: sol_overflow_check = (ISZERO(EQ(product, product_raw)) != 0) rule.check(Or(actual_overflow, actual_underflow), sol_overflow_check)
Overflow checked unsigned integer multiplication. """ n_bits = 256 # Check that YulUtilFunction::cleanupFunction cleanup matches BVUnsignedCleanupFunction for type_bits in range(8, 256, 8): rule = Rule() # Input vars X = BitVec('X', n_bits) mask = BitVecVal((1 << type_bits) - 1, n_bits) cleaned_reference = BVUnsignedCleanupFunction(X, type_bits) cleaned = AND(X, mask) rule.check(cleaned, cleaned_reference) # Check that BVUnsignedCleanupFunction properly cleans up values. for type_bits in range(8, 256, 8): rule = Rule() # Input vars X_short = BitVec('X', type_bits) dirt = BitVec('dirt', n_bits - type_bits) X = BVUnsignedUpCast(X_short, n_bits) X_dirty = Concat(dirt, X_short) X_cleaned = BVUnsignedCleanupFunction(X_dirty, type_bits)