19 Oct 2026, Mon

Critical Solidity Compiler Bug Discovered: Memory Byte Array Deletions Overwrite Surrounding Data

Introduction and Discovery

On August 14, 2026, developer Shaheen Fazim reported a significant code generation vulnerability in the Solidity programming language through the official Ethereum Foundation bug bounty program. The defect, which predates the very first official release version of Solidity, affects a vast range of compiler versions up to and including version 0.8.36.

The security flaw centers on how the Solidity compiler handles memory-based byte arrays when the delete operator is applied to a specific element. Instead of precisely clearing only the targeted byte, the generated bytecode instructs the Ethereum Virtual Machine (EVM) to write 32 consecutive zero bytes—an entire EVM word—starting directly at the index of the deleted element. This aggressive wipe reaches past the intended single-element scope, clearing up to 31 bytes that follow it. Depending on where the element resides within the array, the overflow can corrupt subsequent entries in the same data structure or even spill over into adjacent memory allocations.

Crucially, this anomalous behavior is entirely isolated to the legacy default evmasm compilation pipeline. Contracts compiled using the modern IR (Intermediate Representation) pipeline remain completely safe and unimpacted by the defect. Furthermore, the presence or absence of the compiler optimizer has no bearing on whether the bug triggers, as the faulty semantics are deeply embedded directly into the foundational codegen stage itself.

Scope, Affected Versions, and Prevalence

The discovery of this long-standing flaw prompted immediate security assessments by the Solidity core team. An extensive review established that every historical release of Solidity up to version 0.8.36 contains the vulnerable code path. The issue was successfully addressed and neutralized in Solidity version 0.8.37.

To determine the real-world exposure of smart contracts deployed across the blockchain ecosystem, developers conducted a large-scale scan of the Sourcify database. Analyzing roughly 870,000 verified source compilations for the precise triggering code pattern, the scan identified only 17 matching compilations. These compilations mapped to just two distinct functions across the entire repository. Further safety analysis revealed that neither of those specific functions was actually vulnerable to malicious data corruption; in both instances, the overwritten bytes were either discarded immediately or landed in unallocated, harmless memory spaces. Consequently, the core team found no evidence of any live, deployed smart contracts actively compromised by this flaw.

Understanding whether a specific smart contract falls into the danger zone requires meeting a precise combination of conditions. A contract is vulnerable if and only if it uses a vulnerable compiler version up to 0.8.36, relies on the default evmasm compilation pipeline, and applies the delete operator directly to an indexed element inside a memory-resident bytes or string array. If an application never invokes delete on an individual element of a memory byte or string array, it remains entirely unaffected.

Technical Internals of Memory Layouts

To fully comprehend why the bug occurs, it is necessary to examine how Solidity manages memory allocation for variable-length data structures. Within the EVM memory space, most data types occupy clean, standardized 32-byte words. Dynamic byte arrays (bytes) and strings represent a notable exception to this rule. In memory, a dynamic byte array consists of a 32-byte length header immediately followed by raw data packed back-to-back without internal gaps.

Solidity allocates memory dynamically by incrementing the global free memory pointer, ensuring that consecutive allocations sit directly flush against one another, separated only by necessary word-boundary padding. When arrays are created using standard constructors like new bytes(n) or initialized from literal strings, the compiler automatically appends up to 31 zero bytes of padding at the conclusion of the data payload. This guarantees that any subsequent memory allocation starts cleanly on a fresh 32-byte word boundary.

However, certain operations—such as bytes.concat, string.concat, and the extensive suite of abi.encode functions—produce unpadded arrays. In these unpadded structures, the data ends precisely where the next memory allocation begins, leaving zero buffer room.

Because byte arrays pack individual elements into single bytes rather than dedicating a full 32-byte word per element, storing values into a memory byte array ordinarily requires specialized handling. For standard assignments like b[i] = 0, the compiler correctly emits a single-byte store instruction (mstore8) when it recognizes the element width. This ensures that only the targeted byte is modified, leaving neighboring data pristine.

The Flawed Delete Path

