Snowbridge V2 Audit Fixes#8240
Merged
acatangiu merged 37 commits intoparitytech:masterfrom May 5, 2025
Merged
Conversation
* optimize sparse bitmap * update methods * adds docs
… Queue v2 (#19) * use ExternalConsensusLocationsConverterFor instead of EthereumLocationsConverterFor * use AccountId
* better unique message id * use blake2_256 * Revert "use blake2_256" This reverts commit 78b7326.
* fix call indexes * update call indexes
* Increase nonce before processing * Fix the call index
acatangiu
reviewed
Apr 23, 2025
bridges/snowbridge/primitives/inbound-queue/src/v2/converter.rs
Outdated
Show resolved
Hide resolved
bridges/snowbridge/primitives/inbound-queue/src/v2/converter.rs
Outdated
Show resolved
Hide resolved
Co-authored-by: Adrian Catangiu <adrian@parity.io>
Co-authored-by: Adrian Catangiu <adrian@parity.io>
Co-authored-by: Adrian Catangiu <adrian@parity.io>
vgeddes
approved these changes
Apr 30, 2025
Contributor
|
Deduplicate some tests in claravanstaden#25 |
* Credit to reward address * convert account (#18) * Fallback reward to relayer * Cleaup * Improve inbound tests --------- Co-authored-by: Vincent Geddes <117534+vgeddes@users.noreply.github.com>
franciscoaguirre
approved these changes
May 2, 2025
Co-authored-by: Francisco Aguirre <franciscoaguirreperez@gmail.com>
…snowbridge-v2-audit-fixes
auto-merge was automatically disabled
May 5, 2025 11:47
Head branch was pushed to by a user without write access
Merged
via the queue into
paritytech:master
with commit May 5, 2025
632eed9
242 of 249 checks passed
|
Created backport PR for
Please cherry-pick the changes locally and resolve any conflicts. git fetch origin backport-8240-to-stable2503
git worktree add --checkout .worktree/backport-8240-to-stable2503 backport-8240-to-stable2503
cd .worktree/backport-8240-to-stable2503
git reset --hard HEAD^
git cherry-pick -x 632eed96b4560a45ce41c6f6bedec9a4c745a604
git push --force-with-lease |
acatangiu
added a commit
that referenced
this pull request
May 9, 2025
Backport #8240 into `stable2503` from claravanstaden. See the [documentation](https://github.com/paritytech/polkadot-sdk/blob/master/docs/BACKPORT.md) on how to use this bot. <!-- # To be used by other automation, do not modify: original-pr-number: #${pull_number} --> --------- Co-authored-by: Clara van Staden <claravanstaden64@gmail.com> Co-authored-by: Adrian Catangiu <adrian@parity.io>
castillax
pushed a commit
that referenced
this pull request
May 12, 2025
This PR contains the following fixes after the Snowbridge V2 audit issue were found. ## Failure to advance nonce on error allows repeated execution of failed events without relayer incentives (issue 1) _Severity: Critical_ In `bridges/snowbridge/pallets/inbound-queue-v2/src/lib.rs:216`, the submit extrinsic allows submission of inbound events originating from Ethereum. These events are decoded into messages and processed by the process_message function. However, if the send_xcm operation fails, the transaction reverts without storing the nonce or registering rewards. Consequently, the same inbound message remains unmarked and eligible for re-execution. This creates multiple risks: the same message can be replayed multiple times, potentially in varying orders and at a specific timing decided by the relayer under diff erent conditions, potentially causing unintended state transitions. Additionally, since rewards are only registered upon successful execution, relayers are incentivized to reorder messages to maximize successful transactions, potentially at the expense of the user and system fairness. **Recommendation** We recommend storing the nonce and reward relayers also in case the send_xcm operation fails. **Snowbridge notes:** We dispute this finding, because the inbound-queue can never replay messages because it checks the nonce. Also, by allowing send_xcm to fail without reverting the whole transaction, we are essentially killing the message and preventing it from being retried at a later time. This will lead to a loss of user funds. Message sends can fail for a number of reasons, like if the HRMP queues between parachains are full, and that's something we don't have control over. So we need to allow processing a message again if the send XCM fails. We did however move setting the nonce to before the send XCM method, even though it does not make a difference functionally, it is better semantically. ## Reward misallocation due to ignored `reward_address` field (issue 5) _Severity: Major_ In `bridges/snowbridge/pallets/outbound-queue-v2/src/lib.rs:374`, the process_delivery_receipt function processes a DeliveryReceipt parameter. However, while the function correctly validates the gateway and nonce fields, it disregards the `reward_address` field contained within the DeliveryReceipt structure. As a result, delivery rewards are always assigned to the transaction sender rather than to the beneficiary specified by the `reward_address`. **Recommendation** We recommend validating and utilizing all fields of the DeliveryReceipt structure to correctly allocate rewards. ## Non-sequential call indexes in systemv2 pallet (issue 15) _Severity: Informational_ In `bridges/snowbridge/pallets/system-v2/src/lib.rs`, the call indexes for extrinsics are inconsistently assigned. Specifically, while indexes 0, 3, and 4 are implemented, indexes 1 and 2 remain undefined. Although this does not currently pose a functional or security risk, the non-sequential indexing undermines the clarity and consistency of the codebase. In the context of a newly developed pallet, maintaining orderly call indexes aids in readability, developer experience, and future maintainability. **Recommendation:** We recommend assigning sequential call indexes to all implemented extrinsics within the `systemv2` pallet. --- ## Incorrect NatSpec in register_token function (issue 17) _Severity: Informational_ In `bridges/snowbridge/pallets/system-v2/src/lib.rs:182`, the `register_token` function is documented to include a `fee` parameter, but no such parameter is actually used in the function. **Recommendation:** We recommend updating the NatSpec documentation to accurately reflect the function parameters and behavior, removing any reference to a non-existent fee. --- ## Potentially confusing message hashing implementation (issue 21) _Severity: Informational_ In `bridges/snowbridge/pallets/system-v2/src/lib.rs:230-235`, the `Message` struct includes an id field, which is intended to store the `blake2_256` hash of the message itself. However, during the hash computation, the `id` field is first initialized with its default value. The hash is then calculated over the struct containing this default `id`, and only afterward is the computed hash assigned back to the id field. This approach, while functional, introduces complexity for external systems or tools attempting to verify the message. Verifiers must replicate this specific sequence: extract the `id` value from the message, reset the `id` field in the struct to its default value, compute the hash, and then compare it against the originally extracted `id`. **Recommendation:** We recommend implementing a wrapper data structure over the Message that would contain its hash as a separate member, not part of the Message itself. **Snowbridge notes:** We used unique_id to generate an ID instead. --- ## Optimize SparseBitmap (issue 22) _Severity: Informational_ In `bridges/snowbridge/primitives/core/src/sparse_bitmap.rs:9`, the mapping from buckets to masks is declared, used to define the SparseBitmap structure. The type of keys in this mapping is u128. Then, within lines 22-27, the function `compute_bucket_and_mask` is defined, which accepts a parameter index of type u128. This function is called both from get and set functions, which are called only with nonces of type `u64`. The type of the parameter index can be replaced by `u64`. The function compute_bucket_and_mask computes the index of the corresponding bucket by dividing the index by 128, so the value range of the output is less than the value range of the input. Hence, the return type of the function, as well as the type of keys in the mapping declared on line 9, can be declared as `u64`. Currently, the Rust implementation of SparseBitmap utilizes 128 times more space than it could. **Recommendation:** We recommend optimizing the storage consumption by adopting `u64` as the types of keys in the internal mappings of both Rust and Solidity implementations of SparseBitmap. --- ## Misleading implementation details (issue 25) _Severity: Informational_ In `bridges/snowbridge/primitives/inbound-queue/src/lib.rs:23`, the commentary states that the structure `EthereumLocationsConverterFor` is deprecated. However, it is still used in the V2 source file: bridges/snowbridge/primitives/inbound-queue/src/v2/converter.rs. **Recommendation:** We recommend resolving the aforementioned misleading implementation details. --- --------- Co-authored-by: Ron <yrong1997@gmail.com> Co-authored-by: Vincent Geddes <117534+vgeddes@users.noreply.github.com> Co-authored-by: Adrian Catangiu <adrian@parity.io> Co-authored-by: Francisco Aguirre <franciscoaguirreperez@gmail.com>
alstjd0921
pushed a commit
to bifrost-platform/polkadot-sdk
that referenced
this pull request
Aug 14, 2025
Backport paritytech#8240 into `stable2503` from claravanstaden. See the [documentation](https://github.com/paritytech/polkadot-sdk/blob/master/docs/BACKPORT.md) on how to use this bot. <!-- # To be used by other automation, do not modify: original-pr-number: #${pull_number} --> --------- Co-authored-by: Clara van Staden <claravanstaden64@gmail.com> Co-authored-by: Adrian Catangiu <adrian@parity.io>
alvicsam
pushed a commit
that referenced
this pull request
Oct 17, 2025
This PR contains the following fixes after the Snowbridge V2 audit issue were found. ## Failure to advance nonce on error allows repeated execution of failed events without relayer incentives (issue 1) _Severity: Critical_ In `bridges/snowbridge/pallets/inbound-queue-v2/src/lib.rs:216`, the submit extrinsic allows submission of inbound events originating from Ethereum. These events are decoded into messages and processed by the process_message function. However, if the send_xcm operation fails, the transaction reverts without storing the nonce or registering rewards. Consequently, the same inbound message remains unmarked and eligible for re-execution. This creates multiple risks: the same message can be replayed multiple times, potentially in varying orders and at a specific timing decided by the relayer under diff erent conditions, potentially causing unintended state transitions. Additionally, since rewards are only registered upon successful execution, relayers are incentivized to reorder messages to maximize successful transactions, potentially at the expense of the user and system fairness. **Recommendation** We recommend storing the nonce and reward relayers also in case the send_xcm operation fails. **Snowbridge notes:** We dispute this finding, because the inbound-queue can never replay messages because it checks the nonce. Also, by allowing send_xcm to fail without reverting the whole transaction, we are essentially killing the message and preventing it from being retried at a later time. This will lead to a loss of user funds. Message sends can fail for a number of reasons, like if the HRMP queues between parachains are full, and that's something we don't have control over. So we need to allow processing a message again if the send XCM fails. We did however move setting the nonce to before the send XCM method, even though it does not make a difference functionally, it is better semantically. ## Reward misallocation due to ignored `reward_address` field (issue 5) _Severity: Major_ In `bridges/snowbridge/pallets/outbound-queue-v2/src/lib.rs:374`, the process_delivery_receipt function processes a DeliveryReceipt parameter. However, while the function correctly validates the gateway and nonce fields, it disregards the `reward_address` field contained within the DeliveryReceipt structure. As a result, delivery rewards are always assigned to the transaction sender rather than to the beneficiary specified by the `reward_address`. **Recommendation** We recommend validating and utilizing all fields of the DeliveryReceipt structure to correctly allocate rewards. ## Non-sequential call indexes in systemv2 pallet (issue 15) _Severity: Informational_ In `bridges/snowbridge/pallets/system-v2/src/lib.rs`, the call indexes for extrinsics are inconsistently assigned. Specifically, while indexes 0, 3, and 4 are implemented, indexes 1 and 2 remain undefined. Although this does not currently pose a functional or security risk, the non-sequential indexing undermines the clarity and consistency of the codebase. In the context of a newly developed pallet, maintaining orderly call indexes aids in readability, developer experience, and future maintainability. **Recommendation:** We recommend assigning sequential call indexes to all implemented extrinsics within the `systemv2` pallet. --- ## Incorrect NatSpec in register_token function (issue 17) _Severity: Informational_ In `bridges/snowbridge/pallets/system-v2/src/lib.rs:182`, the `register_token` function is documented to include a `fee` parameter, but no such parameter is actually used in the function. **Recommendation:** We recommend updating the NatSpec documentation to accurately reflect the function parameters and behavior, removing any reference to a non-existent fee. --- ## Potentially confusing message hashing implementation (issue 21) _Severity: Informational_ In `bridges/snowbridge/pallets/system-v2/src/lib.rs:230-235`, the `Message` struct includes an id field, which is intended to store the `blake2_256` hash of the message itself. However, during the hash computation, the `id` field is first initialized with its default value. The hash is then calculated over the struct containing this default `id`, and only afterward is the computed hash assigned back to the id field. This approach, while functional, introduces complexity for external systems or tools attempting to verify the message. Verifiers must replicate this specific sequence: extract the `id` value from the message, reset the `id` field in the struct to its default value, compute the hash, and then compare it against the originally extracted `id`. **Recommendation:** We recommend implementing a wrapper data structure over the Message that would contain its hash as a separate member, not part of the Message itself. **Snowbridge notes:** We used unique_id to generate an ID instead. --- ## Optimize SparseBitmap (issue 22) _Severity: Informational_ In `bridges/snowbridge/primitives/core/src/sparse_bitmap.rs:9`, the mapping from buckets to masks is declared, used to define the SparseBitmap structure. The type of keys in this mapping is u128. Then, within lines 22-27, the function `compute_bucket_and_mask` is defined, which accepts a parameter index of type u128. This function is called both from get and set functions, which are called only with nonces of type `u64`. The type of the parameter index can be replaced by `u64`. The function compute_bucket_and_mask computes the index of the corresponding bucket by dividing the index by 128, so the value range of the output is less than the value range of the input. Hence, the return type of the function, as well as the type of keys in the mapping declared on line 9, can be declared as `u64`. Currently, the Rust implementation of SparseBitmap utilizes 128 times more space than it could. **Recommendation:** We recommend optimizing the storage consumption by adopting `u64` as the types of keys in the internal mappings of both Rust and Solidity implementations of SparseBitmap. --- ## Misleading implementation details (issue 25) _Severity: Informational_ In `bridges/snowbridge/primitives/inbound-queue/src/lib.rs:23`, the commentary states that the structure `EthereumLocationsConverterFor` is deprecated. However, it is still used in the V2 source file: bridges/snowbridge/primitives/inbound-queue/src/v2/converter.rs. **Recommendation:** We recommend resolving the aforementioned misleading implementation details. --- --------- Co-authored-by: Ron <yrong1997@gmail.com> Co-authored-by: Vincent Geddes <117534+vgeddes@users.noreply.github.com> Co-authored-by: Adrian Catangiu <adrian@parity.io> Co-authored-by: Francisco Aguirre <franciscoaguirreperez@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR contains the following fixes after the Snowbridge V2 audit issue were found.
Failure to advance nonce on error allows repeated execution of failed events without relayer incentives (issue 1)
Severity: Critical
In
bridges/snowbridge/pallets/inbound-queue-v2/src/lib.rs:216, the submit extrinsic allows submission of inbound events originating from Ethereum. These events are decoded into messages and processed by the process_message function.However, if the send_xcm operation fails, the transaction reverts without storing the nonce or registering rewards. Consequently, the same inbound message remains unmarked and eligible for re-execution.
This creates multiple risks: the same message can be replayed multiple times, potentially in varying orders and at a specific timing decided by the relayer under diff erent conditions, potentially causing unintended state transitions.
Additionally, since rewards are only registered upon successful execution, relayers are incentivized to reorder messages to maximize successful transactions, potentially at the expense of the user and system fairness.
Recommendation
We recommend storing the nonce and reward relayers also in case the send_xcm operation fails.
Snowbridge notes:
We dispute this finding, because the inbound-queue can never replay messages because it checks the nonce. Also, by allowing send_xcm to fail without reverting the whole transaction, we are essentially killing the message and preventing it from being retried at a later time. This will lead to a loss of user funds. Message sends can fail for a number of reasons, like if the HRMP queues between parachains are full, and that's something we don't have control over. So we need to allow processing a message again if the send XCM fails. We did however move setting the nonce to before the send XCM method, even though it does not make a difference functionally, it is better semantically.
Reward misallocation due to ignored
reward_addressfield (issue 5)Severity: Major
In
bridges/snowbridge/pallets/outbound-queue-v2/src/lib.rs:374, the process_delivery_receipt function processes a DeliveryReceipt parameter.However, while the function correctly validates the gateway and nonce fields, it disregards the
reward_addressfield contained within the DeliveryReceipt structure.As a result, delivery rewards are always assigned to the transaction sender rather than to the beneficiary specified by the
reward_address.Recommendation
We recommend validating and utilizing all fields of the DeliveryReceipt structure to correctly allocate rewards.
Non-sequential call indexes in systemv2 pallet (issue 15)
Severity: Informational
In
bridges/snowbridge/pallets/system-v2/src/lib.rs, the call indexes for extrinsics are inconsistently assigned.Specifically, while indexes 0, 3, and 4 are implemented, indexes 1 and 2 remain undefined.
Although this does not currently pose a functional or security risk, the non-sequential indexing undermines the clarity and consistency of the codebase. In the context of a newly developed pallet, maintaining orderly call indexes aids in readability, developer experience, and future maintainability.
Recommendation:
We recommend assigning sequential call indexes to all implemented extrinsics within the
systemv2pallet.Incorrect NatSpec in register_token function (issue 17)
Severity: Informational
In
bridges/snowbridge/pallets/system-v2/src/lib.rs:182, theregister_tokenfunction is documented to include afeeparameter, but no such parameter is actually used in the function.Recommendation:
We recommend updating the NatSpec documentation to accurately reflect the function parameters and behavior, removing any reference to a non-existent fee.
Potentially confusing message hashing implementation (issue 21)
Severity: Informational
In
bridges/snowbridge/pallets/system-v2/src/lib.rs:230-235, theMessagestruct includes an id field, which is intended to store theblake2_256hash of the message itself.However, during the hash computation, the
idfield is first initialized with its default value. The hash is then calculated over the struct containing this defaultid, and only afterward is the computed hash assigned back to the id field.This approach, while functional, introduces complexity for external systems or tools attempting to verify the message. Verifiers must replicate this specific sequence: extract the
idvalue from the message, reset theidfield in the struct to its default value, compute the hash, and then compare it against the originally extractedid.Recommendation:
We recommend implementing a wrapper data structure over the Message that would contain its hash as a separate member, not part of the Message itself.
Snowbridge notes:
We used unique_id to generate an ID instead.
Optimize SparseBitmap (issue 22)
Severity: Informational
In
bridges/snowbridge/primitives/core/src/sparse_bitmap.rs:9, the mapping from buckets to masks is declared, used to define the SparseBitmap structure. The type of keys in this mapping is u128.Then, within lines 22-27, the function
compute_bucket_and_maskis defined, which accepts a parameter index of type u128. This function is called both from get and set functions, which are called only with nonces of typeu64. The type of the parameter index can be replaced byu64.The function compute_bucket_and_mask computes the index of the corresponding bucket by dividing the index by 128, so the value range of the output is less than the value range of the input. Hence, the return type of the function, as well as the type of keys in the mapping declared on line 9, can be declared as
u64.Currently, the Rust implementation of SparseBitmap utilizes 128 times more space than it could.
Recommendation:
We recommend optimizing the storage consumption by adopting
u64as the types of keys in the internal mappings of both Rust and Solidity implementations of SparseBitmap.Misleading implementation details (issue 25)
Severity: Informational
In
bridges/snowbridge/primitives/inbound-queue/src/lib.rs:23, the commentary states that the structureEthereumLocationsConverterForis deprecated. However, it is still used in the V2 source file: bridges/snowbridge/primitives/inbound-queue/src/v2/converter.rs.Recommendation:
We recommend resolving the aforementioned misleading implementation details.