Repository: vyperlang/vyper

Issue:
Negative constants can be used in some places where `uint256` is required
The following code casts `int128` literals to `uint256` without any check on the value:

https://github.com/ethereum/vyper/blob/c296b2d7532d913103aad494b749f8179a3acddc/vyper/parser/parser_utils.py#L415-L417

This permits the following (invalid) code to compile without error:

```python
Test: event({ n: uint256 })
x: map(uint256, uint256)

@public
def test():
    a: uint256 = self.x[-7]
    log.Test(-7)
```


Discussion:
Check for literal value matching destination type

Resolving patch:
diff --git a/vyper/parser/parser_utils.py b/vyper/parser/parser_utils.py
--- a/vyper/parser/parser_utils.py
+++ b/vyper/parser/parser_utils.py
@@ -418,14 +418,20 @@ def base_type_conversion(orig, frm, to, pos, in_function_call=False):
         is_base_type(frm, 'int128') and is_base_type(to, 'decimal')
     ) and are_units_compatible(frm, to)
 
-    if getattr(frm, 'is_literal', False) and frm.typ in ('int128', 'uint256'):
-        if not SizeLimits.in_bounds(frm.typ, orig.value):
-            raise InvalidLiteralException("Number out of range: " + str(orig.value), pos)
-        # Special Case: Literals in function calls should always convey unit type as well.
-        if in_function_call and not (frm.unit == to.unit and frm.positional == to.positional):
-            raise InvalidLiteralException(
-                f"Function calls require explicit unit definitions on calls, expected {to}", pos
-            )
+    if getattr(frm, 'is_literal', False):
+        if frm.typ in ('int128', 'uint256'):
+            if not SizeLimits.in_bounds(frm.typ, orig.value):
+                raise InvalidLiteralException(f"Number out of range: {orig.value}", pos)
+            # Special Case: Literals in function calls should always convey unit type as well.
+            if in_function_call and not (frm.unit == to.unit and frm.positional == to.positional):
+                raise InvalidLiteralException(
+                    f"Function calls require explicit unit definitions on calls, expected {to}",
+                    pos
+                )
+        if to.typ in ('int128', 'uint256'):
+            if not SizeLimits.in_bounds(to.typ, orig.value):
+                raise InvalidLiteralException(f"Number out of range: {orig.value}", pos)
+
     if not isinstance(frm, (BaseType, NullType)) or not isinstance(to, BaseType):
         raise TypeMismatchException(
             f"Base type conversion from or to non-base type: {frm} {to}", pos
@@ -564,14 +570,6 @@ def make_setter(left, right, location, pos, in_function_call=False):
             pos,
             in_function_call=in_function_call,
         )
-        # TODO this overlaps a type check in parser.stmt.Stmt._check_valid_assign
-        # and should be examined during a refactor (@iamdefinitelyahuman)
-        if 'int' in left.typ.typ and isinstance(right.value, int):
-            if not SizeLimits.in_bounds(left.typ.typ, right.value):
-                raise InvalidLiteralException(
-                    f"Number out of range for {left.typ}: {right.value}",
-                    pos
-                )
         if location == 'storage':
             return LLLnode.from_list(['sstore', left, right], typ=None)
         elif location == 'memory':


Test patch:
diff --git a/tests/parser/functions/test_default_parameters.py b/tests/parser/functions/test_default_parameters.py
--- a/tests/parser/functions/test_default_parameters.py
+++ b/tests/parser/functions/test_default_parameters.py
@@ -262,7 +262,7 @@ def foo(a: uint256 = -1): pass
 # value out of range for int128
 @public
 def foo(a: int128 = 170141183460469231731687303715884105728): pass
-    """, TypeMismatchException),
+    """, InvalidLiteralException),
     ("""
 # value out of range for uint256 array
 @public
diff --git a/tests/parser/syntax/test_invalids.py b/tests/parser/syntax/test_invalids.py
--- a/tests/parser/syntax/test_invalids.py
+++ b/tests/parser/syntax/test_invalids.py
@@ -57,7 +57,7 @@ def foo():
 def foo():
     x: int128 = 5
     x = 0x1234567890123456789012345678901234567890
-""", TypeMismatchException)
+""", InvalidLiteralException)
 
 must_fail("""
 @public
@@ -115,6 +115,13 @@ def foo():
     x: int128 = self.b[5]
 """)
 
+must_fail("""
+b: map(uint256, uint256)
+@public
+def foo():
+    x: int128 = self.b[-5]
+""", InvalidLiteralException)
+
 must_fail("""
 b: map(int128, int128)
 @public
diff --git a/tests/parser/syntax/test_logging.py b/tests/parser/syntax/test_logging.py
--- a/tests/parser/syntax/test_logging.py
+++ b/tests/parser/syntax/test_logging.py
@@ -7,6 +7,7 @@
     compiler,
 )
 from vyper.exceptions import (
+    InvalidLiteralException,
     TypeMismatchException,
 )
 
@@ -33,7 +34,14 @@ def foo():
 @public
 def logSale(amount: uint256):
    log.Sale(amount)
-   """
+   """,
+    ("""
+Test: event({ n: uint256 })
+
+@public
+def test():
+    log.Test(-7)
+   """, InvalidLiteralException),
 ]