The root cause of the vulnerability lies in a divergence between how standard assignments and delete operations are handled within the legacy evmasm compilation pipeline. Logically, the operation delete x should assign the default zero value associated with the data type of x. Therefore, executing delete b[i] ought to produce identical machine code and execution behavior to the explicit assignment b[i] = 0.

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

While both operations ultimately attempt to clear data at the designated element’s memory location, the evmasm code generator routes delete b[i] through a completely different internal code path than standard assignments. Instead of utilizing a single-byte store operation, the delete handler executes a full 32-byte word write (mstore) initialized with zeroes.

Because this expansive write begins directly at the index of the targeted array element rather than aligning with the start of a 32-byte word boundary, it stretches across multiple memory bytes. The length header of the array, which sits safely ahead of the data payload, remains untouched, meaning the array preserves its original length property. As a result, subsequent operations relying on the array’s contents—including cryptographic hashing via keccak256, ABI encoding routines, or external function returns—silently process corrupted, zeroed-out data without throwing runtime exceptions.

Examining the Mechanics of Corruption

The extent of the memory corruption depends heavily on the array’s length, its padding status, and the precise index targeted by the delete operation. Corruption manifests in two distinct ways: internally within the array itself and externally beyond the boundaries of the array.

Internal corruption occurs when deleting any of the array’s elements triggers a 32-byte zero-fill that overwrites up to 31 subsequent elements residing further down in the same array. Unless those following elements already consist entirely of zeroes, this operation irrevocably alters the array’s internal state immediately upon execution.

External corruption happens when the deleted element sits near the trailing end of a padded array or anywhere within an unpadded array. Because memory allocations sit directly adjacent to one another, a 32-byte zero write that exceeds the array’s length spills directly into whatever data structure was allocated next in memory.

The consequences for the overwritten neighboring data depend on its alignment and data type. For left-aligned types, the most significant bytes are wiped clean. For right-aligned types such as integers, addresses, or nested pointers, the leading bytes of the target word are cleared. For instance, a minor write can easily strip the upper bytes of a crucial uint256 variable, silently mutilating large numbers into fractions of their intended value while bypassing all compiler warnings and runtime checks.

Practical Demonstration and Real-World Impact

A straightforward contract demonstrates the dangerous mechanics of this vulnerability. Consider a contract featuring a custom structure allocated adjacently in memory alongside a byte array. When a developer invokes a function that allocates a small byte array, deletes the first element using delete d[0], and returns the array, the evmasm pipeline clears not only the initial byte but the subsequent four bytes as well.

Similarly, when deleting the final element of a 32-byte padded array that sits directly beside a maximum-value 256-bit unsigned integer (type(uint256).max), the 32-byte zero write spills 31 bytes into the integer’s storage location. Because integers are stored with their most significant bytes first, the overwrite obliterates the upper 31 bytes of the integer, leaving only the lowest byte intact. Consequently, a variable intended to hold the maximum possible numerical value silently collapses to 255.

Despite the severe nature of the memory corruption, the overall practical risk in real-world decentralized applications remains low. The bug requires a very specific combination of conditions, involves memory manipulation that rarely occurs by accident in standard business logic, and failed to appear in any active, deployed smart contracts during comprehensive ecosystem scans. Nevertheless, because the compiler emits no warnings and the EVM does not trigger runtime reverts, identifying such silent data corruption during routine testing can prove exceptionally difficult.

Resolution and Recommended Actions

The Solidity development team resolved the vulnerability in version 0.8.37. The corrective patch aligns the code generation logic of the delete pathway with the assignment pathway. The compiler now correctly consults the element width when processing delete instructions on packed byte arrays, emitting precise single-byte store instructions (mstore8) instead of sweeping 32-byte word writes. As a result, delete b[i] and b[i] = 0 now execute with identical, secure semantics across both compilation pipelines.

Developers maintaining codebases utilizing Solidity versions up to 0.8.36 are strongly encouraged to upgrade their compiler toolchains to version 0.8.37 or later. Alternatively, projects tied to older compiler versions can migrate their compilation configurations to use the modern IR pipeline, which is entirely immune to the defect, or carefully audit their codebases to ensure that the delete operator is never applied to individual elements within memory-based bytes or string arrays.

Leave a Reply

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