{"id":1190,"date":"2026-10-10T14:49:22","date_gmt":"2026-10-10T14:49:22","guid":{"rendered":"https:\/\/wyominginvest.com\/?p=1190"},"modified":"2026-10-10T14:49:22","modified_gmt":"2026-10-10T14:49:22","slug":"critical-solidity-memory-bug-discovered-deleting-elements-in-memory-byte-arrays-clears-full-32-byte-words","status":"publish","type":"post","link":"https:\/\/wyominginvest.com\/?p=1190","title":{"rendered":"Critical Solidity Memory Bug Discovered: Deleting Elements in Memory Byte Arrays Clears Full 32-Byte Words"},"content":{"rendered":"<p>The Ethereum Foundation\u2019s security team has released an urgent advisory detailing a severe code generation vulnerability affecting the Solidity programming language. Uncovered on August 14, 2026, by researcher Shaheen Fazim through the Ethereum Foundation&#8217;s bug bounty program, the vulnerability resides within the Solidity code generator. Specifically, when the <code>delete<\/code> operator is applied to an individual element of a <code>bytes<\/code> or <code>string<\/code> array located in memory, the compiler generates bytecode that writes 32 zero bytes starting precisely at that element, instead of clearing only the targeted single byte. <\/p>\n<p>This unexpected behavior introduces catastrophic memory corruption risks. Not only does the operation wipe out the intended byte, but it also inadvertently clears up to 31 subsequent bytes. Depending on where the deleted element sits within the array, this spillover effect either obliterates neighboring elements further down the same array or breaches the boundaries of the allocation entirely, overwriting whatever critical data happens to reside in adjacent memory slots. Interestingly, the equivalent manual assignment operation, such as setting <code>b[i] = 0<\/code>, remains completely unaffected by this flaw and correctly clears only a single byte.<\/p>\n<p>The development team has assigned this vulnerability a severity rating of low-to-medium on their internal scoring scale. The defect exclusively impacts the default <code>evmasm<\/code> compilation pipeline, while the modern IR (Intermediate Representation) pipeline remains entirely safe. Furthermore, the activation of the Solidity optimizer is not required for the bug to manifest; the flawed semantics are baked directly into the base compilation output of the code generator stage. <\/p>\n<h2>Scope and Affected Versions<\/h2>\n<p>The vulnerable code path is deeply embedded in the compiler&#8217;s history, predating the very first official release version of Solidity. Consequently, all legacy and current versions up to and including Solidity 0.8.37 are impacted. Developers utilizing these versions must take immediate notice, as the defect persists across a vast ecosystem of smart contracts written over the years. Fortunately, the Solidity core team acted swiftly upon receiving Shaheen Fazim\u2019s report, implementing a robust fix that officially lands in Solidity version 0.8.38.<\/p>\n<p>To gauge the real-world impact of the bug across deployed infrastructure, the Ethereum Foundation conducted a comprehensive scan of the Sourcify database, which catalogs approximately 870,000 verified smart contract compilations. The analysis searched specifically for the exact source pattern capable of triggering the bug. Out of nearly a million verified compilations, the vulnerable pattern appeared in a mere 17 instances, which collectively comprised only two distinct functions. <\/p>\n<p>A deeper security assessment of those two unique functions revealed a fortunate reality: neither function was actually exploitable in practice. In both instances, the bytes cleared inside the array were either immediately discarded or superseded by subsequent logic, meaning only the memory trailing the array held any relevance. In one case, the host array&#8217;s length was mathematically proven never to expand enough to reach the overwritten memory space. In the other case, no data was actively allocated in the adjacent memory slots when the delete instruction executed, causing the erroneous zero-write to harmlessly land in unassigned memory. As a result, no known deployed smart contracts have been compromised or affected in the wild.<\/p>\n<h2>Which Contracts Are Affected?<\/h2>\n<p>Determining whether a specific codebase is vulnerable requires evaluating a specific set of conditions. A smart contract is only susceptible to this memory corruption bug if multiple technical criteria align simultaneously during compilation and execution. <\/p>\n<p>First, the contract must be compiled using an affected version of Solidity, ranging anywhere from the language&#8217;s inception up to version 0.8.37. Second, the compilation must rely on the default <code>evmasm<\/code> code generation pipeline rather than the IR pipeline. Third, the contract logic must actively apply the <code>delete<\/code> operator to an element of a dynamic byte array or string that resides specifically in memory. <\/p>\n<p>While the first two conditions can be easily verified by inspecting compiler configurations and source files, the third condition requires a thorough understanding of the memory layout generated by the compiler. Because memory allocation structures can be intricate, developers who confirm the first two criteria are strongly advised to treat their contracts as potentially vulnerable rather than attempting to manually rule out the complex memory layout interactions. Conversely, codebases that never apply the <code>delete<\/code> keyword to elements of memory-based <code>bytes<\/code> or <code>string<\/code> variables remain entirely safe.<\/p>\n<p>The language maintainers emphasize that neither the optimizer settings nor the target EVM version influence whether the bug manifests. The root defect stems strictly from the instructions emitted during the codegen phase, and the optimizer merely preserves those faulty semantics. <\/p>\n<p>Several technical nuances dictate the boundaries of this behavior. Only element-level deletions are vulnerable; calling <code>delete b<\/code> on an entire array invokes a completely separate code path that functions correctly. Furthermore, the target array must reside in memory. Byte arrays stored in persistent storage utilize an entirely independent management path and are immune, while calldata arrays cannot have the <code>delete<\/code> operator applied to them at all. Arrays whose elements occupy a full 32-byte word in memory\u2014such as <code>bytes1[] memory<\/code>\u2014are also unaffected because only <code>bytes<\/code> and <code>string<\/code> types pack their elements tightly without internal padding. <\/p>\n<p>String values, while fundamentally representing variable-length UTF-8 characters, share the exact same underlying memory representation as <code>bytes<\/code>. While writing <code>delete s[i]<\/code> directly for a <code>string memory s<\/code> will fail to compile, developers frequently cast strings to <code>bytes<\/code> to perform granular manipulations. Executing <code>delete bytes(s)[i]<\/code> on a casted string triggers the exact same memory corruption vulnerability. Conversely, fixed-byte types like <code>bytesN<\/code> are immune because indexing them yields an isolated copy of the byte rather than a mutable reference to the underlying memory structure.<\/p>\n<h2>Technical Details<\/h2>\n<p>To comprehend why this bug occurs, one must examine how Solidity handles dynamic byte arrays in memory. Most data types in Solidity memory occupy full 32-byte words, maintaining clean alignment boundaries. The primary exception is the <code>bytes<\/code> type, where a byte array consists of a 32-byte length header followed immediately by its raw data payload, packed back-to-back without any padding gaps between individual bytes.<\/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>Solidity manages memory allocation by continuously advancing a global free memory pointer. Every new allocation begins precisely where the preceding allocation terminates. Consequently, consecutive memory allocations sit flush against each other, separated at most by alignment padding. This tight arrangement means that any write operation running past the allocated boundary of one data structure spills directly into the next.<\/p>\n<p>In standard scenarios\u2014such as arrays initialized via <code>new bytes(n)<\/code> or instantiated from hardcoded literals\u2014the compiler appends up to 31 zero bytes of padding at the end of the data payload to ensure that any subsequent memory allocation starts cleanly on a fresh 32-byte word boundary. These are categorized as padded arrays. However, the default <code>evmasm<\/code> pipeline omits this padding for the results of <code>bytes.concat<\/code>, <code>string.concat<\/code>, and various ABI encoding functions like <code>abi.encode<\/code>, <code>abi.encodePacked<\/code>, <code>abi.encodeWithSelector<\/code>, <code>abi.encodeWithSignature<\/code>, and <code>abi.encodeCall<\/code>. In these unpadded arrays, the data ends precisely where the next allocation begins.<\/p>\n<p>When a value narrower than a word is stored in memory, its alignment depends heavily on its type. Left-aligned values, such as <code>bytesN<\/code> types, anchor themselves at the very beginning of the word. Right-aligned values, including integers, addresses, memory pointers, and dynamic array length headers, anchor themselves at the tail end of the word with leading zero bytes filling the front.<\/p>\n<p>Because byte arrays pack individual bytes tightly, storing a value into a byte array represents a rare scenario where a memory write is not equivalent to writing a full 32-byte word. The compiler normally handles this by issuing a single-byte store instruction (<code>mstore8<\/code>) whenever it recognizes the precise element width. However, the <code>delete<\/code> path introduces a critical exception to this rule.<\/p>\n<p>When a developer invokes <code>delete x<\/code>, the compiler is tasked with assigning the default zero value of that type. Logically, <code>delete b[i]<\/code> should behave identically to <code>b[i] = 0<\/code>. While both operations ultimately conclude with a write to the element&#8217;s specific memory location, the <code>evmasm<\/code> compilation pipeline utilizes completely distinct code paths and store implementations for each. <\/p>\n<p>Standard assignment correctly utilizes single-byte storage logic. In sharp contrast, the <code>delete<\/code> path mistakenly executes a full 32-byte word store (<code>mstore<\/code>) initialized with zeroes. Because this 32-byte zero write initiates at the specific index of the deleted element rather than aligning to the start of a 32-byte memory word, it sweeps forward indiscriminately. The array&#8217;s length header, which sits safely ahead of the data payload, remains untouched, allowing the array to retain its original length descriptor. Nevertheless, any downstream operations that compute hashes via <code>keccak256<\/code>, perform ABI encoding, or return the array contents will read the corrupted, zeroed-out memory bytes.<\/p>\n<h2>Practical Demonstration and Impact<\/h2>\n<p>The mechanics of this bug can be illustrated through a sample contract containing adjacent variable declarations that demonstrate both internal array corruption and external memory spillover.<\/p>\n<pre><code class=\"language-solidity\">contract C \n    struct S  uint256 x; \n\n    function clearElement() public pure returns (bytes memory) \n        bytes memory d = hex\"0102030405\";\n        delete d[0];\n        return d;\n    \n\n    function clearLastElement() public pure returns (uint256) \n        bytes memory b = new bytes(32);\n        S memory s = S(type(uint256).max);\n        delete b[31];\n        return s.x;\n    \n<\/code><\/pre>\n<p>When compiled and executed under the default <code>evmasm<\/code> pipeline, these functions produce unexpected alterations. In the <code>clearElement<\/code> function, a five-byte array occupies a single padded word. Executing <code>delete d[0]<\/code> triggers a 32-byte zero-write starting from the zeroth index, wiping out the initial byte alongside the remaining four bytes of the array. The function returns an entirely zeroed-out byte sequence instead of clearing solely the first index.<\/p>\n<p>In the <code>clearLastElement<\/code> function, a 32-byte array is allocated without padding, sitting directly adjacent to a struct containing a maximum-value 256-bit integer. Deleting the final element of the array causes the 32-byte zero write to spill 31 bytes past the array boundary and directly into the memory space occupied by the integer. Because integers are stored with their most significant bytes first, the spillover operation obliterates the 31 most significant bytes of the integer, leaving only the lowest byte intact. Consequently, the function returns an altered numerical value instead of the expected maximum integer. Replacing these delete statements with standard assignments correctly preserves the surrounding data.<\/p>\n<h2>Remediation and Recommended Actions<\/h2>\n<p>The discovery of this code generation flaw highlights the ongoing complexities of low-level EVM compilation and memory management. Although the practical likelihood of encountering this bug in production environments remains low\u2014as demonstrated by the sparse occurrences in the Sourcify ecosystem\u2014the potential for silent data corruption makes addressing it a high priority for smart contract developers.<\/p>\n<p>The core engineering team has successfully patched the vulnerability in Solidity version 0.8.37 by modifying the <code>delete<\/code> code path to properly consult element widths. The compiler now correctly emits a single-byte store instruction when clearing packed byte array elements, ensuring that <code>delete b[i]<\/code> and <code>b[i] = 0<\/code> share identical semantics across all compilation pipelines. <\/p>\n<p>Security professionals and smart contract developers currently maintaining codebases on vulnerable compiler versions are strongly advised to audit their code for any instances where the <code>delete<\/code> operator is applied to memory-based <code>bytes<\/code>, <code>string<\/code>, or casted string elements. Upgrading the project compiler version to 0.8.38 or later completely resolves the underlying code generation defect. For projects unable to upgrade immediately, developers should substitute element-level delete operations with explicit zero assignments to ensure reliable and predictable memory behavior.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The Ethereum Foundation\u2019s security team has released an urgent advisory detailing a severe code generation vulnerability affecting the Solidity programming language. Uncovered on August 14, 2026, by researcher Shaheen Fazim through the Ethereum Foundation&#8217;s bug bounty program, the vulnerability resides within the Solidity code generator. Specifically, when the delete operator is applied to an individual<\/p>\n","protected":false},"author":23,"featured_media":1189,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[366],"tags":[1280,401,951,1281,201,1278,949,1279,194,950,5,400,402,1282],"class_list":["post-1190","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-smart-contract-auditing","tag-arrays","tag-auditing","tag-byte","tag-clears","tag-critical","tag-deleting","tag-discovered","tag-elements","tag-full","tag-memory","tag-security","tag-smart-contracts","tag-solidity","tag-words"],"_links":{"self":[{"href":"https:\/\/wyominginvest.com\/index.php?rest_route=\/wp\/v2\/posts\/1190","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/wyominginvest.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/wyominginvest.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/wyominginvest.com\/index.php?rest_route=\/wp\/v2\/users\/23"}],"replies":[{"embeddable":true,"href":"https:\/\/wyominginvest.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1190"}],"version-history":[{"count":0,"href":"https:\/\/wyominginvest.com\/index.php?rest_route=\/wp\/v2\/posts\/1190\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/wyominginvest.com\/index.php?rest_route=\/wp\/v2\/media\/1189"}],"wp:attachment":[{"href":"https:\/\/wyominginvest.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1190"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/wyominginvest.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1190"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/wyominginvest.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1190"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}