We have released Solidity v0.8.37.
This is primarily a bugfix release. It contains three important bugfixes, two rated low/medium severity and one very low, as well as fixes for two smaller miscompilations that are not security issues. On the experimental side, the SSA CFG code generator gets a new stack shuffler that replaces the previous greedy algorithm with a planning one. The release also adds support for the SLOTNUM opcode of the Amsterdam EVM version, removes two experimental features and adds deprecation warnings for several features planned for removal.
Important Bugfixes
Memory Byte Array Element Delete Clears 32 Bytes
When delete was applied to an element of a bytes array in memory, the evmasm pipeline wrote 32 zero bytes starting at that element instead of clearing only the element's byte. The extra zero bytes overwrote up to 31 bytes following the element. Those bytes may belong to later elements of the same array or, when the element sits near the end of the array, to whatever is allocated after it. The same applies to string values converted to bytes. The IR pipeline is not affected, and the analogous assignment b[i] = 0 clears exactly one byte on both pipelines. All versions up to and including 0.8.36 are affected.
The bug was reported by shaheenfazim through the Ethereum Foundation's bug bounty program.
Write-up: Memory Byte Array Element Delete Clears Whole Word Bug
Spill Slot Collision Across Mutual Recursion
The stack-to-memory mover of the IR pipeline works around the EVM's stack depth limit by relocating some local variables to fixed memory offsets, called spill slots. In certain call graph configurations containing mutual recursion, too few slots were reserved, so variables of two different functions could be assigned the same slot even though both could be live at the same time. One variable was then silently overwritten by the other. The bug is closely related to, but distinct from, the Unsound Spill In Mutual Recursion Bug fixed in 0.8.36. Versions 0.7.2 through 0.8.36 are affected when compiling with --via-ir.
The bug was reported by Ng Sze Hon through the Ethereum Foundation's bug bounty program.
Write-up: Spill Slot Collision Across Mutual Recursion Bug
Misordered Named Parameters in require with Custom Errors
In the IR pipeline, the arguments of a custom error constructed with named arguments and passed to require, as in require(condition, MyError({b: 2, a: 1})), were put on the stack in call-site order rather than declaration order. During ABI encoding they could therefore be matched with the wrong parameters and be misinterpreted as values of completely different types. The encoder would produce a structurally valid payload matching the error signature but not reflecting the values used to instantiate the error. Plain revert MyError({...}) statements or any other language construct that allows named parameters are not affected. Only the revert data of a transaction that is already reverting is affected, so the bug has no impact on contract state.
The bug was reported by Carl from Cantina Security through the Ethereum Foundation's bug bounty program.
Write-up: Misordered Named Parameters in require with Custom Errors Bug
Other Miscompilations
Uninitialized Function Pointers in Packed Storage
In the evmasm pipeline, reading an uninitialized internal function pointer from a packed storage slot yielded the wrong value when a variable stored after it in the same slot held a non-zero value. As a consequence, an equality comparison between such a pointer and a default-initialized local function pointer could be false, even though neither had ever been assigned.
The bug only affects the evmasm pipeline and has no effect on function calls made through such pointers. Only the very specific case of packed pointers being compared before ever being assigned produces wrong results. Relying on the equality of internal function pointers is fragile, since their values depend on the exact bytecode and are not stable across contract updates, which is why the compiler already warns about such comparisons and we plan to disallow them in the next breaking release. Triggering the bug requires a very specific usage pattern, and it is reliably detectable through unit testing.
Constants Read in Both Checked and Unchecked Contexts
In the IR pipeline, the code computing the value of a constant was generated once per contract, with either checked or unchecked arithmetic depending on which use site was generated first, and every other use site reused that code. For a constant whose initializer overflows its type, a read in a checked context could therefore miss its required overflow panic, or a read in an unchecked context could revert with a spurious one.
All operands of the affected read are compile-time constants, so the miscompiled result is deterministic, independent of any transaction input and outside of an attacker's influence. Exploiting it would require code that intentionally relies on a constant that always reverts at runtime, or on one that is meant to work inside unchecked blocks but not outside of them, neither of which is a plausible design. The realistic failure mode is a mistake in a contract with insufficient test coverage: the evaluation of the constant reverts with a Panic on the first execution of the affected function, and the missing check yields a fixed wrapped value, both of which would be easily discoverable in testing. Reaching the bug also requires reading the same overflowing constant from both a checked and an unchecked context within one contract, which we expect to be rare in practice.
Notable Features and Changes
SSA CFG Planning Stack Shuffler (Experimental)
The experimental SSA CFG code generator precomputes a stack layout for every point in the program. Its stack shuffler emits the SWAP, DUP, POP and PUSH instructions that get the stack from one layout to the next. Until now the shuffler worked greedily, applying one local step at a time until the target layout was reached. 0.8.37 replaces it with a planning shuffler. The new shuffler first computes an explicit plan that maps the items on the current stack to their positions in the target layout, and then realizes that plan bottom-up. If it gets stuck because an item is out of reach for SWAP or DUP, it compresses the stack to achieve reachability and starts over from a new plan. The more structured approach is easier to maintain, and initial benchmarks show it to be computationally more efficient than the greedy one while producing bytecode of roughly the same size.
The SSA CFG code generator remains experimental and is enabled with --experimental --via-ssa-cfg.
Support for the SLOTNUM Opcode
The Amsterdam EVM version introduces the SLOTNUM opcode (EIP-7843), which returns the beacon chain slot number of the current block. 0.8.37 exposes it in Solidity as block.slotnum of type uint64, and in Yul and inline assembly as the builtin slotnum(). The SMTChecker supports the new member as well. block.slotnum and slotnum() are available only when compiling with --evm-version amsterdam. This is another step towards adding support for all Amsterdam features, which started with 0.8.36 and is still experimental. From Amsterdam on, slotnum is also a reserved identifier in Yul.
Removals and Deprecations
Two features that were restricted to the experimental mode in 0.8.35 are now removed entirely: the Language Server Protocol mode (--lsp) and the Generic Solidity prototype (pragma experimental solidity). The LSP mode was unfinished and unmaintained. We have not abandoned the idea of a language server, but such tooling should be standalone rather than tightly integrated with the compiler, since that integration does not fit our goal of a leaner and more maintainable compiler. Generic Solidity was an early prototype of Core Solidity that attempted to refactor the compiler in place and iterate on the next version of the language within the same codebase. That approach proved neither fast nor flexible enough, and we abandoned it in favor of solcore, a standalone prototype that is better suited to experimentation and will be the base for a future production implementation.
The experimental ABIEncoderV2 pragma now emits a deprecation warning. The pragma has been redundant since pragma abicoder v2 was introduced in 0.7.4 and ABI coder v2 became the default in 0.8.0. It is a leftover from the time when ABI coder v2 was still experimental, so its removal does not require a breaking release. However, many projects still seem to use it, often indirectly through a dependency on older forge-std versions, so we decided to give them a chance to drop it before we proceed with the removal.
Continuing the deprecation work started in 0.8.31, this release adds deprecation warnings for the EVM versions constantinople, petersburg, istanbul and berlin, and for the SMTChecker's BMC engine.
Full Changelog
Important Bugfixes
- Code Generator: Fix delete applied to an element of a bytes array in memory zeroing the whole 32-byte word starting at the element's location instead of only the element's byte.
- Yul IR Code Generation: Encode custom error named parameters in declaration order instead of call-site order when used from a require function.
- Yul Optimizer: Fix too few memory slots being reserved when moving local variables to memory to work around stack-too-deep errors in code containing mutually recursive functions. Variables of two functions that can be active at the same time could be assigned the same slot, silently overwriting one of them while it was still in use.
Language Features
- Custom Storage Layout: Allow signed positive expressions.
- EVM: Support block.slotnum to access the beacon chain slot number of the current block, available since the Amsterdam EVM version (EIP-7843).
- Yul: Introduce builtin slotnum() for the SLOTNUM opcode, available since the Amsterdam EVM version (EIP-7843).
Compiler Features
- Commandline Interface: Remove support for the experimental Language Server Protocol (LSP) mode.
- EVM: Deprecate support for "constantinople", "petersburg", "istanbul" and "berlin" EVM versions.
- Evmasm Optimizer: Improve performance of block deduplicator.
- General: Improve performance throughout the compiler using Boost's flat versions of unordered set and map.
- General: Remove support for the experimental Generic Solidity prototype (pragma experimental solidity).
- General: Replace the current greedy stack shuffler in the experimental SSA CFG pipeline with a planning one.
- SMTChecker: Emit a deprecation warning for the BMC engine.
- SMTChecker: Support block.slotnum.
- Yul Optimizer: LoopInvariantCodeMotion can now move expressions depending on function parameters out of loops.
- Yul Optimizer: UnusedStoreEliminator can now recognize redundant memory and storage operations whose start offset or length is a function parameter.
- Yul Optimizer: Remove the ineffective elimination of unused returndatacopy() operations in simple cases from UnusedStoreEliminator.
Bugfixes
- Code Generator: Fix constants read in both checked and unchecked contexts within one contract getting the checked/unchecked semantics of whichever read was generated first via IR, which could remove a required overflow panic or introduce a spurious one.
- Code Generator: Fix ICE on parenthesized custom error construction in require statement.
- Code Generator: Fix uninitialized internal function pointers being read from a packed storage slot with the wrong value when a subsequent variable in the slot holds a non-zero value.
- Commandline Interface: Reject --model-checker-ext-calls in unsupported input modes instead of silently ignoring it.
- Commandline Interface: Report proper error instead of ICE on non-hex mixed-case address value given via --libraries.
- Standard JSON Interface: Fix the entire output being replaced by a JSONError ("Error writing output JSON.") when an error message quotes a long source line and truncating it splits a multi-byte character.
- Type Checker: Report an unimplemented feature error instead of ICE when a variable of a fixed point type is accessed in inline assembly.
- Yul Optimizer: Fix incorrect removal of returndatacopy() operations referencing a stale result of returndatasize().
Build System
- Update minimum version requirement of Boost to 1.83.0 for Windows build. This matches the minimum version for other systems.
How to Install/Upgrade?
To upgrade to the new version of the Solidity Compiler, please follow the installation instructions available in our documentation. You can download the release from GitHub: v0.8.37.
If you want to build from the source code, do not use the source archives generated automatically by GitHub. Instead, use the solidity_0.8.37.tar.gz source tarball or check out the v0.8.37 tag via git.
And last but not least, we would like to give a big thank you to all the contributors who helped make this release possible!