VYPR
High severityNVD Advisory· Published Apr 24, 2023· Updated Feb 12, 2025

Vyper's raw_call with outsize=0 and revert_on_failure=False returns incorrect success value

CVE-2023-30629

Description

Vyper is a Pythonic Smart Contract Language for the ethereum virtual machine. In versions 0.3.1 through 0.3.7, the Vyper compiler generates the wrong bytecode. 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. A patch is available and, as of time of publication, anticipated to be part of Vyper 0.3.8. As a workaround, one may always put max_outsize>0.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Vyper 0.3.1–0.3.7 contracts using raw_call with revert_on_failure=False and max_outsize=0 may return incorrect boolean due to bytecode bug.

In Vyper versions 0.3.1 through 0.3.7, a bytecode generation flaw in the compiler causes the built-in raw_call function to return an incorrect boolean value when invoked with revert_on_failure=False and max_outsize=0 [1][2]. The return value depends on uninitialized memory, making it unpredictable.

To exploit, an attacker would need to interact with a contract that uses raw_call in this specific configuration. No special privileges are required, as the bug occurs during normal operation. However, the attacker would need to understand the specific conditions under which the memory garbage yields True or False [4].

This can lead to logical errors in smart contracts that rely on the return value of raw_call for control flow. For example, a contract might incorrectly assume a call succeeded when it failed, or vice versa, potentially leading to loss of funds or unauthorized state changes [2][4].

A patch is available in Vyper 0.3.8, and users are advised to upgrade [2]. As a workaround, setting max_outsize>0 avoids the bug [1]. The Lido protocol identified this issue in their gate-seals contract and applied a fix [4].

AI Insight generated on May 20, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
vyperPyPI
>= 0.3.1, < 0.3.80.3.8

Affected products

1

Patches

1
851f7a1b3aa2

Merge pull request from GHSA-w9g2-3w7p-72g9

https://github.com/vyperlang/vyperCharles CooperApr 24, 2023via ghsa
2 files changed · +33 7
  • tests/parser/functions/test_raw_call.py+18 0 modified
    @@ -296,8 +296,10 @@ def test_checkable_raw_call(get_contract, assert_tx_failed):
     def fail1(should_raise: bool):
         if should_raise:
             raise "fail"
    +
     # test both paths for raw_call -
     # they are different depending if callee has or doesn't have returntype
    +# (fail2 fails because of staticcall)
     @external
     def fail2(should_raise: bool) -> int128:
         if should_raise:
    @@ -320,6 +322,7 @@ def foo(_addr: address, should_raise: bool) -> uint256:
         )
         assert success == (not should_raise)
         return 1
    +
     @external
     @view
     def bar(_addr: address, should_raise: bool) -> uint256:
    @@ -334,6 +337,19 @@ def bar(_addr: address, should_raise: bool) -> uint256:
         )
         assert success == (not should_raise)
         return 2
    +
    +# test max_outsize not set case
    +@external
    +@nonpayable
    +def baz(_addr: address, should_raise: bool) -> uint256:
    +    success: bool = True
    +    success = raw_call(
    +        _addr,
    +        _abi_encode(should_raise, method_id=method_id("fail1(bool)")),
    +        revert_on_failure=False,
    +    )
    +    assert success == (not should_raise)
    +    return 3
         """
     
         target = get_contract(target_source)
    @@ -343,6 +359,8 @@ def bar(_addr: address, should_raise: bool) -> uint256:
         assert caller.foo(target.address, False) == 1
         assert caller.bar(target.address, True) == 2
         assert caller.bar(target.address, False) == 2
    +    assert caller.baz(target.address, True) == 3
    +    assert caller.baz(target.address, False) == 3
     
     
     uncompilable_code = [
    
  • vyper/builtins/functions.py+15 7 modified
    @@ -1188,7 +1188,9 @@ def build_IR(self, expr, args, kwargs, context):
     
                 if revert_on_failure:
                     typ = bytes_ty
    +                # check the call success flag, and store returndata in memory
                     ret_ir = ["seq", check_external_call(call_ir), store_output_size]
    +                return IRnode.from_list(ret_ir, typ=typ, location=MEMORY)
                 else:
                     typ = TupleT([bool_ty, bytes_ty])
                     ret_ir = [
    @@ -1198,16 +1200,22 @@ def build_IR(self, expr, args, kwargs, context):
                         IRnode.from_list(call_ir, typ=bool_ty),
                         IRnode.from_list(store_output_size, typ=bytes_ty, location=MEMORY),
                     ]
    +                # return an IR tuple of call success flag and returndata pointer
    +                return IRnode.from_list(ret_ir, typ=typ)
    +
    +        # max_outsize is 0.
    +
    +        if not revert_on_failure:
    +            # return call flag as stack item
    +            typ = bool_ty
    +            return IRnode.from_list(call_ir, typ=typ)
     
             else:
    -            if revert_on_failure:
    -                typ = None
    -                ret_ir = check_external_call(call_ir)
    -            else:
    -                typ = bool_ty
    -                ret_ir = call_ir
    +            # check the call success flag and don't return anything
    +            ret_ir = check_external_call(call_ir)
    +            return IRnode.from_list(ret_ir, typ=None)
     
    -        return IRnode.from_list(ret_ir, typ=typ, location=MEMORY)
    +        raise CompilerPanic("unreachable!")
     
     
     class Send(BuiltinFunction):
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

8

News mentions

0

No linked articles in our index yet.