Pragma abicoder accepts undocumented string literal forms
- Dominant language
- C++
- Stars
- 25.7k
- Forks
- 6.2k
- Avg merge
- 1d 11h
- Merged PRs (30d)
- 21
Description
## Description
The Solidity parser silently accepts string-literal forms (regular string, unicode string, and hex string) as the value argument of `pragma abicoder` and `pragma experimental`, deviating from the documented identifier-only syntax. As a result, `pragma abicoder hex"7631";` is silently treated as `pragma abicoder v1;` (activating the deprecated ABI coder v1), and `pragma experimental hex"414249456e636f6465725632";` activates `ABIEncoderV2` via a hex-decoded identifier.
Mechanism: `libsolidity/parsing/Parser.cpp:240-258` (`parsePragmaDirective`) stores each token's decoded literal value into `literals` (alongside the raw token kind in `tokens`), but the downstream checkers in `libsolidity/analysis/SyntaxChecker.cpp:141-152` (and the equivalent block for `experimental`) compare only `_pragma.literals()[1]` against `{"v1", "v2"}`. Because the scanner decodes `"v1"`, `unicode"v1"`, and `hex"7631"` all to the same string content `"v1"`, the value-only comparison matches identifier and string-literal forms identically. `pragma solidity` uses a separate parser (`parsePragmaVersion`) which DOES validate token structure and properly rejects string-literal forms (`pragma solidity "^0.8.29";` → "Invalid version pragma"), so the validation gap is per-pragma-kind.
Documented syntax (`docs/abi-spec.rst`, `docs/using-the-compiler.rst`, `docs/layout-of-source-files.rst`) only lists the identifier forms `pragma abicoder v1;`, `pragma abicoder v2;`, and `pragma experimental ;`. The string-literal/unicode-literal/hex-literal forms are undocumented.
Empirical behavior:
```
pragma form | result
------------------------------------------------------|----------
pragma abicoder v1; | accepted (documented)
pragma abicoder "v1"; | ACCEPTED (undocumented)
pragma abicoder unicode"v1"; | ACCEPTED (undocumented)
pragma abicoder hex"7631"; | ACCEPTED (undocumented)
pragma abicoder V1; (uppercase) | rejected
pragma abicoder v3; (unknown version) | rejected
pragma experimental ABIEncoderV2; | accepted (documented)
pragma experimental "ABIEncoderV2"; | ACCEPTED (undocumented)
pragma experimental unicode"ABIEncoderV2"; | ACCEPTED (undocumented)
pragma experimental hex"414249456e636f6465725632"; | ACCEPTED (undocumented)
pragma solidity "^0.8.29"; | rejected (separate parser)
```
Audit/UX consequences:
1. **Code-review obfuscation**: `pragma abicoder hex"7631"` is semantically `pragma abicoder v1` but visually opaque. A reviewer may not realize the deprecated ABI coder v1 is being activated.
2. **Tooling discovery**: static analyzers that match `pragma abicoder v[12]` via regex may miss the obfuscated forms.
3. **Documentation mismatch**: the docs document identifier-only syntax; the string-literal forms are undocumented.
Expected: `pragma abicoder` and `pragma experimental` should reject non-identifier value tokens, matching both the documented syntax and the stricter validation already applied to `pragma solidity`. Suggested fix: at `SyntaxChecker.cpp:141-152` (and the experimental-pragma site), additionally require `_pragma.tokens()[1] == Token::Identifier`.
## Environment
- Compiler version: 0.8.35-develop.2026.5.5+commit.47b9dedd.Linux.g++
- Operating system: Linux Ubuntu Jammy
## Steps to Reproduce
Compile each of the following single-file inputs with `solc` and observe that the string-literal / unicode-literal / hex-literal forms are accepted with no diagnostic, despite being undocumented:
```solidity
// abicoder via hex literal — decodes to "v1", silently activates the deprecated ABI coder v1
pragma abicoder hex"7631";
contract C {}
```
```solidity
// abicoder via regular string literal — equivalent to `pragma abicoder v1;`
pragma abicoder "v1";
contract C {}
```
```solidity
// abicoder via unicode string literal — equivalent to `pragma abicoder v1;`
pragma abicoder unicode"v1";
contract C {}
```
```solidity
// experimental via hex literal — decodes to "ABIEncoderV2", silently activates ABIEncoderV2
pragma experimental hex"414249456e636f6465725632";
contract C {}
```
```solidity
// experimental via regular string literal
pragma experimental "ABIEncoderV2";
contract C {}
```
For contrast, the analogous string-literal form for `pragma solidity` IS properly rejected (separate parser), demonstrating the per-pragma-kind asymmetry:
```solidity
pragma solidity "^0.8.29"; // rejected with "Invalid version pragma"
contract C {}
```
Verification script output (`bash research/scripts/verify_E2ae271.sh`):
```
abicoder v1 (control) -> accepted ✓
abicoder "v1" (string literal) -> accepted ✓
abicoder unicode"v1" (unicode literal) -> accepted ✓
abicoder hex"7631" (hex literal=v1) -> accepted ✓
abicoder V1 (uppercase, identifier) -> rejected ✓
abicoder v3 (unknown version) -> rejected ✓
experimental ABIEncoderV2 (control) -> accepted ✓
experimental "ABIEncoderV2" (string literal) -> accepted ✓
experimental unicode"ABIEncoderV2" (unicode) -> accepted ✓
experimental hex"..." (hex literal=ABIEncoderV2) -> accepted ✓
solidity "^0.8.29" (string) properly rejected -> rejected ✓
```
Contributor guide
Assessment
This issue has not been assessed yet.