IBC time-out packet is fallible
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:
A user on an IBC-connected Chain B sends
2^128-1
coins to Penumbra;mint_note
increases the total supply to2^128-1
.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.A user on an IBC-connected Chain B sends one more coin to Penumbra;
mint_note
increases the total supply to 1.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↗.