Advisories for Pypi/Vyper package

2024

vyper performs incorrect topic logging in raw_log

Incorrect values can be logged when raw_log builtin is called with memory or storage arguments to be used as topics. A contract search was performed and no vulnerable contracts were found in production. In particular, no uses of raw_log() were found at all in production; it is apparently not a well-known function.

vyper performs double eval of the slice args when buffer from adhoc locations

Using the slice builtin can result in a double eval vulnerability when the buffer argument is either msg.data, self.code or <address>.code and either the start or length arguments have side-effects. A contract search was performed and no vulnerable contracts were found in production. Having side-effects in the start and length patterns is also an unusual pattern which is not that likely to show up in user code. It is also …

vyper performs double eval of raw_args in create_from_blueprint

Using the create_from_blueprint builtin can result in a double eval vulnerability when raw_args=True and the args argument has side-effects. A contract search was performed and no vulnerable contracts were found in production. In particular, the raw_args variant of create_from_blueprint was not found to be used in production.

vyper default functions don't respect nonreentrancy keys

Prior to v0.3.0, default() functions did not respect the @nonreentrancy decorator and the lock was not emitted. This is a known bug and was already visible in the issue tracker (https://github.com/vyperlang/vyper/issues/2455), but it is being re-issued as an advisory so that tools relying on the advisory publication list can incorporate it into their searches. A contract search was additionally performed and no vulnerable contracts were found in production.

Vyper's `_abi_decode` vulnerable to Memory Overflow

Summary If an excessively large value is specified as the starting index for an array in _abi_decode, it can cause the read position to overflow. This results in the decoding of values outside the intended array bounds, potentially leading to bugs in contracts that use arrays within _abi_decode. The advisory has been assigned low severity, because it is only observable if there is a memory write between two invocations of …

Vyper negative array index bounds checks

Arrays can be keyed by a signed integer, while they are defined for unsigned integers only. The typechecker doesn't throw when spotting the usage of an int as an index for an array. Typically, negative integers are filtered out at runtime by the bounds checker, but small enough (i.e. large in magnitude, ex. -2255 + 5) quantities combined with large enough arrays (at least 2255 in length) can pass the …

Vyper sha3 codegen bug

There is an error in the stack management when compiling the IR for sha3_64. Concretely, the height variable is miscalculated. The vulnerability can't be triggered without writing the IR by hand. That is, it cannot be triggered from regular vyper code, it can only be triggered by using the fang binary directly (this binary used to be called vyper-ir prior to v0.3.4).

Vyper's external calls can overflow return data to return input buffer

When calls to external contracts are made, we write the input buffer starting at byte 28, and allocate the return buffer to start at byte 0 (overlapping with the input buffer). When checking RETURNDATASIZE for dynamic types, the size is compared only to the minimum allowed size for that type, and not to the returned value's length. As a result, malformed return data can cause the contract to mistake data …

Vyper's bounds check on built-in `slice()` function can be overflowed

The bounds check for slices does not account for the ability for start + length to overflow when the values aren't literals. If a slice() function uses a non-literal argument for the start or length variable, this creates the ability for an attacker to overflow the bounds check. This issue can be used to do OOB access to storage, memory or calldata addresses. It can also be used to corrupt …

Vyper's raw_call `value=` kwargs not disabled for static and delegate calls

Vyper compiler allows passing a value in builtin raw_call even if the call is a delegatecall or a staticcall. But in the context of delegatecall and staticcall the handling of value is not possible due to the semantics of the respective opcodes, and vyper will silently ignore the value= argument. A contract search was performed and no vulnerable contracts were found in production.

concat built-in can corrupt memory in vyper

concat built-in can write over the bounds of the memory buffer that was allocated for it and thus overwrite existing valid data. The root cause is that the build_IR for concat doesn't properly adhere to the API of copy functions (for >=0.3.2 the copy_bytes function). A contract search was performed and no vulnerable contracts were found in production. Tracked in issue https://github.com/vyperlang/vyper/issues/3737

2023

Vyper's `_abi_decode` input not validated in complex expressions

_abi_decode() does not validate input when it is nested in an expression. the following example gets correctly validated (bounds checked): x: int128 = _abi_decode(slice(msg.data, 4, 32), int128) however, the following example is not bounds checked @external def abi_decode(x: uint256) -> uint256: a: uint256 = convert(_abi_decode(slice(msg.data, 4, 32), (uint8)), uint256) + 1 return a # abi_decode(256) returns: 257 the issue can be triggered by constructing an example where the output of …

Vyper vulnerable to memory corruption in certain builtins utilizing `msize`

In certain conditions, the memory used by the builtins raw_call, create_from_blueprint and create_copy_of can be corrupted. For raw_call, the argument buffer of the call can be corrupted, leading to incorrect calldata in the sub-context. For create_from_blueprint and create_copy_of, the buffer for the to-be-deployed bytecode can be corrupted, leading to deploying incorrect bytecode. Below are the conditions that must be fulfilled for the corruption to happen for each builtin:

Vyper: reversed order of side effects for some operations

For the following (probably non-exhaustive) list of expressions, the compiler evaluates the arguments from right to left instead of left to right. - unsafe_add - unsafe_sub - unsafe_mul - unsafe_div - pow_mod256 - |, &, ^ (bitwise operators) - bitwise_or (deprecated) - bitwise_and (deprecated) - bitwise_xor (deprecated) - raw_call - <, >, <=, >=, ==, != - in, not in (when lhs and rhs are enums) This behaviour becomes a …

incorrect order of evaluation of side effects for some builtins

The order of evaluation of the arguments of the builtin functions uint256_addmod, uint256_mulmod, ecadd and ecmul does not follow source order. • For uint256_addmod(a,b,c) and uint256_mulmod(a,b,c), the order is c,a,b. • For ecadd(a,b) and ecmul(a,b), the order is b,a. Note that this behaviour is problematic when the evaluation of one of the arguments produces side effects that other arguments depend on.

Vyper has incorrectly allocated named re-entrancy locks

In versions 0.2.15, 0.2.16 and 0.3.0, named re-entrancy locks are allocated incorrectly. Each function using a named re-entrancy lock gets a unique lock regardless of the key, allowing cross-function re-entrancy in contracts compiled with the susceptible versions. A specific set of conditions is required to result in misbehavior of affected contracts, specifically: A .vy contract compiled with either of the following vyper versions: 0.2.15, 0.2.16, 0.3.0 A primary function that …

ecrecover can return undefined data if signature does not verify

the ecrecover precompile does not fill the output buffer if the signature does not verify, see https://github.com/ethereum/go-ethereum/blob/b058cf454b3bdc7e770e2b3cec83a0bcb48f55ee/core/vm/contracts.go#L188. however, the ecrecover builtin will still return whatever is at memory location 0. this means that the if the compiler has been convinced to write to the 0 memory location with specially crafted data (generally, this can happen with a hashmap access or immutable read) just before the ecrecover, a signature check might …

Vyper's nonpayable default functions are sometimes payable

in contracts with at least one regular nonpayable function, due to the callvalue check being inside of the selector section, it is possible to send funds to the default function by using less than 4 bytes of calldata, even if the default function is marked nonpayable. this applies to contracts compiled with vyper<=0.3.7.

Vyper vulnerable to OOB DynArray access when array is on both LHS and RHS of an assignment

during codegen, the length word of a dynarray is written before the data, which can result in OOB array access in the case where the dynarray is on both the lhs and rhs of an assignment. here is a minimal example producing the issue: a:DynArray[uint256,3] @external def test() -> DynArray[uint256,3]: self.a = [1,2,3] self.a = empty(DynArray[uint256,3]) self.a = [self.a[0],self.a[1],self.a[2]] return self.a # return [1,2,3] and here is an example demonstrating …

Vyper vulnerable to integer overflow in loop

Due to missing overflow check for loop variables, by assigning the iterator of a loop to a variable, it is possible to overflow the type of the latter. In the following example, calling test returns 354, meaning that the variable a did store 354 a value out of bound for the type uint8. @external def test() -> uint16: x:uint8 = 255 a:uint8 = 0 for i in range(x, x+100): a …

Vyper vulnerable to incorrect ordering of arguments for kwargs passed to internal calls

Internal calls to internal functions with more than 1 default argument are compiled incorrectly. Depending on the number of arguments provided in the call, the defaults are added not right-to-left, but left-to-right. If the types are incompatible, typechecking is bypassed. In the bar() function in the following code, self.foo(13) is compiled to self.foo(13,12) instead of self.foo(13,1337). @internal def foo(a:uint256 = 12, b:uint256 = 1337): pass @internal def bar(): self.foo(13) note …

vyper vulnerable to storage allocator overflow

The storage allocator does not guard against allocation overflows. This can result in vulnerabilities like the following: owner: public(address) take_up_some_space: public(uint256[10]) buffer: public(uint256[max_value(uint256)]) @external def initialize(): self.owner = msg.sender @external def foo(idx: uint256, data: uint256): self.buffer[idx] = data Per @toonvanhove, "An attacker can overwrite the owner variable by calling this contract with calldata: 0x04bc52f8 fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5 ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff (spaces inserted for readability) 0x04bc52f8 is the selector for foo(uint256, uint256), and the last …

Incorrect success value returned in vyper

Example of buggy code: @external def returnSome(calling: address, a: uint256) -> bool: success: bool = false success = raw_call( calling, _abi_encode(a, method_id=method_id("a(uint256)")), revert_on_failure=False ) any contract that uses the raw_call with revert_on_failure=False and max_outsize=0 receives the wrong response from raw_call. Depending on the memory garbage, the result can be either True or False.

2022

Multiple evaluation of contract address in call in vyper

Impact when a calling an external contract with no return value, the contract address could be evaluated twice. this is usually only an efficiency problem, but if evaluation of the contract address has side effects, it could result in double evaluation of the side effects. in the following example, Foo(msg.sender).bar() is the contract address for the following call (to .foo()), and could get evaluated twice interface Foo: def foo(): nonpayable …

Buffer Overflow in vyper

Importing a function from a JSON interface which returns bytes generates bytecode which does not clamp bytes length, potentially resulting in a buffer overrun.

Integer Overflow or Wraparound

Vyper is a pythonic Smart Contract Language for the ethereum virtual machine. In affected versions, the return of <iface>.returns_int128() is not validated to fall within the bounds of int128. This issue can result in a misinterpretation of the integer value and lead to incorrect behavior. As of v0.3.0, <iface>.returns_int128() is validated in simple expressions, but not complex expressions. Users are advised to upgrade. There is no known workaround for this …

Incorrect Comparison

Vyper is a Pythonic Smart Contract Language for the Ethereum Virtual Machine. In version 0.3.1 and prior, bytestrings can have dirty bytes in them, resulting in the word-for-word comparisons giving incorrect results. Even without dirty nonzero bytes, two bytestrings can compare to equal if one ends with "\x00" because there is no comparison of the length. A patch is available and expected to be part of the 0.3.2 release. There …

2021

Storage corruption due to variables overwritten by re-entrancy locks

Background When attempting to use the v0.2.14 release, @pandadefi discovered an issue using the @nonreentrant decorator. Impact Reentrancy protection storage slots get allocated to the same slots as storage variables, leading to the corruption of storage variables when using the @nonreentrant decorator. Patches This issue was fixed in v0.2.15 in #2391, #2379 Workarounds Don't use the @nonreentrant decorator in these versions.

VVE-2021-0002: Incorrect `returndatasize` when using simple forwarder proxies deployed prior to EIP-1167 adoption

Background @tjayrush reported a data handling issue with certain Web3 libraries using Vyper-deploy forwarder proxy contracts using our Vyper's built-in create_forwarder_to function prior to our change to support EIP-1167 style forwarder proxies. Impact If you are an end user of a forwarder-style proxy deployed using Vyper's built-in create_forwarder_to function AND you have a function that returns bytes AND you do no return data sanitation on the value returned, you could …

2020

Vyper interfaces returning integer types less than 256 bits can be manipulated if uint256 is used

VVE-2020-0001 Earlier today, we received a responsible disclosure of a potential issue from @montyly (security researcher at @trailofbits) for Vyper users who make assumptions about what values certain interface types can return. Impact We determined the issue to be mild and unlikely to be exploited, with an easy workaround while the correct resolution is in process. The issue stems from a number of things, which we will detail here. (1) …