ghsa-w9g2-3w7p-72g9
Vulnerability from github
Published
2023-04-24 22:33
Modified
2023-04-25 16:34
Summary
Incorrect success value returned in vyper
Details

Background

During the audit of Lido's Gate Seals code statemind team identified a weird behavior of the code that uses raw_call: https://github.com/lidofinance/gate-seals/blob/051593e74df01a4131c485b4fda52e691cd4b7d8/contracts/GateSeal.vy#L164 .

Construction like this: vyper success = raw_call( sealable, _abi_encode(SEAL_DURATION_SECONDS, method_id=method_id("pauseFor(uint256)")), revert_on_failure=False ) was not fully documented: https://docs.vyperlang.org/en/v0.3.7/built-in-functions.html#raw_call .

The documentation says that: if max_outsize=0 it should return nothing and then it says that if revert_on_failure=False it should return a success flag in the tuple of response, but what if max_outsize=0 and revert_on_failure=False.

image

So the team started researching what exactly happened in that case, after some research we found that the Vyper compiler generates the wrong bytecode in that case, it generates the sequence: CALL // call MLOAD // MLOAD is wrong since the CALL result is already stored in the stack

Impact

Example of buggy code: vyper @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.

Patches

Fix by @charles-cooper https://github.com/vyperlang/vyper/commit/851f7a1b3aa2a36fd041e3d0ed38f9355a58c8ae

Workarounds

The simple workaround is always to put max_outsize>0. Workaround example https://github.com/lidofinance/gate-seals/pull/5/files

References

Lido's fix: https://github.com/lidofinance/gate-seals/pull/5/files

Show details on source website


{
  "affected": [
    {
      "ecosystem_specific": {
        "affected_functions": [
          "vyper.builtins.functions.RawCall.build_IR"
        ]
      },
      "package": {
        "ecosystem": "PyPI",
        "name": "vyper"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0.3.1"
            },
            {
              "fixed": "0.3.8"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2023-30629"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-670"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2023-04-24T22:33:52Z",
    "nvd_published_at": "2023-04-24T22:15:10Z",
    "severity": "HIGH"
  },
  "details": "### Background\nDuring the audit of [Lido\u0027s Gate Seals](https://github.com/lidofinance/gate-seals) code [statemind](https://statemind.io) team identified a weird behavior of the code that uses `raw_call`: https://github.com/lidofinance/gate-seals/blob/051593e74df01a4131c485b4fda52e691cd4b7d8/contracts/GateSeal.vy#L164 .\n\nConstruction like this:\n```vyper\nsuccess = raw_call(\n    sealable,\n    _abi_encode(SEAL_DURATION_SECONDS, method_id=method_id(\"pauseFor(uint256)\")),\n    revert_on_failure=False\n)\n```\nwas not fully documented: https://docs.vyperlang.org/en/v0.3.7/built-in-functions.html#raw_call .\n\nThe documentation says that: if `max_outsize=0` it should return nothing and then it says that if `revert_on_failure=False` it should return a `success` flag in the tuple of response, but what if `max_outsize=0`  and `revert_on_failure=False`.\n\n\u003cimg width=\"715\" alt=\"image\" src=\"https://user-images.githubusercontent.com/22330612/232125364-d2b3bbac-0b4f-40cb-80ff-f55d8eafef44.png\"\u003e\n\n So the team started researching what exactly happened in that case, after some research we found that the Vyper compiler generates the wrong bytecode in that case, it generates the sequence:\n```\nCALL // call\nMLOAD // MLOAD is wrong since the CALL result is already stored in the stack\n```\n\n### Impact\nExample of buggy code:\n```vyper\n@external\ndef returnSome(calling: address, a: uint256) -\u003e bool:\n    success: bool = false\n    success = raw_call(\n        calling,\n        _abi_encode(a, method_id=method_id(\"a(uint256)\")),\n        revert_on_failure=False\n        )\n```\n\nany 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`.\n\n### Patches\nFix by @charles-cooper https://github.com/vyperlang/vyper/commit/851f7a1b3aa2a36fd041e3d0ed38f9355a58c8ae\n\n### Workarounds\nThe simple workaround is always to put  `max_outsize\u003e0`.\nWorkaround example https://github.com/lidofinance/gate-seals/pull/5/files\n\n### References\nLido\u0027s fix: https://github.com/lidofinance/gate-seals/pull/5/files\n",
  "id": "GHSA-w9g2-3w7p-72g9",
  "modified": "2023-04-25T16:34:41Z",
  "published": "2023-04-24T22:33:52Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/vyperlang/vyper/security/advisories/GHSA-w9g2-3w7p-72g9"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2023-30629"
    },
    {
      "type": "WEB",
      "url": "https://github.com/lidofinance/gate-seals/pull/5/files"
    },
    {
      "type": "WEB",
      "url": "https://github.com/vyperlang/vyper/commit/851f7a1b3aa2a36fd041e3d0ed38f9355a58c8ae"
    },
    {
      "type": "WEB",
      "url": "https://docs.vyperlang.org/en/v0.3.7/built-in-functions.html#raw_call"
    },
    {
      "type": "WEB",
      "url": "https://github.com/lidofinance/gate-seals/blob/051593e74df01a4131c485b4fda52e691cd4b7d8/contracts/GateSeal.vy#L164"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/vyperlang/vyper"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "Incorrect success value returned in vyper"
}


Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading...

Loading...

Loading...
  • Seen: The vulnerability was mentioned, discussed, or seen somewhere by the user.
  • Confirmed: The vulnerability is confirmed from an analyst perspective.
  • Exploited: This vulnerability was exploited and seen by the user reporting the sighting.
  • Patched: This vulnerability was successfully patched by the user reporting the sighting.
  • Not exploited: This vulnerability was not exploited or seen by the user reporting the sighting.
  • Not confirmed: The user expresses doubt about the veracity of the vulnerability.
  • Not patched: This vulnerability was not successfully patched by the user reporting the sighting.