argotorg / argotorg/solidity

Pragma abicoder accepts undocumented string literal forms

Open
#16,792 0 comments 0 reactions 1 assignee Claimed by @cameel View on GitHub
bug :bug:
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.