mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Proofs for the overflow and underflow conditions in checked arithmetic for Sol->Yul code generation.
This commit is contained in:
parent
9bb7160c4c
commit
c71fb76bb2
39
test/formal/checked_int_add.py
Normal file
39
test/formal/checked_int_add.py
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
from rule import Rule
|
||||||
|
from opcodes import *
|
||||||
|
from util import *
|
||||||
|
|
||||||
|
"""
|
||||||
|
Overflow checked signed integer addition.
|
||||||
|
"""
|
||||||
|
|
||||||
|
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 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
|
35
test/formal/checked_int_div.py
Normal file
35
test/formal/checked_int_div.py
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
from rule import Rule
|
||||||
|
from opcodes import *
|
||||||
|
from util import *
|
||||||
|
|
||||||
|
"""
|
||||||
|
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
|
42
test/formal/checked_int_mul_16.py
Normal file
42
test/formal/checked_int_mul_16.py
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
from rule import Rule
|
||||||
|
from opcodes import *
|
||||||
|
from util import *
|
||||||
|
|
||||||
|
"""
|
||||||
|
Overflow checked signed integer multiplication.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# 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 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
|
39
test/formal/checked_int_sub.py
Normal file
39
test/formal/checked_int_sub.py
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
from rule import Rule
|
||||||
|
from opcodes import *
|
||||||
|
from util import *
|
||||||
|
|
||||||
|
"""
|
||||||
|
Overflow checked signed integer subtraction.
|
||||||
|
"""
|
||||||
|
|
||||||
|
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 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
|
35
test/formal/checked_uint_add.py
Normal file
35
test/formal/checked_uint_add.py
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
from rule import Rule
|
||||||
|
from opcodes import *
|
||||||
|
from util import *
|
||||||
|
|
||||||
|
"""
|
||||||
|
Overflow checked unsigned integer addition.
|
||||||
|
"""
|
||||||
|
|
||||||
|
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 condition
|
||||||
|
actual_overflow = Not(BVAddNoOverflow(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::overflowCheckedIntAddFunction
|
||||||
|
overflow_check = GT(X, SUB(maxValue, Y))
|
||||||
|
|
||||||
|
rule.check(overflow_check != 0, actual_overflow)
|
||||||
|
|
||||||
|
type_bits *= 2
|
36
test/formal/checked_uint_mul_16.py
Normal file
36
test/formal/checked_uint_mul_16.py
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
from rule import Rule
|
||||||
|
from opcodes import *
|
||||||
|
from util import *
|
||||||
|
|
||||||
|
"""
|
||||||
|
Overflow checked unsigned integer multiplication.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# 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
|
35
test/formal/checked_uint_sub.py
Normal file
35
test/formal/checked_uint_sub.py
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
from rule import Rule
|
||||||
|
from opcodes import *
|
||||||
|
from util import *
|
||||||
|
|
||||||
|
"""
|
||||||
|
Overflow checked unsigned integer subtraction.
|
||||||
|
"""
|
||||||
|
|
||||||
|
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 condition
|
||||||
|
actual_overflow = Not(BVSubNoUnderflow(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::overflowCheckedIntSubFunction
|
||||||
|
overflow_check = LT(X, Y)
|
||||||
|
|
||||||
|
rule.check(overflow_check != 0, actual_overflow)
|
||||||
|
|
||||||
|
type_bits *= 2
|
@ -33,6 +33,9 @@ def SLT(x, y):
|
|||||||
def SGT(x, y):
|
def SGT(x, y):
|
||||||
return If(x > y, BitVecVal(1, x.size()), BitVecVal(0, x.size()))
|
return If(x > y, BitVecVal(1, x.size()), BitVecVal(0, x.size()))
|
||||||
|
|
||||||
|
def EQ(x, y):
|
||||||
|
return If(x == y, BitVecVal(1, x.size()), BitVecVal(0, x.size()))
|
||||||
|
|
||||||
def ISZERO(x):
|
def ISZERO(x):
|
||||||
return If(x == 0, BitVecVal(1, x.size()), BitVecVal(0, x.size()))
|
return If(x == 0, BitVecVal(1, x.size()), BitVecVal(0, x.size()))
|
||||||
|
|
||||||
|
27
test/formal/util.py
Normal file
27
test/formal/util.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
from z3 import *
|
||||||
|
|
||||||
|
def BVUnsignedUpCast(x, n_bits):
|
||||||
|
assert(x.size() <= n_bits)
|
||||||
|
if x.size() < n_bits:
|
||||||
|
return Concat(BitVecVal(0, n_bits - x.size()), x)
|
||||||
|
else:
|
||||||
|
return x
|
||||||
|
|
||||||
|
def BVUnsignedMax(type_bits, n_bits):
|
||||||
|
assert(type_bits <= n_bits)
|
||||||
|
return BitVecVal((1 << type_bits) - 1, n_bits)
|
||||||
|
|
||||||
|
def BVSignedUpCast(x, n_bits):
|
||||||
|
assert(x.size() <= n_bits)
|
||||||
|
if x.size() < n_bits:
|
||||||
|
return Concat(If(x < 0, BitVecVal(-1, n_bits - x.size()), BitVecVal(0, n_bits - x.size())), x)
|
||||||
|
else:
|
||||||
|
return x
|
||||||
|
|
||||||
|
def BVSignedMax(type_bits, n_bits):
|
||||||
|
assert(type_bits <= n_bits)
|
||||||
|
return BitVecVal((1 << (type_bits - 1)) - 1, n_bits)
|
||||||
|
|
||||||
|
def BVSignedMin(type_bits, n_bits):
|
||||||
|
assert(type_bits <= n_bits)
|
||||||
|
return BitVecVal(-(1 << (type_bits - 1)), n_bits)
|
Loading…
Reference in New Issue
Block a user