Assessment reports>Penumbra>High findings>IBC time-out packet is fallible
Category: Coding Mistakes

IBC time-out packet is fallible

High Severity
High Impact
High Likelihood

Description

If a user tries to transfer funds to an external chain via IBC, a time-out packet can be triggered if it is not received by the destination chain in time:

async fn timeout_packet_execute<S: StateWrite>(mut state: S, msg: &MsgTimeout) {
    // timeouts should never fail
    timeout_packet_inner(&mut state, msg)
        .await
        .expect("able to timeout packet");
}

The timeout_packet_inner will end up calling mint_note->increase_token_supply:

async fn increase_token_supply(
    &mut self,
    asset_id: &asset::Id,
    amount_to_add: Amount,
) -> Result<()> {
    let key = state_key::token_supply(asset_id);
    let current_supply: Amount = self.get(&key).await?.unwrap_or(0u128.into());

    tracing::debug!(
        ?current_supply,
        ?amount_to_add,
        ?asset_id,
        "increasing token supply"
    );
    let new_supply = current_supply.checked_add(&amount_to_add).ok_or_else(|| {
        anyhow::anyhow!(
            "overflow updating token {} supply {} with delta {}",
            asset_id,
            current_supply,
            amount_to_add
        )
    })?;

    self.put(key, new_supply);
    Ok(())
}

The issue is that in between when the tokens are transferred and when the time-out packet occurs, it is possible for the token supply to change. A malicious user could transfer in enough tokens that would cause the total supply to overflow when the time-out handler attempts to mint the tokens, triggering a panic and crashing the node.

Impact

A malicious user could cause the node to crash by performing the following steps:

  1. A user on an IBC-connected Chain B sends 2^128-1 coins to Penumbra; mint_note increases the total supply to 2^128-1.

  2. The user then returns the 2^128-1 coins from Penumbra the original chain with a time-out height that is about to expire / already expired, decreasing the total supply to zero.

  3. A user on an IBC-connected Chain B sends one more coin to Penumbra; mint_note increases the total supply to 1.

  4. The time-out handler for Step 2 is called, and mint_note fails as the total supply is now > u128.

Recommendations

An error should be returned instead of triggering a panic.

Remediation

This issue has been acknowledged by Penumbra Labs, and a fix was implemented in commit 15d81099.

Zellic © 2025Back to top ↑