Home / Smart Contract Auditing / Solidity Compiler Vulnerability Discovered: Memory Byte Array Element Deletion Bug Prompts Urgent Patch in Version 0.8.37

Solidity Compiler Vulnerability Discovered: Memory Byte Array Element Deletion Bug Prompts Urgent Patch in Version 0.8.37

A critical logic defect in the Solidity code generator has been discovered affecting memory-based byte arrays and strings. Reported on August 14, 2026, by researcher Shaheen Fazim via the Ethereum Foundation’s bug bounty program, the vulnerability causes the deletion of a single element within a memory byte array to improperly overwrite a full 32-byte word instead of clearing just the targeted byte. This unexpected memory corruption affects all versions of Solidity up to and including version 0.8.36, prompting developers to update to the newly released Solidity 0.8.37.

The core of the issue lies in how the legacy evmasm code generation pipeline handles memory stores during element-level delete operations. When the delete keyword is applied to an individual element of a memory bytes array—or a string cast to a bytes type—the compiler emits bytecode that writes 32 zero bytes starting precisely at the location of that element. Because dynamic byte arrays store their elements packed back-to-back without word-alignment gaps, this wide-scale write operations stretches far beyond the intended index. It systematically wipes out up to 31 subsequent bytes, either further down inside the same array or spilling completely past the array boundaries into whatever data happens to reside next in the Ethereum Virtual Machine’s (EVM) memory allocation space.

Interestingly, the equivalent manual assignment operation, such as setting b[i] = 0, remains completely unaffected by the defect and successfully clears precisely one single byte as intended. The bug is strictly isolated to the default evmasm pipeline and does not impact the newer IR (Intermediate Representation) pipeline, nor does it require the Solidity optimizer to be active for the anomalous behavior to manifest. According to the development team, the affected code path predates the very first publicly released version of Solidity, meaning the flaw has silently existed in the compiler infrastructure for years.

To gauge the real-world impact of the discovery, developers conducted a comprehensive scan of the Sourcify database, analyzing roughly 870,000 verified smart contract compilations for the specific source code pattern capable of triggering the bug. The analysis revealed that the vulnerable pattern appeared in a mere 17 compilations, encompassing only two distinct deployed functions. Further security assessments confirmed that neither of those functions was actually exploitable in a malicious or destructive manner; in both cases, the overwritten bytes were either immediately discarded, deleted anyway, or landed safely within unused memory allocations or array lengths that could not possibly expand far enough to reach critical variables. As a result, no deployed smart contracts are currently known to have suffered malicious exploitation or unintended state corruption due to this issue.

Which Contracts Are Affected?

Navigating whether a specific codebase is vulnerable requires meeting a precise set of conditions regarding compiler configurations and source code patterns. A smart contract is only potentially exposed if it was compiled using any version of Solidity up to 0.8.36, relies on the default evmasm pipeline rather than the IR pipeline, and explicitly applies the delete operator to an element of a memory-resident bytes or string data structure.

While checking the compiler settings and source code for the first two conditions is straightforward, evaluating the third condition requires a deep understanding of memory layout mechanics produced by the compiler. Because memory layout management can be intricate, developers who confirm the first two criteria are advised to treat their contracts as potentially affected rather than attempting to prove absolute safety around the memory boundaries. Conversely, if a contract never applies the delete keyword to an individual element of a memory-based byte or string array, it is entirely immune to the defect.

It is also important to clarify the boundaries of what is and is not affected. Only element-level deletions trigger the bug; calling delete b on an entire array invokes a completely separate code path that functions correctly. Furthermore, the array in question must reside in memory. Byte arrays stored in persistent storage operate via an entirely different code path and are completely unaffected, while calldata arrays do not permit the use of delete at all. Arrays whose elements occupy a full 32-byte word natively—such as bytes1[] memory—are also immune, because bytes and string are the sole types that tightly pack elements together without word padding. Strings share the exact internal representation as bytes, meaning that while delete s[i] cannot be compiled directly for a memory string, casting the string to a byte array via delete bytes(s)[i] triggers the identical vulnerability.

Technical Details and Memory Layout Internals

Understanding how the bug physically alters runtime data requires looking closely at how Solidity manages memory allocation. Most values in EVM memory occupy full 32-byte words, but bytes arrays are a prominent exception. A memory byte array consists of a 32-byte length header followed immediately by the raw data bytes packed consecutively without gaps. Solidity allocates memory by continually advancing the free memory pointer, ensuring that consecutive allocations sit flush against one another, separated only by necessary padding.

Memory Byte Array Element Delete Clears Whole Word Bug | Solidity Programming Language

