19 Oct 2026, Mon

Critical Solidity Bug Discovered: Memory Byte Array Element Deletion Flaw Prompts 0.8.37 Emergency Release

A critical compiler bug affecting the Solidity code generator has been officially patched in version 0.8.37, following a disclosure through the Ethereum Foundation’s bug bounty program. The vulnerability, which dates back to the earliest pre-release iterations of the language, causes the delete operator to misbehave when applied to individual elements of memory-based bytes and string arrays under the default EVM assembly pipeline. Instead of wiping a single byte clean, the affected generated code executes a full 32-byte zero write, inadvertently wiping out adjacent data either within the array itself or spilling over into subsequent memory allocations.

The vulnerability was brought to light on August 14, 2026, by researcher Shaheen Fazim. According to the internal assessment by the Solidity development team, the bug has been classified with a "low/medium" severity rating. While the technical implications of writing 32 consecutive zero bytes to arbitrary memory addresses are severe, practical exploitation or accidental triggering in production smart contracts requires a very specific convergence of coding patterns and compiler configurations. Crucially, the alternative IR (Intermediate Representation) compilation pipeline remains completely immune to the defect, and the use of the Solidity optimizer is neither required to trigger the bug nor capable of masking its flawed semantics.

Which Contracts Are Affected?

Understanding the scope of the vulnerability requires a careful look at how Solidity manages dynamic memory allocation and variable data types. The issue specifically targets code compiled under the legacy evmasm pipeline across all Solidity versions up to and including version 0.8.36. For a smart contract to be considered vulnerable, a precise set of criteria must be met simultaneously during execution.

First and foremost, the codebase must utilize the default evmasm compilation pipeline rather than the newer IR pipeline. Second, the developer must explicitly invoke the delete operator on an individual element of a memory-based byte array or string. Entire array deletions—such as calling delete b on a complete array—utilize an entirely distinct code path and execute flawlessly. Furthermore, the targeted array must reside in memory; byte arrays stored persistently in contract storage operate via a completely separate code path and are entirely unaffected, while calldata arrays cannot accept the delete operator at all.

Another nuance involves the data type itself. Arrays whose individual elements naturally occupy a full 32-word block in memory, such as bytes1[] memory, are safe because their structural spacing prevents over-writing. The bug is strictly confined to packed types like bytes and string where multiple elements share space within packed word structures. While direct index deletion like delete s[i] for a memory string is forbidden by the compiler, developers frequently cast strings to byte arrays via bytes(s)[i] to bypass the restriction, inadvertently exposing themselves to the vulnerability.

Technical Details

To comprehend why the compiler produces such destructive output, one must examine the fundamental architectural differences between how Solidity handles full-word data storage versus packed byte arrays in memory. In Ethereum’s execution environment, most values allocated in memory occupy clean, 32-byte words. Dynamic byte arrays (bytes) diverge from this standard norm by allocating a 32-byte length field followed immediately by raw data packed back-to-back without internal gaps or padding.

Solidity allocates memory sequentially by advancing a global free memory pointer, ensuring that consecutive allocations sit flush against one another. To maintain clean 32-byte word boundaries for subsequent allocations, the compiler typically injects up to 31 zero bytes of padding at the end of standard byte arrays, such as those initialized via new bytes(n) or static literals. However, unpadded allocations—including the outputs of bytes.concat, string.concat, and the family of abi.encode functions—terminate precisely where the next memory block begins, leaving zero protective padding.

When a developer assigns a value to a byte array element using standard assignment syntax, such as b[i] = 0, the compiler correctly resolves the element width and issues a single-byte store operation known as mstore8. This ensures that exactly one byte is modified without altering adjacent data. However, the internal code path responsible for handling the delete operator fails to consult the element width. Instead of executing a localized single-byte clear, the evmasm pipeline treats the deletion like a full-word operation, emitting an mstore instruction that dumps 32 zero bytes starting precisely at the index of the targeted element.

Because this massive zero-write initiates at the specific array index rather than adhering to a 32-byte word boundary, it stretches forward across memory. If the deletion occurs near the end of an array, the write easily bleeds past the array’s boundaries and corrupts whatever data structure happens to be allocated immediately afterward in memory.

Analyzing the Impact of Memory Corruption

