Skip to content

Conversation

@avichalp
Copy link
Collaborator

@avichalp avichalp commented Mar 6, 2025

Summary

This PR addresses issue #458 where malicious users could upload large blobs while declaring smaller sizes in the SDK, resulting in validators storing more data than users are charged for.

Problem

In the current implementation, users first stage data in a validator (either using Iroh's reverse upload or directly). Without proper validation, a user could:

  1. Upload a large blob (e.g., 1GB)
  2. But override the size parameter in the SDK to a smaller value (e.g., 1MB)
  3. Result: User is charged for storing only 1MB while validators are forced to store the full 1GB

Solution

The PR implements size validation at multiple checkpoints throughout the blob lifecycle:

  1. Size Verification During Set Pending: Added a validation check in set_blob_pending that verifies the blob's actual size matches the claimed size before changing status to pending

    if blob.size != size {
        return Err(ActorError::assertion_failed(format!(
            "blob {} size mismatch (expected: {}; actual: {})",
            hash, size, blob.size
        )));
    }
  2. Size Validation During Download: Added validation in the Iroh resolver to ensure downloaded blob size matches the expected size

    if res.local_size + res.downloaded_size != size {
        return Err(anyhow!(
            "downloaded blob size {} does not match expected size {}",
            res.local_size + res.downloaded_size,
            size
        ));
    }
  3. Size Information Propagation: Modified relevant data structures to carry size information throughout the resolution process:

    • Added size to BlobRequest, PendingBlob, and FinalizedBlob structures
    • Updated BlobPoolItem to include size parameter
    • Modified Iroh resolver interfaces to include size validation

Key Changes

  1. Data Structure Updates:

    • Modified BlobRequest to include blob size: (Hash, u64, HashSet<(Address, SubscriptionId, PublicKey)>)
    • Added size field to FinalizedBlob and PendingBlob structures
    • Updated BlobPoolItem to include size information
  2. Function Modifications:

    • Updated get_added_blobs and get_pending_blobs to return blob size
    • Modified set_blob_pending to accept and validate size parameter
    • Updated Iroh resolver to verify downloaded sizes
  3. Validation Points:

    • Blob actor validates size matches during pending status changes
    • Iroh download process validates actual downloaded size matches claimed size

This fix ensures users cannot exploit the system by misrepresenting blob sizes, protecting validators from storing more data than they're compensated for.

Resolves #458

@avichalp avichalp force-pushed the avichalp/size-quorum branch from 46cfc09 to 9f1e487 Compare March 7, 2025 04:53
@avichalp avichalp changed the title feat: blob size validation fix: blob size validation Mar 7, 2025
let blob = blobs
.get(&hash)?
.ok_or_else(|| ActorError::not_found(format!("blob {} not found", hash)))?;
Ok((hash, blob.size, sources))
Copy link
Collaborator Author

@avichalp avichalp Mar 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gets the size of the newly added blobs and return it to Fendermint

let blob = blobs
.get(&hash)?
.ok_or_else(|| ActorError::not_found(format!("blob {} not found", hash)))?;
Ok((hash, blob.size, sources))
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Returns the size of pending blobs to Fendermint.

env.blob_pool.add(BlobPoolItem {
subscriber: blob.subscriber,
hash: blob.hash,
size: blob.size,
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added size in the pool item so it can be checked against the download blob.

/// The return type used when fetching "added" or "pending" blobs.
/// See `get_added_blobs` and `get_pending_blobs` for more information.
type BlobRequest = (Hash, HashSet<(Address, SubscriptionId, PublicKey)>);
type BlobRequest = (Hash, u64, HashSet<(Address, SubscriptionId, PublicKey)>);
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Returns the size of the blob as u64 so that it could be added to the resolution pool

/// The blake3 hash of the blob.
pub hash: Hash,
/// The size of the blob.
pub size: u64,
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FinalizedBlob needs to have size to construct BlobPoolItem again so it can be removed from the pool.

@avichalp avichalp force-pushed the avichalp/size-quorum branch from 1f5a5f0 to 48a1a1a Compare March 7, 2025 22:00
avichalp added 2 commits March 7, 2025 16:12
Signed-off-by: avichalp <hi@avichalp.me>
Signed-off-by: avichalp <hi@avichalp.me>
@avichalp avichalp force-pushed the avichalp/size-quorum branch from 00cac73 to f238fa4 Compare March 7, 2025 23:14
@avichalp avichalp marked this pull request as ready for review March 8, 2025 00:08
@avichalp avichalp requested a review from sanderpick March 8, 2025 00:09
Copy link
Contributor

@sanderpick sanderpick left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

very nice! big improvement. i left one question about changing the toolchain version

use recall_ipld::hamt::{BytesKey, MapKey};

type BlobSourcesResult =
anyhow::Result<Vec<(Hash, u64, HashSet<(Address, SubscriptionId, PublicKey)>)>, ActorError>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

at some point we should make these giant types into structs

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@@ -1,4 +1,4 @@
[toolchain]
channel = "1.81.0"
channel = "1.82.0"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need to do this on this PR? I think CI will need to be changed as well... esp. the docker build and publish

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i will revert this for now

This reverts commit f238fa4.

Signed-off-by: avichalp <hi@avichalp.me>
@avichalp avichalp merged commit 1cfd68d into main Mar 13, 2025
13 checks passed
@avichalp avichalp deleted the avichalp/size-quorum branch March 13, 2025 03:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Blob size vulnerability

2 participants