The compiler automatically pads most memory allocations—such as arrays created via new bytes(n) or initialized from string and hex literals—with up to 31 zero bytes of padding at the end, ensuring that any subsequent memory allocation begins cleanly on a fresh word boundary. However, the evmasm pipeline deliberately omits this padding for the results of certain operations, including bytes.concat, string.concat, and the entire suite of ABI encoding functions (abi.encode, abi.encodePacked, abi.encodeWithSelector, abi.encodeWithSignature, and abi.encodeCall). For these unpadded arrays, the data ends precisely where the next memory allocation begins, leaving zero buffer space.

When a memory value is narrower than a full 32-byte word, its positioning depends heavily on its data type. Left-aligned values like bytesN types start at the very first byte of the word, whereas right-aligned values such as integers, addresses, memory pointers, and dynamic array length indicators sit flush against the end of the word, padded with leading zeroes. Because byte array elements are single bytes, byte arrays represent the sole scenario where writing to memory is not natively equivalent to storing a full word. The compiler normally handles this nuance by issuing a single-byte store instruction (mstore8) whenever it recognizes the element width.

The root cause of the vulnerability exists squarely within the code generation path for the delete keyword. While delete x is conceptually designed to assign the zero value of a given type—making delete b[i] logically identical to b[i] = 0—the evmasm pipeline historically failed to share the underlying store implementation between the two operations. While standard element assignment correctly resolves to a precise single-byte store, the delete operation triggers a full 32-byte zero-word store (mstore) starting directly at the offset of the targeted array element.

Because this 32-byte write begins at the specific index of the element rather than aligning with a standard 32-byte word boundary, it spills forward and indiscriminately overwrites adjacent memory. Because the array’s internal length header, which sits prior to the data block, is never touched by this operation, the array retains its original length metadata. Consequently, any subsequent operations that read from the array—such as computing cryptographic hashes via keccak256, performing ABI encoding routines, or returning data to an external caller—will process the silently corrupted byte sequence without throwing errors or triggering runtime reverts. The IR pipeline, by contrast, correctly routes delete b[i] through the proper single-byte assignment path, rendering it immune to the bug.

Real-World Manifestation and Demonstration

To illustrate how the bug impacts runtime execution, consider a demonstration contract featuring two functions designed to expose both internal array corruption and overflow corruption into adjacent memory structures. In the first scenario, a function declares a short memory byte array containing five bytes and invokes delete d[0] on the first element. Rather than clearing only the byte at index zero, the generated EVM bytecode issues a 32-byte zero write starting at index zero, wiping out the entire array content from index zero to index four.

In the second scenario, a function allocates a 32-byte memory array with no padding and immediately declares a uint256 variable initialized to its maximum possible value (type(uint256).max) right after it in memory. When delete b[31] is executed on the final element of the array, the 32-byte zero write spills 31 bytes past the end of the array and directly into the memory space occupied by the adjacent integer. Because integers are stored in memory with their most significant bytes placed first, the 31 most significant bytes of the integer are wiped clean, leaving only the single least significant byte intact. Consequently, a variable intended to hold the maximum possible integer value unexpectedly evaluates to 255 at runtime.

Severity Assessment and Remediation

Assessing the overall risk profile of the vulnerability highlights both the insidious nature of compiler bugs and the practical safeguards protecting deployed applications. The compiler emits no warnings when compiling vulnerable code, and the EVM does not encounter an out-of-gas or structural revert when executing the corrupted writes. This lack of explicit failure signals can make debugging exceptionally difficult if tests do not explicitly verify byte-level array contents after deletion operations.

Despite the low practical likelihood of exploitation—underscored by the minimal occurrences found across verified source code databases—the severity of silent memory corruption warranted immediate action from the Solidity core team. The vulnerability has been fully addressed and resolved in Solidity version 0.8.37. The implementation-level fix modifies the compiler’s delete code path to properly consult the element width in the same manner as standard assignment operations. When clearing an element of a packed byte array, the compiler now correctly emits a single-byte store instruction rather than a full-word store, ensuring total behavioral parity between delete b[i] and b[i] = 0 across all compilation pipelines.

Developers maintaining smart contracts written in Solidity 0.8.36 or earlier are strongly advised to upgrade their compiler toolchains to version 0.8.37 or later. Projects that cannot immediately upgrade should thoroughly audit their codebases to identify and refactor any instances where the delete keyword is applied to individual elements of memory-resident byte arrays or string values, replacing them with explicit zero-assignment statements or migrating workloads to the IR compilation pipeline.

Tagged:

Leave a Reply

Your email address will not be published. Required fields are marked *