argotorg / argotorg/solidity

Signed-cast overflow inverts version pragma range check

Closed Beginner friendly
#16,803 2 comments 0 reactions 0 assignees View on GitHub
bug :bug: low effort low impact should have
Dominant language
C++
Stars
25.7k
Forks
6.2k
Avg merge
2d 19h
Merged PRs (30d)
29

Description

## Description

The version-pragma range comparison is **inverted** for version components in the range [2^31, 2^32).

Solidity version-pragma components are unsigned and the parser admits any value up to 2^32-1 (it only throws "Integer too large to be used in a version number." at `>= 2^32`, `liblangutil/SemVerHandler.cpp:275`), so a 10-digit component like `3000000000` is a legal pragma. However, the SemVer comparison casts each component to a signed `int` before subtracting:

```cpp
// liblangutil/SemVerHandler.cpp:116-118
didCompare = true;
cmp = static_cast(_version.numbers[i]) - static_cast(version.numbers[i]);
// ^ unsigned component >= 2^31 casts to a NEGATIVE int -> cmp sign flips
```

For a component in [2^31, 2^32) the `static_cast` of the unsigned value wraps to a negative int, so `cmp` gets the wrong sign and the entire range check is inverted.

On a 0.8.35 compiler, `pragma solidity >3000000000.0.0;` — an unsatisfiable constraint, since no compiler is version three-billion — compiles cleanly, while `pragma solidity <3000000000.0.0;` — satisfied by every compiler that will ever exist — is rejected with "Source file requires different compiler version". Components below 2^31 compare correctly (e.g. `>999999999.0.0` is rejected and `<999999999.0.0` is accepted), confirming the cause is the signed-cast overflow rather than the magnitude itself.

Expected: version components are unsigned, so the comparison should be unsigned (or a three-way compare) and the range check should hold for any component the parser accepts.

## Environment

- Compiler version: 0.8.35-develop.2026.5.5+commit.47b9dedd.Linux.g++
- Operating system: Linux Ubuntu Jammy

## Steps to Reproduce

```solidity
pragma solidity >3000000000.0.0; // unsatisfiable, yet COMPILES on 0.8.35
contract C {}

pragma solidity <3000000000.0.0; // always satisfiable, yet REJECTED
contract D {}
```

Control pair below 2^31 (compares correctly): `pragma solidity >999999999.0.0;` is rejected and `pragma solidity <999999999.0.0;` is accepted, proving the inversion is specific to the [2^31, 2^32) overflow range.

Contributor guide

Open the contributing guide

Research direction

Start in liblangutil/SemVerHandler.cpp at lines 116-118 and inspect how version components are compared, then review the parser limit at line 275. Reproduce the issue with the two 3000000000.0.0 pragma examples and add coverage showing that accepted unsigned components preserve the expected range-check results.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp, solidity
Domain
compilers
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
76/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.