Skip to content

Conversation

R9295
Copy link
Collaborator

@R9295 R9295 commented Sep 1, 2025

This PR introduces a fuzzing harness for pallet-domains, encoding invariants and valid user-processes when fuzzing for maximum impact.

Please note that the changes seem large due to the use of the macro to make functions public conditionally.

Code contributor checklist:

Copy link

🛡️ Immunefi PR Reviews

We noticed that your project isn't set up for automatic code reviews. If you'd like this PR reviewed by the Immunefi team, you can request it manually using the link below:

🔗 Send this PR in for review

Once submitted, we'll take care of assigning a reviewer and follow up here.

@jfrank-summit
Copy link
Member

@R9295 thank you for this PR! It will likely not get fully reviewed until early next week. In the meantime could you please cleanup the commit history. See our contribution guide for what we are expecting. Thanks!

Copy link
Member

@teor2345 teor2345 left a comment

Choose a reason for hiding this comment

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

Thank you for this PR, it's an important part of our security testing.

I've made a large number of comments, but most of them are code style nitpicks (starting with Nit:). Feel free to ignore those, one of the developers can do them later.

The most important revisions are:

  • adding comments to new functions and structs, so reviewers know what they're meant to do
  • reducing the size of the PR by doing re-exports in the standard Rust way

"pallet-subspace/runtime-benchmarks",
"schnorrkel",
"hex-literal",
Copy link
Member

Choose a reason for hiding this comment

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

Nit: please keep dependencies in alphabetical order, and add any new dependencies in order
(This was likely an accidental revert in a merge commit.)

domain-pallet-executive = {workspace = true, optional = true}
pallet-timestamp = {workspace = true, optional = true}
pallet-block-fees = {workspace = true, optional = true}
prop-test = {workspace = true, optional = true}
Copy link
Member

Choose a reason for hiding this comment

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

This dependency is not part of the fuzz feature. This seems like a copy-paste mistake?

@@ -35,6 +35,12 @@ sp-subspace-mmr.workspace = true
sp-version = { workspace = true, features = ["serde"] }
subspace-core-primitives.workspace = true
subspace-runtime-primitives.workspace = true
domain-pallet-executive = {workspace = true, optional = true}
Copy link
Member

@teor2345 teor2345 Sep 8, 2025

Choose a reason for hiding this comment

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

A new section would make this clearer:

Suggested change
domain-pallet-executive = {workspace = true, optional = true}
# fuzz feature optional dependencies
domain-pallet-executive = {workspace = true, optional = true}

@@ -0,0 +1,216 @@
// Copyright 2025 Security Research Labs GmbH
Copy link
Member

Choose a reason for hiding this comment

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

The rest of the crate is 0BSD, do you mind updating this copyright to match?
https://opensource.org/license/0bsd

I also have the same licence ask for the fuzzing crate.

use sp_core::H256;
use sp_domains::{DomainId, OperatorId};
use sp_runtime::traits::One;

Copy link
Member

Choose a reason for hiding this comment

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

Nit: Most of the crate uses a single import block. This might require a rustfmt run after this change.

Suggested change

println!("skipping NominateOperator");
continue;
}
let old_amount = amount.min(&21);
Copy link
Member

Choose a reason for hiding this comment

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

What does this do, and why is it needed?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good catch, it should be amount.max

println!("skipping WithdrawStake");
continue;
}
// TODO:
Copy link
Member

Choose a reason for hiding this comment

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

Nit: remove this or complete it?

.1;
let slash_reason = match slash_reason % 2 {
0 => SlashedReason::InvalidBundle(0),
_ => SlashedReason::BadExecutionReceipt(H256::from([0u8; 32])),
Copy link
Member

Choose a reason for hiding this comment

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

Nit: I try to avoid wildcard matches, so the compiler will tell us that we need to update them if the surrounding code changes. What we really want is a compiler error when there is a new SlashedReason.

The standard way of doing it would be:

  • add an enum without fields that matches each existing SlashedReason, and converts to a dummy SlashedReason
  • add a function that converts a u8 to that enum
  • convert from the u8 to the fieldless enum to the dummy SlashedReason here

let config = OperatorConfig {
signing_key: pair.public(),
minimum_nominator_stake,
nomination_tax: sp_runtime::Percent::from_percent(60),
Copy link
Member

Choose a reason for hiding this comment

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

Is this deliberately different from the DomainsConfig above?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good catch, we wanted the fuzzer to mutate these values too. I will update it

.unwrap()
}

fn main() {
Copy link
Member

@teor2345 teor2345 Sep 8, 2025

Choose a reason for hiding this comment

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

This fuzzing harness isn't tested in CI, so any other PR could accidentally break it. We've had similar issues with the runtime benchmarks in the past.

Things to do (edited):

  • before this PR merges, add a single fuzzer run in CI, to make sure it works
  • add it to the rust-all job

Here's how that works for benchmarks:

# Checks benchmark code generation, and runs each benchmark once.
# Fails if code generation fails, benchmarks panic, or generated weights are not valid Rust.
check-runtime-benchmarks:

@teor2345 teor2345 changed the title SRLabs: Intrdouce fuzzing harness for pallet-domains SRLabs: Introduce fuzzing harness for pallet-domains Sep 8, 2025
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.

3 participants