{"id":926,"date":"2026-10-07T22:15:20","date_gmt":"2026-10-07T22:15:20","guid":{"rendered":"https:\/\/smartcryptosecurity.com\/?p=926"},"modified":"2026-10-07T22:15:20","modified_gmt":"2026-10-07T22:15:20","slug":"critical-memory-corruption-bug-discovered-in-solidity-code-generator-affects-versions-up-to-0-8-37","status":"publish","type":"post","link":"https:\/\/smartcryptosecurity.com\/?p=926","title":{"rendered":"Critical Memory Corruption Bug Discovered in Solidity Code Generator Affects Versions Up to 0.8.37"},"content":{"rendered":"<p>A significant vulnerability in the Solidity compiler has been disclosed, shedding light on a code-generation flaw that could lead to unintended memory corruption in Ethereum smart contracts. Tracked and reported through the Ethereum Foundation\u2019s bug bounty program by developer Shaheen Fazim on August 14, 2026, the issue resides within the default evmasm compilation pipeline. When the <code>delete<\/code> operator is applied to an individual element of a memory-resident <code>bytes<\/code> or <code>string<\/code> array, the compiler emits a faulty instruction that clears 32 consecutive zero bytes starting at the target element, rather than safely erasing a single byte.<\/p>\n<p>This unexpected behavior effectively wipes out up to 31 bytes following the targeted index. Depending on the array&#8217;s contents and layout, the corruption can spill over internally into subsequent elements of the same array or, if the deleted element sits near the tail end of the data structure, overwrite whatever memory allocation comes immediately after it. Notably, the equivalent manual assignment operation, such as setting <code>b[i] = 0<\/code>, remains completely unaffected by the defect, correctly modifying only the single specified byte.<\/p>\n<p>The development team has assigned the bug a low-to-medium severity rating on its internal scale. The flaw is strictly localized to the default evmasm compilation pipeline; the modern IR (Intermediate Representation) pipeline remains entirely safe. Furthermore, the activation of the Solidity optimizer is not required for the bug to manifest, as the code-generation phase itself emits the erroneous instruction sequence. <\/p>\n<p>According to the vulnerability disclosure, the affected code path is deeply embedded in the compiler&#8217;s history, predating the very first official release of Solidity. Consequently, every compiler version up to and including version 0.8.36 is susceptible to the issue. The vulnerability has been officially patched and resolved in Solidity 0.8.37.<\/p>\n<p>To gauge the real-world impact of the bug, the Solidity team conducted a comprehensive scan of the Sourcify database, analyzing approximately 870,000 verified smart contract compilations for the specific source pattern that triggers the defect. The scan revealed that the problematic pattern appeared in a mere 17 compilations, comprising only two distinct functions. Upon closer inspection, neither of those functions was found to be genuinely vulnerable; in both instances, the bytes cleared within the array were either immediately discarded or deleted anyway, and the memory spaces located just beyond the arrays were either protected by structural limits or remained completely unallocated when the execution paths ran. As a result, no deployed smart contracts are currently known to have been harmed by the bug.<\/p>\n<p>Which Contracts Are Affected?<\/p>\n<p>For a smart contract to be vulnerable to this specific memory corruption issue, a precise combination of technical criteria must be met. First, the contract must be compiled using an affected version of Solidity\u2014any release up to version 0.8.36\u2014running on the default evmasm pipeline. Second, the source code must explicitly invoke the <code>delete<\/code> operator on an index belonging to a dynamic <code>bytes<\/code> or <code>string<\/code> array that resides strictly in memory. <\/p>\n<p>Developers who never apply the <code>delete<\/code> operator to individual elements of memory-based byte or string arrays do not need to worry, as their code is inherently safe from this vector. Neither the compiler optimizer settings nor the target Ethereum Virtual Machine version changes whether the bug manifests, because the underlying flaw stems purely from the instructions emitted during the code-generation stage, which the optimizer preserves without modification.<\/p>\n<p>Certain nuances help clarify the exact scope of vulnerability. Only targeted element deletions are affected; invoking <code>delete b<\/code> on an entire array executes an entirely separate and correct code path. Additionally, the target array must reside in memory. Byte arrays stored permanently in storage utilize a completely different management architecture and are untouched, while the <code>delete<\/code> operator cannot be applied to calldata arrays under any circumstances. Furthermore, arrays whose individual elements occupy a full 32-byte word in memory\u2014such as <code>bytes1[] memory<\/code>\u2014are immune to the bug. Only packed formats like <code>bytes<\/code> and <code>string<\/code> pack their elements tightly without internal padding. <\/p>\n<p>While direct index deletion like <code>delete s[i]<\/code> is not syntactically permitted for a <code>string memory<\/code> variable, developers frequently circumvent this by casting strings to bytes via <code>bytes(s)[i]<\/code> before deletion, which unwittingly triggers the exact same underlying vulnerability. The physical reach of the resulting data corruption depends entirely on where the deleted element is positioned within the array and how that specific array was allocated in memory during runtime execution.<\/p>\n<p>Technical Details<\/p>\n<p>To fully understand why the vulnerability occurs, one must look at how the Solidity compiler handles memory management for byte arrays. Most data types in Ethereum memory consume a full 32-byte word. The primary exception is the <code>bytes<\/code> type, where a byte array consists of an initial 32-byte length descriptor followed immediately by its raw data payload packed back-to-back without intervening gaps. <\/p>\n<p>Solidity allocates memory dynamically by advancing a global free memory pointer, ensuring that consecutive allocations sit flush against each other with minimal padding. When a write operation overruns the intended boundaries of one allocation, it bleeds directly into the adjacent memory block. <\/p>\n<p>In standard scenarios, arrays created via expressions like <code>new bytes(n)<\/code> or initialized directly from literals are padded with up to 31 zero bytes at the end, ensuring that any subsequent memory allocation begins neatly on a fresh 32-byte word boundary. These are categorized as padded arrays. Conversely, the evmasm pipeline leaves certain constructs\u2014such as <code>bytes.concat<\/code>, <code>string.concat<\/code>, and the entire family of <code>abi.encode<\/code> functions\u2014unpadded. For these unpadded arrays, the data ends precisely where the next memory allocation begins, leaving no buffer zone whatsoever.<\/p>\n<figure class=\"article-inline-figure\"><img decoding=\"async\" src=\"https:\/\/soliditylang.org\/assets\/logo-og-image.png\" alt=\"Memory Byte Array Element Delete Clears Whole Word Bug | Solidity Programming Language\" class=\"article-inline-img\" loading=\"lazy\" \/><\/figure>\n<p>Because individual elements within a byte array occupy only a single byte, storing data into a byte array is fundamentally different from storing a full word. The compiler is ordinarily designed to handle this by issuing a single-byte store instruction (<code>mstore8<\/code>) wherever the element width is explicitly known. However, the <code>delete<\/code> operation introduces a critical exception to this rule within the legacy pipeline.<\/p>\n<p>When a developer writes <code>delete x<\/code>, the compiler attempts to assign the zero value corresponding to that variable&#8217;s data type. Theoretically, <code>delete b[i]<\/code> should compile down to the exact same machine instructions as <code>b[i] = 0<\/code>. Both operations ultimately culminate in a memory store targeting the specific location of the array element. However, in the evmasm compilation pipeline, these two actions rely on entirely separate internal store implementations. <\/p>\n<p>While the standard assignment path correctly utilizes the single-byte store, the legacy <code>delete<\/code> path erroneously invokes a full 32-byte zero write. Because this write operation is initiated at the offset of the specific array element rather than aligned to a 32-byte word boundary, it stretches far beyond the intended single byte, overwriting 32 consecutive bytes of memory in a forward direction. Because the 32-byte length header located at the beginning of the array remains untouched, the array retains its original length parameter. Consequently, any subsequent operations that read from the array\u2014such as computing cryptographic hashes via <code>keccak256<\/code>, performing ABI encoding routines, or returning data to an external caller\u2014will quietly process the corrupted, zeroed-out memory contents. The IR compilation pipeline, by contrast, routes <code>delete b[i]<\/code> through the exact same unified store routine used by regular assignments, leaving it entirely immune to the defect.<\/p>\n<p>How Far the Write Reaches<\/p>\n<p>The consequences of this 32-byte zero write manifest in two distinct ways depending on the array&#8217;s structural layout: internal corruption and external corruption. <\/p>\n<p>When corruption occurs inside the array, every subsequent element following the deleted index\u2014up to a maximum of 31 elements\u2014is forcefully overwritten with zeros. Unless the remainder of the array happened to consist entirely of zero bytes anyway, this immediately alters the stored data without requiring any unusual runtime conditions.<\/p>\n<p>When corruption reaches beyond the array, the behavior is dictated by whether the array is padded or unpadded. For padded arrays, the trailing padding bytes act as a buffer, absorbing a portion of the unintended write. Deletions that occur near the middle of an array stay safely within bounds, whereas deletes targeting the final 31 elements can breach the boundary of the array allocation and spill into whatever data structure happens to be allocated immediately afterward. The exact number of vulnerable ending elements depends on the array&#8217;s total length. <\/p>\n<p>For unpadded arrays generated by concatenation or encoding functions, there is no trailing buffer padding. Consequently, deleting any of the final 31 elements\u2014regardless of the array&#8217;s overall length\u2014will invariably bleed into the next adjacent memory allocation. For small unpadded arrays consisting of 31 bytes or fewer, this vulnerability applies to every single index, including the very first element.<\/p>\n<p>Example<\/p>\n<p>A clear demonstration of this behavior involves a smart contract containing adjacent memory allocations. In test environments utilizing the default evmasm pipeline, calling a function that performs an element deletion on a short memory byte array immediately zeroes out subsequent bytes within that same array. Similarly, executing a deletion on the final element of a padded 32-byte array that sits directly adjacent to a memory struct variable results in the partial wiping of that neighboring data structure. <\/p>\n<p>In tests involving large integer values stored right after an array, the 31 overwritten bytes correspond to the most significant portions of the integer, transforming what should have been a maximum possible value down to a fraction of its intended size. Replacing the <code>delete<\/code> statement with an explicit assignment cleanly avoids the bug, demonstrating that the failure is isolated entirely to how the compiler generates code for the deletion pathway.<\/p>\n<p>Severity Assessment and Mitigation<\/p>\n<p>Because the compiler emits no warning messages during the build process and the EVM does not encounter a runtime exception or revert execution, bugs of this nature can prove exceptionally difficult to diagnose during standard development cycles. The failure manifests quietly as incorrect computational results rather than a crashing transaction. Nevertheless, internal array corruption is readily caught by comprehensive test suites that inspect the state of memory byte arrays after execution.<\/p>\n<p>While the likelihood of encountering this bug in the wild is low\u2014as evidenced by the lack of any discovered vulnerable contracts in public compilation databases\u2014developers maintaining legacy codebases using Solidity versions 0.8.36 and earlier are strongly advised to audit their code for memory byte and string array element deletions. <\/p>\n<p>The definitive resolution to the vulnerability is upgrading the compiler toolchain to Solidity 0.8.37 or later. The patch successfully aligns the compiler&#8217;s internal delete code path with its assignment pathway, ensuring that clearing a packed array element correctly issues a single-byte store instruction rather than a sweeping 32-byte memory write.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>A significant vulnerability in the Solidity compiler has been disclosed, shedding light on a code-generation&#8230;<\/p>\n","protected":false},"author":24,"featured_media":925,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[366],"tags":[963,401,406,961,201,962,407,960,5,400,402,964],"class_list":["post-926","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-smart-contract-auditing","tag-affects","tag-auditing","tag-code","tag-corruption","tag-critical","tag-discovered","tag-generator","tag-memory","tag-security","tag-smart-contracts","tag-solidity","tag-versions"],"_links":{"self":[{"href":"https:\/\/smartcryptosecurity.com\/index.php?rest_route=\/wp\/v2\/posts\/926","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/smartcryptosecurity.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/smartcryptosecurity.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/smartcryptosecurity.com\/index.php?rest_route=\/wp\/v2\/users\/24"}],"replies":[{"embeddable":true,"href":"https:\/\/smartcryptosecurity.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=926"}],"version-history":[{"count":0,"href":"https:\/\/smartcryptosecurity.com\/index.php?rest_route=\/wp\/v2\/posts\/926\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/smartcryptosecurity.com\/index.php?rest_route=\/wp\/v2\/media\/925"}],"wp:attachment":[{"href":"https:\/\/smartcryptosecurity.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=926"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/smartcryptosecurity.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=926"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/smartcryptosecurity.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=926"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}