argotorg / argotorg/solidity

Standard-json null sources entry throws uncaught type_error

Open
#16,819 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
2d 19h
Merged PRs (30d)
29

Description

## Description

In standard-JSON input, setting a `sources.` entry to `null` or to an empty array `[]` produces an uncaught `nlohmann::json::type_error 305` instead of a clean, typed compiler error.

The guard in `checkKeys` at `libsolidity/interface/StandardCompiler.cpp:458`:

```cpp
if (!_input.empty() && !_input.is_object())
return formatFatalError(..., "\"" + _name + "\" must be an object");
```

short-circuits when `_input.empty()` is true. For `nlohmann::json`, both `null` and `[]` are `empty()`, so the `is_object()` check is skipped and `checkKeys` returns `nullopt`. The non-object value then flows into the per-source loop and reaches the unchecked `operator[]` at `libsolidity/interface/StandardCompiler.cpp:742`:

```cpp
else if (sourceValue["urls"].is_array())
```

which throws `nlohmann::json::type_error 305` ("cannot use operator[] with a string argument with null" / "...with array").

Observed behavior for various `sources.a.sol` values:

| value | result |
|-----------|-----------------------------------------------------------------|
| `42` | clean error: `"a.sol" must be an object` |
| `true` | clean error: `"a.sol" must be an object` |
| `"str"` | clean error: `"a.sol" must be an object` |
| `[1]` | clean error: `"a.sol" must be an object` (non-empty array) |
| `null` | uncaught `nlohmann::type_error 305` (null `operator[]`) |
| `[]` | uncaught `nlohmann::type_error 305` (array `operator[]`) |

Expected: every non-object `sources.` value should produce the typed `"...must be an object"` diagnostic that the guard already emits for primitives and non-empty arrays.

The same upstream empty-bypass affects every caller of `checkKeys` (`checkRootKeys`, `checkSourceKeys`, `checkAuxiliaryInputKeys`, `checkMetadataKeys`, `checkOptimizerKeys`, `checkSettingsKeys`, …). A sweep across settings sub-paths confirms additional observably-exploitable sites where the same upstream hole reaches a different downstream `operator[]`:

| JSON path | `null` | `[]` |
|----------------------------|------------|------------|
| `sources.` | UNCAUGHT | UNCAUGHT |
| `settings` | UNCAUGHT | UNCAUGHT |
| `settings.metadata` | UNCAUGHT | (n/t) |
| `settings.optimizer` | clean | clean |
| `settings.modelChecker` | clean | clean |
| `auxiliaryInput` | clean | (n/t) |

Suggested one-line fix at line 458: replace `!_input.empty()` with `!_input.is_null()` (or simply drop the empty exception and use `if (!_input.is_object())`, letting empty objects pass naturally). This closes all axis-closure sites simultaneously.

## Environment

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

## Steps to Reproduce

Feed either of the following standard-JSON inputs to `solc --standard-json`:

Case A — `null` source entry:

```json
{
"language": "Solidity",
"sources": {
"a.sol": null
}
}
```

Case B — empty-array source entry:

```json
{
"language": "Solidity",
"sources": {
"a.sol": []
}
}
```

Both produce an uncaught `nlohmann::json::type_error 305` rather than the typed `"a.sol" must be an object` diagnostic that the guard emits for `42`, `true`, `"str"`, or `[1]`.

Verifier output:

```
$ bash research/scripts/verify_E086ed5.sh
control sources=42 -> clean ✓
control sources=true -> clean ✓
control sources="str" -> clean ✓
sources=null -> uncaught cannot use operator[] with a string argument with null ✓
sources=[] -> uncaught cannot use operator[] with a string argument with array ✓
sources=[1] -> clean (guard catches non-empty non-object)
OK: StandardCompiler.cpp:458 empty-guard hole + line 742 unchecked operator[]
```

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.