Flow

Sablier
FoundryDeFi
20,000 USDC
View results
Submission Details
Severity: medium
Invalid

The Reorg attack will lead to void of wrong stream

Summary

It can be observed that the stream can be void at any time and also the sender, receiver and approved third party can void the stream.

To void a stream we only take streamId as arguments and processed with the void process.

Vulnerability Details

To void stream the sender , receiver or operator are allowed to call the void function.

function void(uint256 streamId)
external
override
noDelegateCall
notNull(streamId)
notVoided(streamId)
updateMetadata(streamId)
{
// Checks, Effects, and Interactions: void the stream.
_void(streamId);
}
...
/// @dev See the documentation for the user-facing functions that call this internal function.
function _void(uint256 streamId) internal {
// Check: `msg.sender` is either the stream's sender, recipient or an approved third party.
if (msg.sender != _streams[streamId].sender && !_isCallerStreamRecipientOrApproved(streamId)) {
revert Errors.SablierFlow_Unauthorized({ streamId: streamId, caller: msg.sender });
}

As it is evident from the above code that Sender , Receiver and third party are allowed to void the stream. In this case the following scenario could occur and result in void of wrong stream.

  1. User A creates a stream with User B, designating User C as the approved third party.StreamId = 1.

  2. Within the same block, another user initiates a stream with User D, also designating User C as the approved third party. StreamId = 2.

  3. A block re-org occurs, causing a reordering of transactions.As a result, StreamId = 2 is now associated with User B, and StreamId = 1 is now associated with User D.

  4. User C, the approved third party, decides to void the stream with User B, assuming StreamId = 1 still corresponds to User B.

  5. Due to the re-org, voiding StreamId = 1 now affects User D instead of User B.

Impact

The operator or third party will void the incorrect stream, which cannot be restarted once voided. This will require the creation of a new stream, disrupting the existing stream’s continuity.

Tools Used

Manual Review

Recommendations

Use the internal function _verifyStreamSenderRecipient inside void.

diff --git a/src/SablierFlow.sol b/src/SablierFlow.sol
index 55c4103..9fc0c10 100644
--- a/src/SablierFlow.sol
+++ b/src/SablierFlow.sol
@@ -402,7 +402,7 @@ contract SablierFlow is
/// @inheritdoc ISablierFlow
- function void(uint256 streamId)
+ function void(uint256 streamId , address sender , address recipient)
external
override
noDelegateCall
@@ -410,7 +410,9 @@ contract SablierFlow is
notVoided(streamId)
updateMetadata(streamId)
{
// Checks, Effects, and Interactions: void the stream.
+ _verifyStreamSenderRecipient(streamId, sender, recipient);
_void(streamId);
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 10 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Appeal created

0xaman Submitter
10 months ago
inallhonesty Lead Judge
10 months ago
inallhonesty Lead Judge 10 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.