diff --git a/advisories/github-reviewed/2025/02/GHSA-2p94-8669-xg86/GHSA-2p94-8669-xg86.json b/advisories/github-reviewed/2025/02/GHSA-2p94-8669-xg86/GHSA-2p94-8669-xg86.json new file mode 100644 index 00000000000..d150abdb65e --- /dev/null +++ b/advisories/github-reviewed/2025/02/GHSA-2p94-8669-xg86/GHSA-2p94-8669-xg86.json @@ -0,0 +1,57 @@ +{ + "schema_version": "1.4.0", + "id": "GHSA-2p94-8669-xg86", + "modified": "2025-02-21T22:43:30Z", + "published": "2025-02-21T22:43:30Z", + "aliases": [ + "CVE-2025-26622" + ], + "summary": "Vyper's sqrt doesn't define rounding behavior", + "details": "Vyper's `sqrt()` builtin uses the babylonian method to calculate square roots of decimals. Unfortunately, improper handling of the oscillating final states may lead to sqrt incorrectly returning rounded up results.\n\nthe fix is tracked in https://github.com/vyperlang/vyper/pull/4486\n\n### Vulnerability Details\n\nVyper injects the following code to handle calculation of decimal sqrt. x is the input provided by user.\n```python\nassert x >= 0.0\nz: decimal = 0.0\n\nif x == 0.0:\n z = 0.0\nelse:\n z = x / 2.0 + 0.5\n y: decimal = x\n\n for i: uint256 in range(256):\n if z == y:\n break\n y = z\n z = (x / z + z) / 2.0\n```\nNotably, the terminal condition of the algorithm is either `z_cur == z_prev`, or the algorithm runs for 256 rounds.\n\nHowever, for certain inputs, `z` might actually oscillate between `N` and `N + epsilon`, where `N ** 2 <= x < (N + epsilon) ** 2`. This means that the current behavior does not define whether it will round up or down to the nearest epsilon.\n\nThe example snippet here returns 0.9999999999, the rounded up result for sqrt(0.9999999998). This is due to the oscillation ending in N + epsilon instead of N.\n```vyper\n@external\ndef test():\n d: decimal = 0.9999999998\n r: decimal = sqrt(d) #this will be 0.9999999999\n```\n\nNote that `sqrt()` diverges from `isqrt()` here -- `isqrt()` consistently rounds down, so it is not subject to the same issue.\n\n### Impact Details\n\nSince `sqrt()` can be used for determining boundary conditions, rounding down is preferred. However, since `sqrt()` is used very rarely in the wild, this advisory has been assigned an impact of `low`.", + "severity": [], + "affected": [ + { + "package": { + "ecosystem": "PyPI", + "name": "vyper" + }, + "ranges": [ + { + "type": "ECOSYSTEM", + "events": [ + { + "introduced": "0" + }, + { + "fixed": "0.4.1" + } + ] + } + ], + "database_specific": { + "last_known_affected_version_range": "<= 0.4.0" + } + } + ], + "references": [ + { + "type": "WEB", + "url": "https://github.com/vyperlang/vyper/security/advisories/GHSA-2p94-8669-xg86" + }, + { + "type": "WEB", + "url": "https://github.com/vyperlang/vyper/pull/4486" + }, + { + "type": "PACKAGE", + "url": "https://github.com/vyperlang/vyper" + } + ], + "database_specific": { + "cwe_ids": [], + "severity": "LOW", + "github_reviewed": true, + "github_reviewed_at": "2025-02-21T22:43:30Z", + "nvd_published_at": null + } +} \ No newline at end of file diff --git a/advisories/github-reviewed/2025/02/GHSA-4w26-8p97-f4jp/GHSA-4w26-8p97-f4jp.json b/advisories/github-reviewed/2025/02/GHSA-4w26-8p97-f4jp/GHSA-4w26-8p97-f4jp.json new file mode 100644 index 00000000000..42e2a534e00 --- /dev/null +++ b/advisories/github-reviewed/2025/02/GHSA-4w26-8p97-f4jp/GHSA-4w26-8p97-f4jp.json @@ -0,0 +1,53 @@ +{ + "schema_version": "1.4.0", + "id": "GHSA-4w26-8p97-f4jp", + "modified": "2025-02-21T22:43:33Z", + "published": "2025-02-21T22:43:33Z", + "aliases": [ + "CVE-2025-27105" + ], + "summary": "AugAssign evaluation order causing OOB write within the object in Vyper", + "details": "Vyper handles AugAssign statements by first caching the target location to avoid double evaluation. However, in the case when target is an access to a DynArray and the rhs modifies the array, the cached target will evaluate first, and the bounds check will not be re-evaluated during the write portion of the statement. In other words, the following code\n\n```vyper\ndef poc():\n a: DynArray[uint256, 2] = [1, 2]\n a[1] += a.pop()\n```\n\nis equivalent to:\n```vyper\ndef poc():\n a: DynArray[uint256, 2] = [1, 2]\n a[1] += a[len(a) - 1]\n a.pop()\n```\nrather than:\n```vyper\ndef poc():\n a: DynArray[uint256, 2] = [1, 2]\n s: uint256 = a[1]\n t: uint256 = a.pop()\n a[1] = s + t # reverts due to oob access\n```", + "severity": [], + "affected": [ + { + "package": { + "ecosystem": "PyPI", + "name": "vyper" + }, + "ranges": [ + { + "type": "ECOSYSTEM", + "events": [ + { + "introduced": "0" + }, + { + "fixed": "0.4.1" + } + ] + } + ], + "database_specific": { + "last_known_affected_version_range": "<= 0.4.0" + } + } + ], + "references": [ + { + "type": "WEB", + "url": "https://github.com/vyperlang/vyper/security/advisories/GHSA-4w26-8p97-f4jp" + }, + { + "type": "PACKAGE", + "url": "https://github.com/vyperlang/vyper" + } + ], + "database_specific": { + "cwe_ids": [], + "severity": "LOW", + "github_reviewed": true, + "github_reviewed_at": "2025-02-21T22:43:33Z", + "nvd_published_at": null + } +} \ No newline at end of file diff --git a/advisories/github-reviewed/2025/02/GHSA-h33q-mhmp-8p67/GHSA-h33q-mhmp-8p67.json b/advisories/github-reviewed/2025/02/GHSA-h33q-mhmp-8p67/GHSA-h33q-mhmp-8p67.json new file mode 100644 index 00000000000..7713d7cad57 --- /dev/null +++ b/advisories/github-reviewed/2025/02/GHSA-h33q-mhmp-8p67/GHSA-h33q-mhmp-8p67.json @@ -0,0 +1,57 @@ +{ + "schema_version": "1.4.0", + "id": "GHSA-h33q-mhmp-8p67", + "modified": "2025-02-21T22:43:36Z", + "published": "2025-02-21T22:43:36Z", + "aliases": [ + "CVE-2025-27104" + ], + "summary": "Vyper has a double eval in For List Iter", + "details": "Multiple evaluation of a single expression is possible in the iterator target of a for loop. While the iterator expression cannot produce multiple writes, it can consume side effects produced in the loop body (e.g. read a storage variable updated in the loop body) and thus lead to unexpected program behavior. Specifically, reads in iterators which contain an ifexp (e.g. `for s: uint256 in ([read(), read()] if True else [])`) may interleave reads with writes in the loop body.\n\nThe fix is tracked in https://github.com/vyperlang/vyper/pull/4488.\n\n### Vulnerability Details\n\nVyper for loops allow two kinds of iterator targets, namely the `range()` builtin and an iterable type, like SArray and DArray. \n\nDuring codegen, iterable lists are required to not produce any side-effects (in the following code, `range_scope` forces `iter_list` to be parsed in a constant context, which is checked against `is_constant`).\n\n```python\ndef _parse_For_list(self):\n with self.context.range_scope():\n iter_list = Expr(self.stmt.iter, self.context).ir_node\n ...\n\ndef range_scope(self):\n prev_value = self.in_range_expr\n self.in_range_expr = True\n yield\n self.in_range_expr = prev_value\n\ndef is_constant(self):\n return self.constancy is Constancy.Constant or self.in_range_expr\n```\n\nHowever, this does not prevent the iterator from consuming side effects provided by the body of the loop. For dynamic arrays, the compiler simply panics:\n```vyper\nx: DynArray[uint256, 3]\n\n@external\ndef test():\n for i: uint256 in (self.usesideeffect() if True else self.usesideeffect()):\n pass\n\n@view\ndef usesideeffect() -> DynArray[uint256, 3]:\n return self.x\n```\n\nFor SArrays on the other hand, `iter_list` is instantiated in the body of a `repeat` ir, so it can be evaluated several times.\n\nHere are three illustrating examples. In the first example, the following test case pre-evaluates the iter list and stores the result to a temporary list in memory. So the list is only evaluated once, before entry into the loop body, and the log output will be 0, 0, 0.\n```vyper\nevent I:\n i: uint256\n\nx: uint256\n\n@deploy\ndef __init__():\n self.x = 0\n\n@external\ndef test():\n for i: uint256 in [self.usesideeffect(), self.usesideeffect(), self.usesideeffect()]:\n self.x += 1\n log I(i)\n\n@view\ndef usesideeffect() -> uint256:\n return self.x\n```\n\nHowever, in the next two examples, because the iterator target is not a list literal, it will be evaluated in the loop body. In the second example, `iter_list` is an ifexp, thus it will be evaluated lazily in the loop body. The log output will be 0, 1, 2 due to consumption of side effects.\n\n```vyper\nevent I:\n i: uint256\n\nx: uint256\n\n@deploy\ndef __init__():\n self.x = 0\n\n@external\ndef test():\n for i: uint256 in ([self.usesideeffect(), self.usesideeffect(), self.usesideeffect()] if True else self.otherclause()):\n self.x += 1\n log I(i)\n\n@view\ndef usesideeffect() -> uint256:\n return self.x\n\n@view\ndef otherclause() -> uint256[3]:\n return [0, 0, 0]\n```\n\nIn the third example, `iter_list` is also an ifexp, thus it will only be evaluated in the loop body. The log output will be 0, 1, 2 due to consumption of side effects.\n\n```vyper\nevent I:\n i: uint256\n\nx: uint256[3]\n\n@deploy\ndef __init__():\n self.x = [0, 0, 0]\n\n@external\ndef test():\n for i: uint256 in (self.usesideeffect() if True else self.otherclause()):\n self.x[0] += 1\n self.x[1] += 1\n self.x[2] += 1\n log I(i)\n\n@view\ndef usesideeffect() -> uint256[3]:\n return self.x\n\n@view\ndef otherclause() -> uint256[3]:\n return [0, 0, 0]\n```", + "severity": [], + "affected": [ + { + "package": { + "ecosystem": "PyPI", + "name": "vyper" + }, + "ranges": [ + { + "type": "ECOSYSTEM", + "events": [ + { + "introduced": "0" + }, + { + "fixed": "0.4.1" + } + ] + } + ], + "database_specific": { + "last_known_affected_version_range": "<= 0.4.0" + } + } + ], + "references": [ + { + "type": "WEB", + "url": "https://github.com/vyperlang/vyper/security/advisories/GHSA-h33q-mhmp-8p67" + }, + { + "type": "WEB", + "url": "https://github.com/vyperlang/vyper/pull/4488" + }, + { + "type": "PACKAGE", + "url": "https://github.com/vyperlang/vyper" + } + ], + "database_specific": { + "cwe_ids": [], + "severity": "LOW", + "github_reviewed": true, + "github_reviewed_at": "2025-02-21T22:43:36Z", + "nvd_published_at": null + } +} \ No newline at end of file