The Ethereum Foundation and the Solidity core development team have released a critical security advisory warning developers of a long-standing vulnerability discovered within the Solidity code generator. Identified and reported on August 14, 2026, by security researcher Shaheen Fazim through the official Ethereum Foundation bug bounty program, the defect affects how memory-based bytes and string array elements are handled when the delete operator is applied.
According to the vulnerability disclosure, when the delete keyword is invoked on an element of a bytes or string array residing in memory, the compiler generates EVM assembly (evmasm) code that writes 32 zero bytes—equivalent to a full 32-byte machine word—starting directly at the index of the targeted element. This behavior deviates sharply from the correct semantics, which should only clear the single specified element. Instead, the erroneous write cascades forward, clearing up to 31 subsequent bytes. Depending on where the deleted element sits within the array, this unintended wipe either obliterates data further down within the same array or spills past the array’s boundaries to corrupt whatever variables or memory allocations happen to be sitting immediately afterward in the EVM’s memory space.
Contrastingly, the equivalent direct assignment operation—such as writing b[i] = 0—functions completely as intended, executing a single-byte store (mstore8) that correctly isolates the modification to exactly one byte.
The core development team has rated this vulnerability as having a low-to-medium severity on their internal risk scale. The flaw exclusively impacts the default evmasm compilation pipeline, while the newer intermediate representation (IR) compilation pipeline remains entirely unaffected. Furthermore, the activation of the Solidity optimizer is not required for the bug to manifest, meaning standard, unoptimized builds are equally vulnerable if compiled using the affected code paths.
Because the vulnerable code path predates the very first publicly released version of Solidity, the scope of affected software is remarkably broad. Every single version of Solidity up to and including version 0.8.36 is susceptible to the issue. The vulnerability has been officially patched and resolved in Solidity version 0.8.37.
To gauge the potential real-world impact of the bug across deployed decentralized applications, the Solidity team conducted a comprehensive scan of the Sourcify database, which catalogs roughly 870,000 verified smart contract compilations. The analysis searched specifically for the vulnerable source pattern. Out of nearly a million verified compilations, the triggering pattern surfaced in a mere 17 instances, which collectively accounted for only two distinct functions. Upon closer inspection, neither of those functions was actually vulnerable in practice. In one case, the data bytes cleared within the array were subsequently discarded, meaning only the memory trailing the array mattered; however, the array’s length mechanism guaranteed it would never grow large enough to reach the overwritten memory. In the other instance, no active allocations existed at the targeted memory addresses when the delete execution ran, causing the zero-byte write to land harmlessly in unused space. Consequently, the development team confirmed that no deployed smart contracts are currently known to have suffered malicious exploitation or accidental damage from this defect.
Which Contracts Are Affected?
Understanding exposure to this vulnerability requires examining specific conditions within a smart contract’s architecture. A contract is only potentially vulnerable if a very specific chain of criteria aligns during compilation and execution.
First, the project must be compiled using an affected version of the Solidity compiler, specifically versions 0.8.36 and earlier. Second, the compilation must utilize the default evmasm pipeline rather than the alternative IR pipeline. Third, the contract source code must explicitly apply the delete operator to an element of a dynamic bytes or string array located in memory.
While the first two conditions can be verified effortlessly by reviewing compiler settings and source files, the third condition requires deeper reasoning regarding the exact memory layout produced by the compiler during runtime. For developers unable to definitively rule out memory layout interactions, the Solidity team advises treating any contract meeting the first two criteria as potentially affected as a precautionary measure.
It is important to emphasize that if a smart contract never applies the delete operator to an individual element of a memory-resident bytes or string data structure, it is completely immune to the bug. Neither the selected EVM target version nor the activation of the compiler’s optimizer alters whether the bug manifests, as the flaw lies intrinsically within the code emission stage of the codegen pipeline, and the optimizer faithfully preserves those faulty execution semantics.
A closer look at the second condition reveals three vital clarifications regarding how memory arrays operate in Solidity. To begin with, only individual element deletions are affected; executing a blanket deletion on an entire array, such as delete b, follows an entirely distinct code path and executes correctly.
Furthermore, the target array must reside in memory (memory). Byte arrays stored permanently in contract storage (storage) utilize a separate, unaffected code path, and the delete operation cannot be applied to calldata arrays at all. Additionally, arrays whose individual elements naturally occupy a full 32-byte word in memory—such as bytes1[] memory—remain completely unaffected because their elements are not packed tightly together. Only bytes and string types pack their individual byte elements consecutively without gaps, making them uniquely susceptible.
Finally, string values are impacted identically to byte arrays. While standard Solidity syntax does not permit direct index deletion on a string memory s variable, developers frequently cast strings to byte arrays to manipulate them, writing delete bytes(s)[i]. Doing so triggers the exact same underlying memory corruption bug.
Technical Details
To fully comprehend the mechanics of the bug, it is necessary to examine how Solidity manages memory allocation and storage structures under the hood.
Most data types in Ethereum memory occupy full, independent 32-byte words. The primary exception to this rule is the bytes type. A dynamic byte array in memory consists of an initial 32-byte length indicator field followed immediately by the raw data bytes, packed back-to-back without internal padding gaps.
Solidity allocates memory sequentially by advancing the global free memory pointer, meaning every new allocation begins precisely where the previous one terminated. Consecutive memory allocations therefore sit flush against one another, separated only by necessary word-boundary padding. Because of this layout, any write operation that accidentally overshoots the allocated boundary of one data structure writes directly into the beginning of the next adjacent allocation.
For most standard memory arrays—such as those initialized via new bytes(n) or instantiated directly from hex literals—the compiler appends up to 31 zero bytes of padding at the end of the data block, ensuring that any subsequent memory allocation cleanly initiates on a fresh 32-byte word boundary. These are referred to as padded arrays.
Conversely, the default evmasm pipeline does not apply padding to the output of certain operations, including bytes.concat, string.concat, and the entire family of ABI encoding functions like abi.encode, abi.encodePacked, abi.encodeWithSelector, abi.encodeWithSignature, and abi.encodeCall. The data produced by these functions ends precisely where the next memory allocation begins, creating what the development team classifies as unpadded arrays. Whether an allocation receives padding is an internal compiler implementation detail, and because the behavior can vary between the evmasm and IR pipelines—or change in future releases—smart contract code must never rely on padding assumptions. Writing inline assembly that touches memory padding directly violates official memory safety guidelines.
When a memory value is narrower than a standard 32-byte word, its horizontal placement within that word depends entirely on its type classification. Left-aligned values, such as fixed-size bytesN types, anchor themselves at the very first byte of the word. Conversely, right-aligned values—including standard integers, contract addresses, memory pointers, and dynamic array length headers—sit flush against the right side of the word, padded with leading zero bytes in front.
String variables share the exact same internal representation as bytes, but because they hold variable-length UTF-8 characters, direct index access is restricted. Converting a string to a byte array via bytes(s) does not duplicate the underlying data in memory; instead, it provides a direct reference to the original string’s memory location, meaning delete bytes(s)[i] directly modifies the string data in place. Fixed-size bytes1 through bytes32 values, however, remain untouched by the bug because indexing them yields an isolated copy of the target byte rather than a mutable reference into underlying memory.

