A newly disclosed vulnerability in the Solidity programming language has brought to light a severe code generation defect that impacts memory-based byte arrays across nearly all historical and current versions of the compiler. Discovered on August 14, 2026, by researcher Shaheen Fazim through the Ethereum Foundation’s bug bounty program, the vulnerability causes memory corruption whenever the delete operator is applied to an individual element of a bytes or string array located in memory. Rather than precisely clearing only the targeted byte, the compiled bytecode executes a full 32-byte zero write starting at that specific index, inadvertently wiping out subsequent data either within the same array or inside adjacent memory allocations.
The Ethereum Foundation’s Solidity team has classified this flaw with a "low/medium" severity rating on its internal scale. The issue exclusively affects the default evmasm compilation pipeline, while the newer IR (Intermediate Representation) pipeline remains entirely immune. Furthermore, the activation of the Solidity optimizer is not required to trigger the bug, meaning developers utilizing default compiler configurations are exposed if their contracts execute the specific vulnerable code pattern. Code paths affected by this defect predate the very first officially released version of Solidity, rendering every version up to and including 0.8.36 vulnerable. The issue has been formally resolved in Solidity version 0.8.37.
To measure the real-world impact of the vulnerability, the development team 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 flaw. The investigation revealed that the pattern appeared in a mere 17 compilations, encompassing only two distinct functions. Upon closer inspection, neither of those functions was determined to be practically exploitable or vulnerable in a way that risks funds or contract state integrity. Furthermore, no actively deployed smart contracts in the wild are known to have been compromised or affected by the defect.
Which Contracts Are Affected?
Understanding whether a specific smart contract is vulnerable requires analyzing both its compilation settings and its source code structure. A contract is only potentially at risk if multiple precise conditions align during execution. First, the contract must be compiled using the legacy or default evmasm code generation pipeline, as projects built exclusively with the IR pipeline do not manifest the error. Second, the codebase must invoke the delete operator specifically on an element of a memory-based bytes or string array, such as delete b[i].
If a contract never applies the delete keyword to individual elements of memory byte arrays or strings, it remains entirely safe. Neither the chosen optimization level nor the target Ethereum Virtual Machine (EVM) version alters how the bug behaves, because the core defect is embedded directly within the bytecode emitted during the code generation stage, and the optimizer merely preserves those faulty semantics.
Certain nuances separate vulnerable code paths from safe ones. Only element-level deletions are impacted; invoking delete b on an entire array routes through an entirely different code path and functions correctly. Additionally, the targeted array must reside in memory. Byte arrays stored permanently in storage utilize a separate handling mechanism and are unaffected, while calldata arrays cannot have the delete operator applied to them at all. Furthermore, arrays whose elements occupy an entire 32-byte word in memory—such as bytes1[] memory—are completely safe. The vulnerability exclusively affects types like bytes and string that pack their elements tightly back-to-back without gaps.
String values are equally vulnerable despite syntax constraints. While writing delete s[i] directly for a string memory s will fail to compile, developers frequently cast strings to dynamic byte arrays via type conversion. Writing delete bytes(s)[i] triggers the exact same underlying compiler bug, modifying the string’s internal memory representation directly in place. Conversely, fixed-size byte types like bytes32 are unaffected because indexing them creates an isolated copy of the byte rather than returning a mutable reference into the underlying data structure.
Technical Details
To fully grasp the mechanics of the bug, one must examine how Solidity manages memory allocation and data representation. Most data types in Ethereum memory consume full 32-byte words, but dynamic bytes arrays represent a notable exception. A memory byte array consists of a 32-byte length header followed immediately by its raw data bytes, packed continuously without padding or internal gaps. Solidity allocates memory sequentially by advancing a global free memory pointer, meaning consecutive memory allocations sit flush against one another. Consequently, any write operation that accidentally overruns the boundaries of a specific allocation spills directly into whatever data structure follows it in memory.
For most standard allocations—such as arrays initialized via new bytes(n) or declared using literal values—the compiler appends up to 31 zero bytes of padding at the end to ensure subsequent memory allocations begin cleanly on a 32-byte word boundary. These are categorized as padded arrays. However, the legacy evmasm pipeline omits this padding for arrays produced by bytes.concat, string.concat, and various ABI encoding utilities including abi.encode, abi.encodePacked, abi.encodeWithSelector, abi.encodeWithSignature, and abi.encodeCall. For these unpadded arrays, the data ends precisely where the next allocation begins, heightening the risk of cross-allocation memory corruption.

When handling narrower types, memory layout rules dictate alignment. Left-aligned values like bytesN begin at the first byte of a word, whereas right-aligned values—including standard integers, contract addresses, memory pointers, and array length headers—sit at the tail end of the word, preceded by leading zero bytes. Because individual elements within byte arrays occupy only a single byte, storing data to memory normally requires specialized single-byte store instructions (mstore8) wherever the compiler tracks element widths.
The fundamental breakdown occurs specifically within the delete code path. Logically, evaluating delete x should assign the default zero value corresponding to x‘s data type, meaning delete b[i] ought to behave identically to an explicit assignment like b[i] = 0. While both operations ultimately conclude with a write operation directed at the element’s memory address, the legacy evmasm pipeline employs entirely different underlying routines for them. Standard assignments correctly identify the element width and execute a targeted single-byte store (mstore8). In contrast, the delete instruction routes through a legacy code path that executes a full 32-byte zero-word write (mstore) starting at the designated element index.
Because this wide write initiates at the element’s specific offset rather than a proper 32-byte word boundary, it stretches forward by 32 bytes, overwriting everything in its path. Crucially, the 32-byte length header located at the beginning of the array is never touched, meaning the array retains its original length metadata. Any subsequent operations that rely on the array’s contents—such as hashing via keccak256, performing ABI encoding routines, or returning data to an external caller—will operate silently on the corrupted byte sequence. The IR compilation pipeline uses unified storage routines for both assignments and deletions, which is why it remains completely unaffected by the vulnerability.
The reach of the corruption depends heavily on the index of the deleted element and how the array was originally allocated. When an element is deleted near the beginning or middle of an array, the 32-byte zero write stays entirely within the boundaries of the array’s memory allocation, clearing the targeted byte alongside up to 31 subsequent elements. Unless those subsequent array elements already happened to be zeroes, this alters the array’s internal contents immediately upon execution.
When a delete operation targets one of the final 31 elements of an array, the 32-byte write extends past the array’s allocated space and spills into whatever data structure happens to be allocated immediately afterward in memory. For padded arrays, the trailing padding absorbs a portion of this overflow, meaning only deletions near the absolute tail end of the array breach the boundary, and they do so by a reduced margin of at most 31 bytes minus the padding size. For unpadded arrays, however, deleting any of the last 31 elements breaches the adjacent memory allocation regardless of the array’s total length. For small unpadded arrays containing 31 bytes or fewer, this means every single index—including the very first element—will cause memory overflow into subsequent allocations.
Example Demonstration
A clear illustration of this memory corruption behavior can be observed in a standard contract designed to demonstrate both internal array corruption and cross-allocation bleeding. Assuming consecutive memory declarations are allocated adjacently by the compiler, consider a contract containing two distinct functions. The first function allocates a short five-byte array, deletes the element at index zero, and returns the array. The second function allocates a 32-byte zero-filled array followed immediately by a struct containing a maximum-value 256-bit integer, and then deletes the final element of the byte array.
contract C
struct S uint256 x;
// Corruption inside the array.
function clearElement() public pure returns (bytes memory)
bytes memory d = hex"0102030405";
delete d[0];
return d;
// Corruption beyond the array.
function clearLastElement() public pure returns (uint256)
bytes memory b = new bytes(32);
S memory s = S(type(uint256).max);
delete b[31];
return s.x;
When compiled using default legacy settings, executing clearElement results in the entire five-byte sequence being zeroed out rather than just the first index, returning a string of five zero bytes instead of the expected single modification. In the second function, because a 32-byte array allocated via new bytes(32) occupies an exact word without padding, deleting the final element at index 31 triggers a 32-byte zero write that spills 31 bytes deep into the adjacent struct memory. Because unsigned integers are stored right-aligned with their most significant bytes first, wiping out the leading 31 bytes of type(uint256).max leaves only the lowest byte intact. Consequently, the function unexpectedly returns 255 instead of the maximum possible 256-bit integer value. Replacing these delete instructions with explicit assignments successfully preserves data integrity across identical compiler versions.
Severity Assessment and Remediation
The Solidity compiler emits no warnings when encountering this pattern, and the generated code executes without triggering runtime reverts. This silent failure mode makes diagnosis challenging, though internal array corruption is frequently caught by unit tests checking output data arrays under the legacy pipeline. Because the bug manifests without requiring inline assembly and affects standard high-level language constructs, developers relying on legacy compiler versions must exercise caution.
Practical risk in production remains low, as evidenced by the sparse occurrences found in the Sourcify ecosystem scan and the absence of known exploits. Nevertheless, upgrading toolchains remains the definitive solution. The development team addressed the root cause in Solidity 0.8.37 by modifying the delete code path to correctly query element widths in the same manner as standard assignment operations. When clearing packed byte array elements, the compiler now correctly emits a single-byte store (mstore8) rather than a full-word store, ensuring complete behavioral parity between delete b[i] and b[i] = 0 across all compilation pipelines.
Developers maintaining legacy smart contract codebases are strongly advised to audit their usage of memory-based bytes and string arrays, identify any instances where individual elements are deleted using the delete keyword, and migrate their compilation toolchains to Solidity 0.8.37 or later to permanently eliminate exposure to the vulnerability.

