Skip to content

Commit

Permalink
fix[codegen]: range bound check for signed integers (#3814)
Browse files Browse the repository at this point in the history
  • Loading branch information
DanielSchiavini authored Feb 28, 2024
1 parent 730679b commit b4429cf
Show file tree
Hide file tree
Showing 4 changed files with 25 additions and 4 deletions.
16 changes: 16 additions & 0 deletions tests/functional/codegen/features/iteration/test_for_range.py
Original file line number Diff line number Diff line change
Expand Up @@ -414,3 +414,19 @@ def foo(a: {typ}) -> {typ}:
assert c.foo(100) == 6
assert c.foo(1) == 666
assert c.foo(0) == 31337


def test_for_range_signed_int_overflow(get_contract, tx_failed):
code = """
@external
def foo() -> DynArray[int256, 10]:
res: DynArray[int256, 10] = empty(DynArray[int256, 10])
x:int256 = max_value(int256)
y:int256 = min_value(int256)+2
for i:int256 in range(x,y , bound=10):
res.append(i)
return res
"""
c = get_contract(code)
with tx_failed():
c.foo()
4 changes: 2 additions & 2 deletions vyper/builtins/_convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
bytes_data_ptr,
clamp,
clamp_basetype,
clamp_le,
get_bytearray_length,
int_clamp,
is_bytes_m_type,
Expand Down Expand Up @@ -110,8 +111,7 @@ def _clamp_numeric_convert(arg, arg_bounds, out_bounds, arg_is_signed):
# out_hi must be smaller than MAX_UINT256, so clample makes sense.
# add an assertion, just in case this assumption ever changes.
assert out_hi < 2**256 - 1, "bad assumption in numeric convert"
CLAMP_OP = "sle" if arg_is_signed else "le"
arg = clamp(CLAMP_OP, arg, out_hi)
arg = clamp_le(arg, out_hi, arg_is_signed)

return arg

Expand Down
5 changes: 5 additions & 0 deletions vyper/codegen/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -1226,6 +1226,11 @@ def clamp_nonzero(arg):
return IRnode.from_list(b1.resolve(ret), typ=arg.typ)


def clamp_le(arg, hi, signed):
LE = "sle" if signed else "le"
return clamp(LE, arg, hi)


def clamp2(lo, arg, hi, signed):
with IRnode.from_list(arg).cache_when_complex("clamp2_arg") as (b1, arg):
GE = "sge" if signed else "ge"
Expand Down
4 changes: 2 additions & 2 deletions vyper/codegen/stmt.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
IRnode,
append_dyn_array,
check_assign,
clamp,
clamp_le,
dummy_node_for_type,
get_dyn_array_count,
get_element_ptr,
Expand Down Expand Up @@ -255,7 +255,7 @@ def _parse_For_range(self):
with end.cache_when_complex("end") as (b1, end):
# note: the check for rounds<=rounds_bound happens in asm
# generation for `repeat`.
clamped_start = clamp("le", start, end)
clamped_start = clamp_le(start, end, target_type.is_signed)
rounds = b1.resolve(IRnode.from_list(["sub", end, clamped_start]))
rounds_bound = kwargs.pop("bound").int_value()
else:
Expand Down

0 comments on commit b4429cf

Please # to comment.