Full Report
The post starts with a small amount of Solidity that crashes the compiler: // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.25; contract A { function a() public pure returns (uint256) { return 1 ** 2; } } Eventually, they traced it down to a line of C++ code on the G++ compiler backend that leads to infinite recursion. This turns out to be a 12 year old bug in G++, an outdated comparison operation in Boost and a small rewrite in C++20 that led to this crash. In C++, there is functionality called overloading where the compiler can choose implementations for operations like equality. A member function has priority over a non-member function to overwrite this. G++ doesn't always follow this rule though. Clang would choose the member function and the G++ issue was reported 12 years ago. In C++20, the spaceship operator () was introduced. This allows for comparisons to be done via overloading as before but will also do the reverse check as well. a=b and code>b=a. This rewrite becomes recursive to do the comparison over and over again if you're not careful. The Boost rational class implemented both a member and a non-member function for operator==. Under C++17, this was safe because of the member vs. non member bug was fixed. However, with C++20 and G++ boost::rational to represent some compile-time constant expressions. Because of this, Solidity inherited the bug mentioned above. To have this happen you had to be using G++ < 14, Boost < 1.75 and C++ enabled for Solidity builds. This crash occurs with any compile-time rational comparisons. Although it's not a security vulnerability, it does show how fragile modern stacks can be. A bug from 2012 led to a broken compiler. Neat!
Analysis Summary
# Vulnerability: G++ and Boost Interaction Causes Solidity Compiler Crash via Infinite Recursion
## CVE Details
- CVE ID: Not Applicable (This is a compiler/library stack instability issue, not a traditional security vulnerability)
- CVSS Score: N/A
- CWE: CWE-674: Uncontrolled Recursion (Applicable to the resultant segmentation fault)
## Affected Systems
- Products: Solidity Compiler (`solc`), G++ Compiler, Boost Library
- Versions:
- G++: Versions prior to 14 (e.g., G++ 11.4)
- Boost: Versions prior to 1.75
- Configuration: Builds must have C++20 enabled (default in recent Solidity builds).
- Configurations: Compiling valid Solidity code involving compile-time rational comparisons (e.g., constant array sizes like `T[0]`, or expressions like `1 ** 2`).
## Vulnerability Description
This issue results from the collision of three factors: a 12-year-old bug in the G++ compiler regarding overload resolution, design choices in the older Boost `rational` class, and the introduction of recursive comparison rewrites in the C++20 standard.
1. **G++ Bug (Pre-v14):** G++ incorrectly prioritizes a specialized non-member `operator==` function over a higher-priority member template `operator==` function during overload resolution (a known issue filed in Bug 53499).
2. **Boost Trapdoor:** Boost's `rational` class (pre-v1.75) implemented both the member and non-member versions of `operator==`. Under C++17, the member function was correctly chosen.
3. **C++20 Interaction:** C++20 introduced implicit rewriting for comparison operators. If a non-member `operator==(T1, T2)` is defined, the reversed comparison `T2 == T1` might call the same function by swapping arguments.
When G++ (pre-v14) is used with C++20 semantics on Boost rational numbers, the outdated system incorrectly resolves the comparison (`rational == 0`) to the non-member overload. This overload, when reversed by C++20 rules, recursively calls itself with swapped arguments, leading to infinite recursion and a stack overflow (segmentation fault) within the compiler backend.
## Exploitation
- Status: Not exploited (Not used for malicious purposes; results in a Denial of Service against the compilation process).
- Complexity: Low (Requires setting up the specific vulnerable dependency versions and compiling valid Solidity code).
- Attack Vector: Local (Requires running the compiler locally or on a build server).
## Impact
- Confidentiality: None
- Integrity: Minor impact on build integrity (Code fails to compile).
- Availability: Denial of Service (DoS) against the compilation service/process.
## Remediation
### Patches
- **Update Boost:** Upgrade the Boost library dependency to version **1.75 or later**.
- **Update G++:** Upgrade the G++ compiler to version **14 or later**.
### Workarounds
- **Disable C++20:** For existing toolchains where immediate dependency updates are not possible, ensure C++20 features are not enabled in the Solidity build configuration, reverting to C++17 semantics (where the overload resolution bug was less likely to trigger this specific infinite recursion path).
## Detection
- **Indicators of Compromise (IOCs):** Segmentation faults or stack overflows occurring during the compilation phase of Solidity source files, specifically when compile-time constant expressions are evaluated.
- **Detection Methods and Tools:** Monitoring build logs for compiler crashes (e.g., `signal: 11 (SIGSEGV)`). This issue is specific to the compiler invocation environment.
## References
- Vendor advisories: (No formal CVE issued, but the incident is documented via related fixes)
- G++ Bug Report: gcc.gnu.org/bugzilla/show_bug.cgi?id=53499
- Boost Rational Fix: github.com/boostorg/rational/issues/43 (Fix implemented in Boost 1.75)
- Related Commit in Solidity: github.com/ethereum/solidity/commit/233a5081835a04939ccf85dfb5286c0b53d23c66 (Indicates the change that might have enabled wider testing against C++20)