{"schema":"threatline.record/2","record":{"id":"GHSA-cw3h-xwxq-8cx2","aliases":["GHSA-cw3h-xwxq-8cx2"],"summary":"Ternary expressions misread nested arrays with different inner bounds","details":"## Summary\r\n\r\nWhen a ternary selects between nested arrays with different inner bounds, selecting the narrower array can corrupt the result. The compiler reads it using the wider array's memory layout without first converting it. Inner arrays after the first are therefore read at the wrong offsets. This can silently produce incorrect values at all optimization levels. The equivalent `if`/`else` assignment gives the correct result.\r\n\r\n## Details\r\n\r\nIn Vyper 0.4.3, `parse_IfExp` (`vyper/codegen/expr.py:781-802`) returns a pointer to the selected branch, labelled with the ternary's result type. When both branches are in memory, it leaves their layouts unchanged. In the PoC below, each inner array in `narrow` occupies 96 bytes: a 32-byte length followed by space for two values. The result type reserves 160 bytes per inner array, allowing four values. `_get_element_ptr_array` in `vyper/codegen/core.py` uses that wider spacing, so it reads the second inner array 64 bytes past its actual start.\r\n\r\nFixed on `master` by [`1fb7dfe2` (#5236)](https://github.com/vyperlang/vyper/commit/1fb7dfe2). The fix copies branches with incompatible layouts into memory using the result type before returning their pointers.\r\n\r\nAffected versions: 0.3.8 (which introduced ternaries) through 0.5.0b1.\r\n\r\n## PoC\r\n\r\n```vyper\r\nremaining: public(DynArray[DynArray[uint256, 4], 4])\r\n\r\n@external\r\ndef set(flag: bool, narrow: DynArray[DynArray[uint256, 2], 4], wide: DynArray[DynArray[uint256, 4], 4]):\r\n    self.remaining = narrow if flag else wide\r\n```\r\n\r\n`set(True, [[6, 4], [7, 3]], [])` stores `[[6, 4], [0, 0, 0]]`. The compiler mistakes `narrow[1][1]`, which holds `3`, for the second inner array's length, then reads three zero values from unused memory. An `if`/`else` assignment stores the expected `[[6, 4], [7, 3]]`. Returning `narrow if flag else wide` as the wider type produces the same corrupted result.\r\n\r\n## Impact\r\n\r\nContracts can silently store or use corrupted values when the misread array lengths remain within bounds, as in this PoC. If a misread length exceeds its bound, the call can revert, depending on how the array is used.\r\n","categories":["Unclassified"],"product":"vyper","vendor":"","ecosystem":null,"affected_ranges":[{"source":"GitHub Advisory Database","source_url":"https://github.com/vyperlang/vyper/security/advisories/GHSA-cw3h-xwxq-8cx2","source_modified_at":"2026-10-05T17:04:08Z","fetched_at":"2026-10-26T06:00:26.954813Z","package":{"ecosystem":"pip","name":"vyper"},"range":">= 0.3.8, <= 0.5.0b1"}],"fixed_versions":[],"cvss":null,"severity":"Medium","kev":{"listed":false},"epss":{"probability":null,"percentile":null,"score_date":null},"published_at":"2026-10-05T17:04:08Z","modified_at":"2026-10-05T17:04:08Z","provenance":[{"source":"GitHub Advisory Database","url":"https://github.com/vyperlang/vyper/security/advisories/GHSA-cw3h-xwxq-8cx2","fetched_at":"2026-10-26T06:00:26.954813Z","source_modified_at":"2026-10-05T17:04:08Z"}],"sources":["GitHub Advisory Database"],"references":[],"locations":[],"motoko_assessment":{"status":"requires_architecture_review","automatic_remediation":false},"classification":{"method":"product-first software-layer rules","rules_version":"2.1","matched_on":null,"confidence":"unknown","reviewed":false},"change_timestamp":"2026-10-05T17:04:08Z","content_hash":"a89e3d92989e5ec4f27a4abe16c26fe0976b6cdfc1b20d2b8794327c936b696c"}}