The consequences of this misbehavior manifest in two distinct ways depending on the location of the targeted index within the array. When corruption occurs entirely inside the array boundaries, every subsequent element following the deleted index—up to a limit of 31 bytes—is forcibly zeroed out. Unless the array happens to be populated exclusively by zeros past that specific point, the contents of the data structure are fundamentally altered upon execution.

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

When the corruption extends beyond the array, the severity depends heavily on whether the array is padded or unpadded. For padded arrays, the trailing padding bytes absorb a portion of the write. Deletions executed near the beginning of a padded array stay safely contained, while deletions targeting the final 31 elements can breach the boundary. The exact reach varies based on array length; arrays whose lengths are clean multiples of 32 lack padding entirely, maximizing the potential for cross-allocation data bleeding.

When these 32 zero bytes overwrite the beginning of a subsequent memory allocation, the structural impact depends on the alignment and data type of the neighboring variable. Left-aligned values lose their most significant bytes, while right-aligned variables such as integer counters, memory pointers, or Ethereum addresses experience the clearing of their leading byte fields. In unpadded arrays, which lack any protective buffer, deleting any of the last 31 elements will invariably breach the neighboring memory allocation, regardless of the array’s total length.

Practical Demonstration

The operational reality of this compiler defect can be observed through a straightforward smart contract implementation designed to isolate both internal and external memory corruption scenarios.

contract C 
    struct S  uint256 x; 

    function clearElement() public pure returns (bytes memory) 
        bytes memory d = hex"0102030405";
        delete d[0];
        return d;
    

    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 and executed under the default evmasm pipeline, clearElement returns a fully zeroed byte array instead of clearing only the initial index. Similarly, in the clearLastElement function, deleting the final index of a 32-byte unpadded array causes the 32-byte zero write to bleed directly into the adjacent memory structure s, overwriting the 31 most significant bytes of a maximum unsigned integer. Consequently, the function returns a truncated value of 255 rather than the expected maximum limit of type(uint256).max. Replacing the delete statements with explicit zero-assignments (d[0] = 0 and b[31] = 0) bypasses the flawed code path, producing the correct execution results across all tested compiler versions.

Severity Assessment and Ecosystem Impact

Despite the alarming technical nature of arbitrary memory overwrites, the real-world risk and overall likelihood of exploitation are calculated to be relatively low. The vulnerability remained undetected throughout the multi-year history of the Solidity programming language, which underscores how rarely developers combine memory byte array element deletions with tight adjacent memory layouts in ways that trigger visible failures.

To measure the broader ecosystem impact, the Ethereum Foundation and security researchers scanned the Sourcify database, which catalogs roughly 870,000 verified smart contract compilations. The analysis revealed that the vulnerable source pattern appeared in a mere 17 distinct compilations, encompassing just two unique deployed functions. Upon closer inspection, neither of those functions was found to be genuinely vulnerable; in both cases, the overwritten data bytes were either immediately discarded or written to unallocated memory regions that posed no security risk to state integrity. No deployed production contracts are currently known to have suffered malicious exploitation or accidental failure as a result of this bug.

Furthermore, the vulnerability does not trigger any compilation warnings, nor does it cause runtime transaction reverts. Instead, it silently corrupts data structures, potentially complicating debugging efforts for developers who rely on unit tests. Because the defect originates entirely within the compiler’s code generation phase, neither adjusting the optimizer settings nor targeting different EVM execution versions provides mitigation.

Recommended Actions and Resolution

The release of Solidity 0.8.37 permanently resolves the issue by unifying the delete path with the standard assignment architecture. The compiler has been updated to check element widths correctly during array deletions, ensuring that operations on packed byte array elements emit the precise single-byte mstore8 instruction rather than a sweeping word-level write.

Smart contract developers utilizing affected compiler versions ranging from the earliest releases up to 0.8.37 are strongly advised to audit their codebases for instances where the delete operator is applied to individual elements of memory-based bytes or string arrays. Projects relying on the legacy evmasm pipeline should upgrade their compiler toolchains to version 0.8.37 immediately, or transition their build pipelines to the fully immune IR compilation backend. Transitioning to the fixed version ensures absolute semantic parity between array element assignments and element deletions, completely eliminating the risk of unintended memory corruption.

By Basiran

Leave a Reply

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