Skip to content

fix(jsonrpc): harden RPC/HTTP parameter validation#6828

Open
0xbigapple wants to merge 2 commits into
tronprotocol:release_v4.8.2from
0xbigapple:fix/rpc-param-validation-guards
Open

fix(jsonrpc): harden RPC/HTTP parameter validation#6828
0xbigapple wants to merge 2 commits into
tronprotocol:release_v4.8.2from
0xbigapple:fix/rpc-param-validation-guards

Conversation

@0xbigapple

Copy link
Copy Markdown
Collaborator

What does this PR do?

Adds length/format validation to several JSON-RPC and HTTP address-handling parameters, so
malformed or oversized input is rejected early instead of triggering O(n²) work or leaking
NullPointerException / -32001(json4j default error). Valid input is unaffected.

  1. Commons.decodeFromBase58Check — reject input whose length != 34 before the O(n²) Base58.decode; single funnel for every HTTP address input.
  2. JsonFormat.unescapeBytesSelfType — an invalid Base58 address (null / illegal char) now returns a clear invalid address error instead of a bare ByteString.copyFrom(null) NPE; covers all merge-path address fields.
  3. JsonRpcApiUtil.addressCompatibleToByteArray — cap length at ADDRESS_SIZE + 2 (the +2 keeps 0x-prefixed 21-byte addresses valid) before fromHexString.
  4. JsonRpcApiUtil.parseBlockNumber — tighten MAX_BLOCK_NUM_HEX_LEN 100 → 20, and route the (String, Wallet) overload through the single-arg parser so it also gets the length cap checks.
  5. TronJsonRpcImpl.getStorageAt — bound the storage key and parse it before the contract lookup; a >32-byte key now returns -32602 invalid storage key value instead of -32001 (json4j default error) .
  6. LogFilterWrapper — validate blockHash via the shared JsonRpcApiUtil.hashToByteArray (moved here from TronJsonRpcImpl).
  7. HASH_REGEX accepts only hex digits [0-9a-fA-F], so a non-hex hash is rejected as invalid hash value rather than slipping through to fromHexString.

Why are these changes required?

Several unauthenticated API entry points accepted unbounded / malformed input, causing two classes of problem:

  • Algorithmic-complexity DoSdecodeFromBase58Check fed the whole string into the O(n²)
    Base58.decode; since the cost grows quadratically with length, a single oversized address
    payload (well within the 4 MB body cap) can tie up a request thread far out of proportion to
    its size — a cheap DoS the body cap and rate limiter don't stop. Block-number parsing likewise
    had no effective length bound before the O(n²) BigInteger constructor.
  • Leaked exceptions / unfriendly errors — an invalid address on the merge path returned a
    bare NullPointerException; eth_getStorageAt surfaced a RuntimeException as
    -32001 Data word can't exceed 32 bytes: xxx instead of -32602 invalid params;

This PR has been tested by:

  • Unit Tests
  • Manual Testing

Follow up

Extra details
Representative affected endpoints:

  • JSON-RPC — grouped by the guarded parameter:
    • block number (length 100 → 20):
    • storage index (bounded + parsed as a 32-byte word): eth_getStorageAt
    • block hash (now validated by the shared hashToByteArray)
    • address (length capped at ADDRESS_SIZE + 2)
  • HTTP /wallet/* — all covered by the Base58 length/DoS guard, but they differ on an invalid address:
    • most endpoints pre-convert via Util.getHexAddress and just return {} (e.g. getcontract,`visible=true) — unchanged;
    • endpoints that merge a Base58 field directly (e.g. getaccount, visible=true) get the NPE → invalid address fix;
    • endpoints that resolve the address via Util.getAddress(request) (e.g. getReward) get null for an invalid address and degrade to reward 0.

  Add length/format guards across the JSON-RPC and HTTP address-handling paths so
  malformed or oversized parameters are rejected early with a clear -32602 / error
  response, instead of triggering O(n^2) work or leaking NPE / Internal errors.

  - Commons.decodeFromBase58Check: reject non-34-char input before the O(n^2)
    Base58 decode; single funnel covering every HTTP address input.
  - JsonFormat.unescapeBytesSelfType: turn an invalid Base58 address (null or
    illegal-char) into a clear "invalid address" error instead of a bare
    ByteString.copyFrom(null) NPE; covers all merge-path address fields.
  - JsonRpcApiUtil.addressCompatibleToByteArray: cap length at ADDRESS_SIZE + 2
    (the +2 keeps 0x-prefixed 21-byte addresses valid) before fromHexString.
  - JsonRpcApiUtil.parseBlockNumber: tighten MAX_BLOCK_NUM_HEX_LEN 100 -> 20 and
    route the (String, Wallet) overload through the single-arg parser so it also
    gets the length cap, overflow (longValueExact) and negative checks.
  - TronJsonRpcImpl.getStorageAt: bound the storage key and parse it before the
    contract lookup; wrap DataWord parsing so a >32-byte key
    returns invalid-params instead of an Internal error.
  - Hoist hashToByteArray/HASH_REGEX into JsonRpcApiUtil so LogFilterWrapper's
    blockHash gets the same 32-byte-hash validation.
@github-actions github-actions Bot requested a review from bladehan1 June 9, 2026 04:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant