The vulnerability stems from a node crash caused by processing a block with an invalid compressed BLS voting key for a validator. The core issue lies in the unsafe use of .unwrap() or .expect() on the result of the key.uncompress() method, which returns an Option. When uncompress() fails on invalid data, it returns None, and the subsequent unwrap() call triggers a panic, leading to a denial of service.
The analysis of the patch commit e10eaebcd7774e5da6d0ff5e88ed13503474f0ff pinpointed several vulnerable functions:
-
Validators::voting_keys: Located in primitives/src/slots_allocation.rs, this function was the primary source of the panic, containing the validator.voting_key.uncompress().unwrap() call.
-
Validators::hash: The Hash implementation for Validators in the same file was also vulnerable as it called voting_keys_g2(), which in turn called the panicking voting_keys() function. This was a major attack vector, as hashing is a standard operation when receiving new blocks from peers. The BlockQueue::request_block function is a runtime indicator of this vector being triggered.
-
Proof Verification Functions: DoubleVoteProof::verify and TendermintProof::verify_justification also contained direct calls to uncompress().expect(), creating additional vectors for the panic if a malicious proof was provided.
The patch rectifies these issues by replacing the unsafe unwraps with proper error handling, primarily by changing functions like voting_keys to return a Result. It also introduces proactive validation by adding a Validators::validate_keys() function and calling block.verify_header() in the block processing queue to reject malformed blocks early.