{"id":1386,"date":"2026-10-12T06:15:21","date_gmt":"2026-10-12T06:15:21","guid":{"rendered":"https:\/\/smartcryptosecurity.com\/?p=1386"},"modified":"2026-10-12T06:15:21","modified_gmt":"2026-10-12T06:15:21","slug":"critical-memory-corruption-bug-discovered-in-solidity-code-generator-affecting-versions-up-to-0-8-37-4","status":"publish","type":"post","link":"https:\/\/smartcryptosecurity.com\/?p=1386","title":{"rendered":"Critical Memory Corruption Bug Discovered in Solidity Code Generator Affecting Versions up to 0.8.37"},"content":{"rendered":"<p>A significant vulnerability in the Solidity code generator has been officially disclosed following a report submitted through the Ethereum Foundation&#8217;s bug bounty program. Discovered by researcher Shaheen Fazim on August 14, 2026, the defect causes the <code>delete<\/code> operator to misbehave when applied to individual elements of dynamic <code>bytes<\/code> or <code>string<\/code> arrays residing in memory. Instead of safely clearing only the targeted byte, the generated code executes a full 32-byte zero-write starting directly at the specified index. This unintended wide-scale overwrite affects internal array elements and, under specific conditions, can spill over into adjacent memory allocations, posing silent data corruption risks for smart contracts compiled using the default <code>evmasm<\/code> pipeline.<\/p>\n<p>The Ethereum Foundation\u2019s Solidity team has assessed the severity of the flaw as low-to-medium on its internal scale. The vulnerability is exclusively tied to the default <code>evmasm<\/code> compilation pipeline, leaving the modern IR (Intermediate Representation) pipeline entirely unaffected. Furthermore, the activation of the Solidity optimizer is not required for the bug to manifest, as the root issue lies directly within the fundamental code emission logic of the code generator stage itself. Historical analysis of the codebase reveals that the affected code path predates the very first official release of Solidity, meaning that all compiler versions up to and including version 0.8.36 are vulnerable. The issue has been officially resolved and patched in Solidity version 0.8.37.<\/p>\n<p>To gauge the real-world impact of the discovery, developers conducted a comprehensive scan of the Sourcify database, analyzing approximately 870,000 verified smart contract compilations for the precise source code pattern capable of triggering the bug. The search identified the pattern in only 17 distinct compilations, which collectively housed just two unique functions. Further security assessments confirmed that neither of those functions was actually susceptible to malicious exploitation or destructive state corruption. As a result, no deployed smart contracts in the wild are currently known to have been compromised by the vulnerability.<\/p>\n<h2>Which Contracts Are Affected?<\/h2>\n<p>Understanding the precise conditions under which the vulnerability occurs is critical for smart contract developers reviewing their codebases. A contract is only vulnerable if it meets a specific set of operational criteria during execution. First, the compilation must utilize the default <code>evmasm<\/code> backend pipeline rather than the IR pipeline. Second, the source code must explicitly apply the <code>delete<\/code> operator to a specific element within a <code>bytes<\/code> or <code>string<\/code> array stored strictly in memory. <\/p>\n<p>While checking the compiler pipeline and source code parameters can be accomplished easily through configuration files and code audits, evaluating the exact memory layout produced by the compiler requires deeper technical analysis. Consequently, security experts advise that if a contract utilizes memory-based byte arrays and applies the <code>delete<\/code> operator to indexed elements under the <code>evmasm<\/code> pipeline, it is safer to treat the contract as potentially affected rather than attempting to manually rule out complex memory proximity scenarios.<\/p>\n<p>Contracts that never apply the <code>delete<\/code> operator to an element of a memory-based <code>bytes<\/code> or <code>string<\/code> type are entirely immune. Neither the activation of the optimizer nor the target Ethereum Virtual Machine (EVM) version alters how the bug manifests, because the defect is embedded directly within the emitted instructions of the code generator, and the downstream optimizer preserves these faulty semantics.<\/p>\n<p>The vulnerability carries several key nuances regarding its operational scope. Only individual element deletes are affected; applying <code>delete<\/code> to an entire array via <code>delete b<\/code> triggers a completely separate and correct code path. Additionally, the array must reside in memory. Byte arrays located in storage utilize entirely distinct handling routines and remain completely unaffected, while calldata arrays cannot have the <code>delete<\/code> operator applied to them at all. Arrays whose elements occupy a full 32-word block in memory\u2014such as <code>bytes1[] memory<\/code>\u2014are likewise unaffected because only dynamic <code>bytes<\/code> and <code>string<\/code> types pack their internal elements back-to-back without padding. <\/p>\n<p>String values are also affected by extension. Although direct usage of <code>delete s[i]<\/code> on a <code>string memory s<\/code> will fail to compile, developers frequently cast strings to <code>bytes<\/code> types\u2014such as <code>bytes(s)[i]<\/code>\u2014to perform index-based manipulations, which immediately triggers the identical underlying memory corruption bug.<\/p>\n<h2>Technical Details and Compiler Internals<\/h2>\n<p>To fully comprehend why the bug occurs, it is necessary to examine how Solidity manages memory allocation and data representation. Most data types in Solidity memory occupy full 32-byte words. The prominent exception is the <code>bytes<\/code> type, where a memory-allocated byte array consists of an initial 32-byte length header followed immediately by raw data packed consecutively without internal gaps. Solidity manages memory allocation sequentially by advancing a global free memory pointer, ensuring that consecutive allocations sit flush against one another, separated only by necessary word-alignment padding. Consequently, any write operation that accidentally overshoots the intended boundaries of one allocation will land directly inside the memory space of the subsequent allocation.<\/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>For most standard byte arrays created via keywords like <code>new bytes(n)<\/code> or initialized from literal values, the compiler appends up to 31 zero bytes of padding so that any subsequent memory allocation begins neatly on a clean 32-byte word boundary. These are referred to as padded arrays. However, the <code>evmasm<\/code> pipeline deliberately omits padding for the results of <code>bytes.concat<\/code>, <code>string.concat<\/code>, and various <code>abi.encode<\/code> variants including <code>abi.encodePacked<\/code>, <code>abi.encodeWithSelector<\/code>, <code>abi.encodeWithSignature<\/code>, and <code>abi.encodeCall<\/code>. In these unpadded allocations, the data ends precisely where the next allocation begins, heightening the risk of cross-allocation memory contamination.<\/p>\n<p>When dealing with narrower data types in memory, alignment rules dictate positioning. Left-aligned types like <code>bytesN<\/code> begin at the first byte of a word, whereas right-aligned types such as integers, addresses, memory pointers, and array length headers sit at the end of a word with leading zero bytes. Because dynamic byte array elements are individual bytes, storing data into a byte array represents a rare scenario where memory writes cannot simply rely on full-word operations. The compiler ordinarily handles this by emitting a single-byte store instruction (<code>mstore8<\/code>) whenever it recognizes the precise element width, except along the flawed deletion code path.<\/p>\n<p>The root cause of the vulnerability lies within the divergence of the delete implementation. When a developer writes <code>delete x<\/code>, the compiler is designed to assign the zero value corresponding to that type, meaning <code>delete b[i]<\/code> should be functionally identical to the standard assignment <code>b[i] = 0<\/code>. Both operations ultimately culminate in a write operation to the element&#8217;s designated location. However, within the vulnerable <code>evmasm<\/code> pipeline, these two code paths utilize completely distinct internal store routines. <\/p>\n<p>While standard assignments properly factor in the single-byte element width and emit a precise <code>mstore8<\/code> instruction, the delete path incorrectly treats the target as a full word. This results in the emission of a 32-byte zero write starting directly at the location of the deleted element. Because this write begins at an indexed byte offset rather than aligned to a clean 32-byte word boundary, it stretches forward by a full 32 bytes, overwriting everything in its path. Because the initial 32-byte length header of the array is located before the data payload, it remains untouched, allowing the array to retain its original length indicator. Consequently, any subsequent operations relying on the array contents\u2014such as computing cryptographic hashes via <code>keccak256<\/code>, performing ABI encoding, or returning data to an external caller\u2014will silently process corrupted, zeroed-out memory bytes. Meanwhile, the modern IR pipeline utilizes a unified store routine for both assignments and deletions, remaining completely immune to the defect.<\/p>\n<p>The physical reach of the runaway zero-write depends heavily on the index of the deleted element and the presence of allocation padding. When deleting an element deep inside a padded array, the trailing padding bytes absorb a portion of the write. For instance, arrays whose lengths are exact multiples of 32 contain zero padding, meaning that deleting any of the final 31 elements will cause the 32-byte zero write to breach the array boundaries and overwrite the beginning of whatever memory allocation sits directly after it. For unpadded arrays generated by concatenation or encoding functions, deleting any of the final 31 elements\u2014or even the very first element if the array itself is short\u2014will immediately spill over into subsequent memory.<\/p>\n<h2>Practical Demonstration and Impact Assessment<\/h2>\n<p>To illustrate the mechanics of the vulnerability, security researchers provided a concrete contract example demonstrating both internal array corruption and external memory spillover. In scenarios involving internal array corruption, deleting an element near the beginning of a short array clears not only that specific byte but also wipes out up to 31 subsequent bytes within the same data structure. In scenarios involving external memory spillover, an array allocated immediately before a complex data structure\u2014such as a memory struct containing a large integer\u2014will experience a partial overwrite of that adjacent struct when an element near the tail end of the array is deleted. <\/p>\n<p>Observed testing confirms that while an explicit assignment like <code>b[i] = 0<\/code> correctly modifies only the targeted byte, using <code>delete b[i]<\/code> on vulnerable compiler versions propagates unexpected zeroes across adjacent memory. Because integers are stored with their most significant bytes first, overwriting the leading bytes of a maxed-out integer variable drastically alters its numerical value, turning a high-scale numeric boundary into a small integer without triggering any compiler warnings or runtime reversion exceptions.<\/p>\n<p>Despite the technical severity of arbitrary memory overwrites, the practical likelihood of exploitation remains low. The bug persisted undetected throughout the multi-year history of the language, and database scans confirmed that the vulnerable code pattern is exceptionally rare in production environments. Furthermore, in the few instances where the pattern was identified, the overwritten bytes were either immediately discarded or positioned in unallocated memory spaces where no critical application state could be damaged.<\/p>\n<p>The Solidity development team has fully resolved the vulnerability in version 0.8.37 by modifying the compiler&#8217;s delete code path to correctly consult element widths. The compiler now emits the proper single-byte store instruction (<code>mstore8<\/code>) during element deletions, ensuring that <code>delete b[i]<\/code> and <code>b[i] = 0<\/code> behave identically across all compilation pipelines. Developers maintaining active codebases utilizing affected compiler versions are strongly encouraged to upgrade their toolchains to Solidity 0.8.37 or later, and to audit any memory-intensive byte array manipulation routines that make use of the <code>delete<\/code> operator.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>A significant vulnerability in the Solidity code generator has been officially disclosed following a report&#8230;<\/p>\n","protected":false},"author":6,"featured_media":1385,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[366],"tags":[869,401,406,961,201,962,407,960,5,400,402,964],"class_list":["post-1386","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-smart-contract-auditing","tag-affecting","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\/1386","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\/6"}],"replies":[{"embeddable":true,"href":"https:\/\/smartcryptosecurity.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1386"}],"version-history":[{"count":0,"href":"https:\/\/smartcryptosecurity.com\/index.php?rest_route=\/wp\/v2\/posts\/1386\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/smartcryptosecurity.com\/index.php?rest_route=\/wp\/v2\/media\/1385"}],"wp:attachment":[{"href":"https:\/\/smartcryptosecurity.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1386"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/smartcryptosecurity.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1386"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/smartcryptosecurity.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1386"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}