-
Notifications
You must be signed in to change notification settings - Fork 254
SRLabs: Introduce fuzzing harness for pallet-domains #3693
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
🛡️ Immunefi PR ReviewsWe 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: Once submitted, we'll take care of assigning a reviewer and follow up here. |
@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! |
There was a problem hiding this 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
use sp_core::H256; | ||
use sp_domains::{DomainId, OperatorId}; | ||
use sp_runtime::traits::One; | ||
|
There was a problem hiding this comment.
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.
.1; | ||
let slash_reason = match slash_reason % 2 { | ||
0 => SlashedReason::InvalidBundle(0), | ||
_ => SlashedReason::BadExecutionReceipt(H256::from([0u8; 32])), |
There was a problem hiding this comment.
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
Any update on this PR @R9295? |
@jfrank-summit sorry, I was OOO. On it |
01b42f5
to
20420d7
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is looking good, but I still have some questions about how it works
fuzz = [ | ||
"dep:domain-pallet-executive", | ||
"dep:pallet-timestamp", | ||
"dep:pallet-block-fees", | ||
"dep:sp-externalities", | ||
"dep:sp-keystore", | ||
] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the fuzz
feature requires std
, then please depend on the std
feature here. That way you can replace all(feature = "std", feature = "fuzz")
with feature = "fuzz"
.
This makes the cfg
s easier to maintain.
#[cfg(all(not(test), feature = "std", feature = "fuzz"))] | ||
pub mod tests; | ||
|
||
pub mod block_tree; | ||
pub mod bundle_storage_fund; | ||
pub mod domain_registry; | ||
pub mod extensions; | ||
#[cfg(feature = "fuzz")] | ||
pub mod fuzz_utils; | ||
pub mod migrations; | ||
mod nominator_position; | ||
pub mod runtime_registry; | ||
pub mod staking; | ||
#[cfg(feature = "fuzz")] | ||
pub mod staking_epoch; | ||
#[cfg(not(feature = "fuzz"))] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please replace these duplicate module declarations with:
#[cfg(feature = "fuzz")]
pub use module;
Only declaring modules once makes the code easier to maintain, and provides clearer compiler errors when we rename a module.
use crate::block_tree::{BlockTreeNode, verify_execution_receipt}; | ||
use crate::domain_registry::{DomainConfig, DomainConfigParams, DomainObject}; | ||
use crate::runtime_registry::ScheduledRuntimeUpgrade; | ||
use crate::staking_epoch::do_finalize_domain_current_epoch; | ||
#[cfg(test)] | ||
use crate::block_tree::verify_execution_receipt; | ||
#[cfg(test)] | ||
use crate::domain_registry::{DomainConfig, DomainObject}; | ||
|
||
#[cfg(test)] | ||
use crate::Config; | ||
#[cfg(test)] | ||
use crate::tests::pallet_mock_version_store::MockPreviousBundleAndExecutionReceiptVersions; | ||
use crate::{self as pallet_domains, BlockSlot, FungibleHoldId}; | ||
#[cfg(test)] | ||
use crate::{ | ||
self as pallet_domains, BalanceOf, BlockSlot, BlockTree, BlockTreeNodes, BundleError, Config, | ||
ConsensusBlockHash, DomainBlockNumberFor, DomainHashingFor, DomainRegistry, | ||
DomainRuntimeUpgradeRecords, DomainRuntimeUpgrades, ExecutionInbox, ExecutionReceiptOf, | ||
FraudProofError, FungibleHoldId, HeadDomainNumber, HeadReceiptNumber, NextDomainId, | ||
OperatorConfig, RawOrigin as DomainOrigin, RuntimeRegistry, ScheduledRuntimeUpgrades, | ||
BalanceOf, BlockTree, BlockTreeNodes, BundleError, ConsensusBlockHash, DomainBlockNumberFor, | ||
DomainHashingFor, DomainRegistry, DomainRuntimeUpgradeRecords, DomainRuntimeUpgrades, | ||
ExecutionInbox, ExecutionReceiptOf, FraudProofError, HeadDomainNumber, HeadReceiptNumber, | ||
NextDomainId, OperatorConfig, OperatorId, ProofOfElection, RawOrigin as DomainOrigin, | ||
RuntimeId, RuntimeRegistry, ScheduledRuntimeUpgrades, block_tree::BlockTreeNode, | ||
domain_registry::DomainConfigParams, runtime_registry::ScheduledRuntimeUpgrade, | ||
staking_epoch::do_finalize_domain_current_epoch, | ||
}; | ||
use core::mem; | ||
use domain_runtime_primitives::BlockNumber as DomainBlockNumber; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are a large number of new #[cfg(test)]
in this module, which makes it difficult to maintain.
It looks like it would be easier to have two module files, with two different compilation cfg
conditions:
- test_support:
cfg(any(test, feature = "fuzz"))
: all the types and functions you want to use in fuzzing, that are currently used in tests - tests:
cfg(test)
: the existing tests (marked with#[test]
) and the code that isn't used for fuzzing
Creating a new module and moving shared code into it should be a separate commit, at the start of your PR.
|
||
Command to install ziggy: | ||
``` | ||
cargo install --force ziggy cargo-afl honggfuzz grcov |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is --force
needed here?
@@ -0,0 +1,3 @@ | |||
#!/usr/bin/env bash | |||
sudo apt install -y protobuf-compiler binutils-dev |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This line only works on a Linux distribution that uses apt
packages. Please delete it.
Install instructions and commands that use sudo
belong in the README, because they are only run once, and they can be different for different OS distributions or package managers.
@@ -0,0 +1,3 @@ | |||
#!/usr/bin/env bash |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The script should include the standard checks that make sure every command runs correctly:
subspace/scripts/runtime-benchmark.sh
Lines 3 to 4 in b59b800
# http://redsymbol.net/articles/unofficial-bash-strict-mode/ | |
set -euo pipefail |
./fuzz/staking/target | ||
./fuzz/staking/output |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this still required now that the crate has been moved into the workspace?
3393a26
to
4f25c3f
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for this documentation!
These notes are for whoever next picks up this PR. I've also resolved any completed changes, and un-resolved anything that's still needed.
"domains/test/service", | ||
"domains/test/utils", | ||
"shared/*", | ||
"fuzz/staking", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: alphabetical order
bugbot run |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comment @cursor review
or bugbot run
to trigger another review on this PR
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
More future change notes
run: cargo install --force ziggy cargo-afl honggfuzz grcov | ||
|
||
- name: test fuzzer | ||
run: scripts/run-fuzzer-ci.sh |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This job should fail if:
- there are crashes or timeouts
- there are zero instances, speed, execs, or coverage
- the status is not "ziggy rocking"
That way we will check and analyse any fuzzing results or run failures.
scripts/run-fuzzer-ci.sh
Outdated
sudo apt install -y protobuf-compiler binutils-dev | ||
|
||
cd ./fuzz/staking && cargo ziggy build --no-honggfuzz | ||
cargo ziggy fuzz --timeout 600 --release |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The timeout should be a parameter (with a default of 5 minutes?) for CI. Making it a parameter lets developers run the script outside CI for long fuzzing sessions.
We can drop the -ci
from the script name when we make this change.
scripts/run-fuzzer-ci.sh
Outdated
|
||
cd ./fuzz/staking && cargo ziggy build --no-honggfuzz | ||
# cargo ziggy fuzz doesn't allow us to set a number of runs or a run time limit | ||
timeout 5m cargo ziggy fuzz --release |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
timeout 5m cargo ziggy fuzz --release | |
timeout --preserve-status 5m cargo ziggy fuzz --release |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently this exits with status 143 due to the SIGTERM
sent by timeout
.
The script should succeed if the exit status is 143, but fail on any other status (zero indicates the fuzzer has changed behaviour or there is a bug that caused it to exit early, so we should fail on that).
Unless there are plans to add an iteration or time limit to the fuzzer itself?
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: