-
Notifications
You must be signed in to change notification settings - Fork 49
Description
Severity: Informational | Likelihood: Low | Impact: Low | Type: Vulnerability
Details
The Redis nonce reservation script returns the sorted-set score (a float) as the nonce, while the Go path rigidly expects an integer string, leading to parse/type failures that stop nonce allocation and block blockchain publishing. An adjacent overflow risk exists when very large pending nonces are cast to int64 during replenishment, potentially wedging publishing in a retry loop.
In the Redis-backed NonceManager, replenishment inserts nonces into a sorted set with the score equal to the nonce (as a float) and the member equal to the nonce value. The reservation Lua script pops the minimum and returns the score as the nonce. The Go code strictly expects the first return element to be a base-10 integer string and parses it with strconv.ParseInt. Under realistic conditions (e.g., very large nonce values, or Redis/RESP variations), the score may be represented as scientific notation or returned as a numeric type instead of a plain string. This triggers a parse/type error, causing GetNonce to fail and blocking blockchain publishing calls that depend on a nonce. Additionally, replenishment casts the pending nonce to int64; if the source nonce exceeds MaxInt64, overflow to negative values can occur, resulting in negative nonces being served and an infinite 'nonce too high' retry loop, further blocking publishing. The issue impacts only the affected node and does not spread.
Exploitation
Scenario 1
A faulty or malicious RPC provider returns a very large pending nonce (e.g., ~1e16). Replenishment stores float scores; ZPOPMIN returns the score formatted in scientific notation; the Go code fails to ParseInt the nonce; GetNonce errors and blockchain publishing is blocked for the node.
Preconditions / Assumptions:
- (a) Gateway uses the Redis-backed NonceManager
- (b) External RPC provider returns an abnormally large pending nonce (~1e16)
- (c) Redis formats the score as scientific notation or a non-integer string
Scenario 2
An RPC provider returns a pending nonce greater than MaxInt64. The replenishment path casts to int64 causing overflow to negative values; negative nonces are served; the publisher repeatedly receives 'nonce too high' rejections and loops, resulting in persistent inability to publish.
Preconditions / Assumptions:
- (a) Gateway uses the Redis-backed NonceManager
- (b) External RPC provider returns a pending nonce greater than MaxInt64
- (c) Replenishment path casts the nonce to int64, enabling overflow to negative
Scenario 3
A Redis server/variant or protocol setting causes the Lua EVAL return for the score to be a numeric type (e.g., float) rather than a string. The Go code's strict string assertion fails, causing nonce allocation errors and blocking blockchain publishing.
Preconditions / Assumptions:
- (a) Gateway uses the Redis-backed NonceManager
- (b) Redis server/variant or protocol setting returns the score from EVAL as a numeric type
- (c) Go client decodes the EVAL result's first element as numeric instead of string
Files impacted
pkg/blockchain/noncemanager/redis/manager.go
Lines 41-57:
-- Move each stale nonce back to available pool
for i, nonce in ipairs(staleNonces) do
redis.call('ZADD', availableKey, nonce, nonce)
end
-- Remove from reserved set
redis.call('ZREMRANGEBYSCORE', reservedKey, '-inf', staleThreshold)
cleanupCount = #staleNonces
end
-- Then, reserve the next available nonce
local result = redis.call('ZPOPMIN', availableKey, 1)
if #result == 0 then
return {nil, cleanupCount}
end
local nonce = result[2]
-- Add it to the reserved set with current timestamp as score for cleanup
redis.call('ZADD', reservedKey, currentTime, nonce)Metadata
Metadata
Assignees
Labels
Type
Projects
Status