Because their constituent elements consist of single bytes, byte arrays represent the sole scenario where storing a value to memory is not structurally equivalent to writing a full word. The compiler normally manages this distinction by issuing a single-byte store instruction (mstore8) wherever it is explicitly aware of the narrow element width—with the sole exception of the faulty delete path.
The Delete Path
When a developer writes delete x, Solidity assigns the default zero value corresponding to x‘s data type. Conceptually, executing delete b[i] should behave identically to executing b[i] = 0. While both operations ultimately culminate in a write operation targeting the memory location of the array element, the evmasm pipeline historically failed to share the underlying store implementation between the two actions.
For standard element assignments like b[i] = 0, the compiler correctly queries the element width, recognizes that the array is composed of single-byte elements, and emits a single-byte store instruction (mstore8), cleanly updating only the targeted index. For the delete path, however, the compiler failed to consult the element width and erroneously treated the operation as a standard full-word write. This resulted in the emission of a 32-byte zero write starting precisely at the index of the deleted element and extending forward. Because the write instruction initiates at the specific byte offset of the element rather than snapping to a 32-byte word boundary, it stretches across multiple memory bytes, corrupting a full 32-byte window.
Crucially, the 32-byte length field preceding the array data is situated before the elements and is never touched by this errant write. Consequently, the array retains its original length header, masking the corruption. Any subsequent logic that computes cryptographic hashes of the array contents via keccak256, performs ABI encoding, or returns the array data to an external caller unknowingly reads and processes the corrupted, zeroed-out bytes.
The modern IR pipeline, by contrast, routes delete b[i] through the exact same unified store logic used by standard assignments, shielding IR-compiled contracts from the defect entirely.
How Far the Write Reaches
The extent of the memory corruption depends heavily on the index of the deleted element and the manner in which the array was initially allocated in memory. Deleting an element situated deep inside a large array causes the 32-byte zero write to sweep across subsequent elements within the same array data block. Every single element following the deleted index—up to a maximum of 31 subsequent elements—is forcibly overwritten with zeroes. Unless the array happens to be entirely populated by zero values beyond that point, this behavior immediately and permanently alters the data structure’s contents.
When the deletion targets elements near the very end of an array, the corruption spills past the array’s boundary entirely, writing directly into whatever variable or memory structure was allocated immediately afterward. For padded arrays, the trailing padding bytes absorb a portion of the write. Deletions affecting elements near the end of the array will only breach the boundary if the padding is insufficient to absorb the full 32-byte cascade. The number of tail elements capable of triggering an overflow depends directly on the array’s total length: arrays with lengths that are multiples of 32 possess zero padding, meaning deletions of their final 31 elements will always spill over into subsequent memory allocations.
When an overflow spills into adjacent memory, the nature of the damage depends on how the subsequent variable is aligned. For left-aligned values, the most significant bytes of the adjacent variable are cleared. For right-aligned values, such as standard uint256 integers or contract addresses, the write clears the leading bytes of the target word. Whether the adjacent variable survives this corruption depends entirely on the size of the write relative to the variable’s width. For instance, a minor write overflow of up to 12 bytes may leave an Ethereum address intact, whereas a maximal write of 31 bytes will obliterate the higher-order bytes of a large integer, leaving only the lowest byte intact.
For unpadded arrays—such as those generated via bytes.concat or ABI encoding functions—deleting any of the final 31 elements will always breach the adjacent memory allocation, regardless of the array’s total length. For unpadded arrays containing 31 bytes or fewer, this vulnerability applies to every single index, including the very first element.
Example Demonstrating the Flaw
To illustrate the practical implications of the bug, security analysts constructed a demonstration contract designed to expose both internal array corruption and cross-allocation memory overwrites. Relying on the deterministic nature of Solidity’s memory allocation model, where consecutive variable declarations are placed adjacently in memory, the contract showcases how easily data can be distorted.
The first function in the test contract, designated to show internal array corruption, initializes a short memory byte array containing five bytes (hex"0102030405"), executes delete d[0], and returns the modified array. Under correct operation, only the initial byte should be cleared, yielding hex"0002030405". Instead, the evmasm pipeline executes a full 32-byte word write starting at index zero, wiping out the entire array and returning hex"0000000000".
The second function demonstrates corruption extending beyond the array boundaries. It allocates a 32-byte padded memory byte array, declares an adjacent structure containing a maximum-value 256-bit unsigned integer (type(uint256).max), and then invokes delete b[31] on the final element of the array. Because the array occupies exactly one 32-byte word with no padding, deleting the final element triggers a 32-byte zero write that spills 31 bytes directly into the adjacent integer variable. Because integers are stored in memory with their most significant bytes placed first, the 31 most significant bytes of the integer are wiped clean to zero. Only the single lowest byte survives, causing the function to return 255 instead of the expected maximum integer value.
Replacing these delete statements with explicit assignments, such as d[0] = 0 and b[31] = 0, produces the correct, expected results across all tested compiler versions.
Severity Assessment and Real-World Likelihood
The Solidity compiler emits no warnings when compiling code containing this vulnerability, and the generated bytecode does not trigger runtime reverts or exceptions. Because the corruption manifests quietly as unexpected computational results rather than an outright crash, diagnosing the root cause can prove challenging for developers. However, internal array corruption—the more frequent of the two manifestations—is typically straightforward to catch during routine unit testing if developers explicitly inspect the state of memory arrays after exercising deletion logic.
Despite the technical severity of arbitrary memory corruption, the real-world probability of accidental exploitation is exceptionally low. The defect remained undetected throughout the multi-year history of the Solidity programming language. The extensive Sourcify database audit revealed that the vulnerable pattern was virtually non-existent in deployed smart contracts, confirming that developers rarely combine memory byte array element deletions with tight subsequent memory packing in ways that trigger dangerous memory overwrites.
The Fix and Recommended Actions
The core development team resolved the vulnerability by ensuring that the compiler’s delete code path consults the element width with the exact same logic utilized by standard assignment paths. When clearing an element belonging to a packed byte array, the compiler now correctly emits a single-byte store (mstore8) rather than a full 32-byte word store. Consequently, delete b[i] and b[i] = 0 now generate identical, safe execution semantics across both compilation pipelines.
Developers maintaining smart contracts written in Solidity are strongly advised to audit their codebases to determine if their projects utilize versions 0.8.36 or earlier. Projects that rely on memory-resident bytes or string arrays should immediately upgrade their compiler toolchains to Solidity version 0.8.37 or later. For teams unable to upgrade their compiler versions immediately, code reviews should focus on identifying and refactoring any instances where the delete operator is applied to individual elements of memory byte arrays or strings, replacing them with explicit zero assignments if necessary to guarantee safe execution.

