Heimdall-v2 ABCI security patch(v0.4.4)

Summary

Two security bugs in Heimdall’s ABCI layer were reported and confirmed via the Bug Bounty Program. Under specific conditions, these bugs could have allowed a malicious validator to trigger a Denial of Service (DoS) and cause a chain halt.

Both issues have been fixed and a patched release is available.


Root Cause

Issue 1 – Non-deterministic majority selection & vote inflation

The first issue was in the GetMajorityMilestoneProposition function in the ABCI layer.

  1. Vote inflation via repeated keys

    The function maintains a parentHashToVotingPower map.

    The key for this map was derived directly from the proposition data, which meant the same key could be inserted multiple times during a single iteration.

    A faulty validator could exploit this by:

    • Reusing the same parent hash key multiple times with different data,

    • Artificially inflating the voting power for that parent hash,

    • While still being able to vote for different proposition data.

  2. Non-determinism due to map iteration

    The function then iterated over parentHashToVotingPower to determine the majorityParentHash.

    Since Go map iteration order is non-deterministic, if multiple parent hashes had majorityVP, different nodes could pick different majorityParentHash values.

  3. Effect on consensus

    • Nodes that selected a valid majority parent hash would commit the milestone to state.

    • Nodes that selected the inflated parent hash would later fail a PreBlocker check and diverge.

    As a result:

    • Some nodes would obtain a valid pendingMilestone,

    • Others would get nil and attempt to rotate the current span.

    This disagreement on milestone propositions prevents validators from reaching consensus and can ultimately lead to a chain halt.


Issue 2 – Late malformed vote extensions causing halts

The second issue involved late-arriving, malformed vote extensions.

  1. Interaction with PrepareProposal

    • The PrepareProposal handler calls ValidateVoteExtensions.

    • If vote extensions are invalid, ValidateVoteExtensions returns an error and PrepareProposal fails.

    • In our integration, this error propagates into CometBFT, leading to a panic and halting the chain.

  2. Mismatch with Cosmos/Comet semantics

    In Cosmos/Comet:

    • Once the 2/3+ voting power threshold is reached, vote extensions are not revalidated at that stage.

    • They are directly added to the local commit.

    Therefore, a faulty validator can:

    • Send a malformed vote extension late in the precommit phase of the previous block,

    • Have it included in the extended commit,

    • Cause the subsequent PrepareProposal to fail when ValidateVoteExtensions rejects the malformed data.

  3. Concrete example

    If a vote extension includes a side transaction response with an invalid transaction hash:

    • ValidateVoteExtensions will fail,

    • PrepareProposal will return an error,

    • The chain will halt even though 2/3+ voting power was already achieved.


Resolution and Recovery

A patch was released on Wednesday, November 12th, 2025 with Heimdall-v2 tag v0.4.4.

The main changes, implemented in PR #499, are:

  1. Safer vote extension validation

    • ValidateVoteExtensions was refactored and split so that it only returns an error (and thus triggers a Comet panic) when strictly necessary.

    • Late malformed vote extensions no longer cause a chain halt once a block has already achieved the 2/3+ voting power threshold.

  2. Deterministic milestone majority selection

    • Non-determinism in milestone majority selection was removed by:

      • Detecting and failing on duplicate block hashes during the milestone proposition phase, and

      • Ensuring deterministic handling of parent hashes and voting power.

  3. Deployment

    • The patch was first tested on a dedicated devnet.

    • It was then deployed and verified on Amoy and Mainnet nodes.

    • A public release announcement was shared, instructing all validators to upgrade to v0.4.4.