From 20380bef5ae6d0034756af82bdac1cb97ab54926 Mon Sep 17 00:00:00 2001 From: Vincent Ulitzsch Date: Thu, 16 Sep 2021 17:39:20 +0200 Subject: [PATCH 01/10] Extend xcm-simulator with a fuzzer for xcm exec This commit adds a very basic fuzzer that fuzzes the xcm exectution, in particular the process_instruction function. --- Cargo.toml | 1 + xcm/xcm-simulator/fuzzer/Cargo.toml | 31 ++ xcm/xcm-simulator/fuzzer/src/fuzz.rs | 163 ++++++++++ xcm/xcm-simulator/fuzzer/src/parachain.rs | 327 ++++++++++++++++++++ xcm/xcm-simulator/fuzzer/src/relay_chain.rs | 189 +++++++++++ 5 files changed, 711 insertions(+) create mode 100644 xcm/xcm-simulator/fuzzer/Cargo.toml create mode 100644 xcm/xcm-simulator/fuzzer/src/fuzz.rs create mode 100644 xcm/xcm-simulator/fuzzer/src/parachain.rs create mode 100644 xcm/xcm-simulator/fuzzer/src/relay_chain.rs diff --git a/Cargo.toml b/Cargo.toml index 2b5cc259d429..65ef216a63f2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,6 +42,7 @@ members = [ "xcm/xcm-executor/integration-tests", "xcm/xcm-simulator", "xcm/xcm-simulator/example", + "xcm/xcm-simulator/fuzzer", "xcm/pallet-xcm", "xcm/procedural", "node/client", diff --git a/xcm/xcm-simulator/fuzzer/Cargo.toml b/xcm/xcm-simulator/fuzzer/Cargo.toml new file mode 100644 index 000000000000..e8971008c684 --- /dev/null +++ b/xcm/xcm-simulator/fuzzer/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "xcm-simulator-fuzzer" +version = "0.9.9" +authors = ["Parity Technologies "] +description = "Examples of xcm-simulator usage." +edition = "2018" + +[dependencies] +codec = { package = "parity-scale-codec", version = "2.0.0" } + +frame-system = { git = "https://github.com/paritytech/substrate", branch = "master" } +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master" } +pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-io = { git = "https://github.com/paritytech/substrate", branch = "master" } + +xcm = { path = "../../" } +xcm-simulator = { path = "../" } +xcm-executor = { path = "../../xcm-executor" } +xcm-builder = { path = "../../xcm-builder" } +pallet-xcm = { path = "../../pallet-xcm" } +polkadot-core-primitives = { path = "../../../core-primitives" } +polkadot-runtime-parachains = { path = "../../../runtime/parachains" } +polkadot-parachain = { path = "../../../parachain" } +honggfuzz = "0.5.54" + +[[bin]] +path = "src/fuzz.rs" +name = "xcm-fuzzer" \ No newline at end of file diff --git a/xcm/xcm-simulator/fuzzer/src/fuzz.rs b/xcm/xcm-simulator/fuzzer/src/fuzz.rs new file mode 100644 index 000000000000..2cec12ac3beb --- /dev/null +++ b/xcm/xcm-simulator/fuzzer/src/fuzz.rs @@ -0,0 +1,163 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +mod parachain; +mod relay_chain; + +use polkadot_parachain::primitives::Id as ParaId; +use sp_runtime::traits::AccountIdConversion; +use xcm_simulator::{decl_test_network, decl_test_parachain, decl_test_relay_chain}; +use honggfuzz::fuzz; +use xcm_simulator::TestExt; +use codec::{Decode, DecodeLimit, Encode}; + +use frame_support::assert_ok; +use xcm::latest::prelude::*; + + +pub const ALICE: sp_runtime::AccountId32 = sp_runtime::AccountId32::new([0u8; 32]); +pub const INITIAL_BALANCE: u128 = 1_000_000_000; + +decl_test_parachain! { + pub struct ParaA { + Runtime = parachain::Runtime, + XcmpMessageHandler = parachain::MsgQueue, + DmpMessageHandler = parachain::MsgQueue, + new_ext = para_ext(1), + } +} + +decl_test_parachain! { + pub struct ParaB { + Runtime = parachain::Runtime, + XcmpMessageHandler = parachain::MsgQueue, + DmpMessageHandler = parachain::MsgQueue, + new_ext = para_ext(2), + } +} + +decl_test_relay_chain! { + pub struct Relay { + Runtime = relay_chain::Runtime, + XcmConfig = relay_chain::XcmConfig, + new_ext = relay_ext(), + } +} + +decl_test_network! { + pub struct MockNet { + relay_chain = Relay, + parachains = vec![ + (1, ParaA), + (2, ParaB), + ], + } +} + +pub fn para_account_id(id: u32) -> relay_chain::AccountId { + ParaId::from(id).into_account() +} + +pub fn para_ext(para_id: u32) -> sp_io::TestExternalities { + use parachain::{MsgQueue, Runtime, System}; + + let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); + + pallet_balances::GenesisConfig:: { balances: vec![(ALICE, INITIAL_BALANCE)] } + .assimilate_storage(&mut t) + .unwrap(); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| { + System::set_block_number(1); + MsgQueue::set_para_id(para_id.into()); + }); + ext +} + +pub fn relay_ext() -> sp_io::TestExternalities { + use relay_chain::{Runtime, System}; + + let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); + + pallet_balances::GenesisConfig:: { + balances: vec![(ALICE, INITIAL_BALANCE), (para_account_id(1), INITIAL_BALANCE)], + } + .assimilate_storage(&mut t) + .unwrap(); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| System::set_block_number(1)); + ext +} + +pub type RelayChainPalletXcm = pallet_xcm::Pallet; +pub type ParachainPalletXcm = pallet_xcm::Pallet; + +fn run_one_input(data: &[u8]) { + MockNet::reset(); + if let Ok(m) = Xcm::decode_all_with_depth_limit(8, data){ + #[cfg(not(fuzzing))] + { + println!("Executing message {:?}", m); + } + ParaA::execute_with(|| { + ParachainPalletXcm::send_xcm( + Here, + Parent.into(), + m, + ); + }); + Relay::execute_with(|| { + use relay_chain::{Event, System}; + }); + } +} + +fn main() { + #[cfg(fuzzing)] + { + loop { + fuzz!(|data: &[u8]| { + run_one_input(data); + }); + } + } + #[cfg(not(fuzzing))] + { + use std::env; + use std::fs; + use std::fs::File; + use std::io::Read; + let args: Vec<_> = env::args().collect(); + let md = fs::metadata(&args[1]).unwrap(); + let all_files = match md.is_dir() { + true => fs::read_dir(&args[1]) + .unwrap() + .map(|x| x.unwrap().path().to_str().unwrap().to_string()) + .collect::>(), + false => (&args[1..]).to_vec(), + }; + println!("All_files {:?}", all_files); + for argument in all_files { + println!("Now doing file {:?}", argument); + let mut buffer: Vec = Vec::new(); + let mut f = File::open(argument).unwrap(); + f.read_to_end(&mut buffer).unwrap(); + run_one_input(&buffer.as_slice()); + } + } +} diff --git a/xcm/xcm-simulator/fuzzer/src/parachain.rs b/xcm/xcm-simulator/fuzzer/src/parachain.rs new file mode 100644 index 000000000000..0d2c74f8ba7c --- /dev/null +++ b/xcm/xcm-simulator/fuzzer/src/parachain.rs @@ -0,0 +1,327 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +//! Parachain runtime mock. + +use codec::{Decode, Encode}; +use frame_support::{ + construct_runtime, parameter_types, + traits::{Everything, Nothing}, + weights::{constants::WEIGHT_PER_SECOND, Weight}, +}; +use sp_core::H256; +use sp_runtime::{ + testing::Header, + traits::{Hash, IdentityLookup}, + AccountId32, +}; +use sp_std::{convert::TryFrom, prelude::*}; + +use pallet_xcm::XcmPassthrough; +use polkadot_core_primitives::BlockNumber as RelayBlockNumber; +use polkadot_parachain::primitives::{ + DmpMessageHandler, Id as ParaId, Sibling, XcmpMessageFormat, XcmpMessageHandler, +}; +use xcm::{latest::prelude::*, VersionedXcm}; +use xcm_builder::{ + AccountId32Aliases, AllowUnpaidExecutionFrom, CurrencyAdapter as XcmCurrencyAdapter, + EnsureXcmOrigin, FixedRateOfFungible, FixedWeightBounds, IsConcrete, LocationInverter, + NativeAsset, ParentIsDefault, SiblingParachainConvertsVia, SignedAccountId32AsNative, + SignedToAccountId32, SovereignSignedViaLocation, +}; +use xcm_executor::{Config, XcmExecutor}; + +pub type AccountId = AccountId32; +pub type Balance = u128; + +parameter_types! { + pub const BlockHashCount: u64 = 250; +} + +impl frame_system::Config for Runtime { + type Origin = Origin; + type Call = Call; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Hashing = ::sp_runtime::traits::BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Header = Header; + type Event = Event; + type BlockHashCount = BlockHashCount; + type BlockWeights = (); + type BlockLength = (); + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type DbWeight = (); + type BaseCallFilter = Everything; + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); +} + +parameter_types! { + pub ExistentialDeposit: Balance = 1; + pub const MaxLocks: u32 = 50; + pub const MaxReserves: u32 = 50; +} + +impl pallet_balances::Config for Runtime { + type MaxLocks = MaxLocks; + type Balance = Balance; + type Event = Event; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type WeightInfo = (); + type MaxReserves = MaxReserves; + type ReserveIdentifier = [u8; 8]; +} + +parameter_types! { + pub const ReservedXcmpWeight: Weight = WEIGHT_PER_SECOND / 4; + pub const ReservedDmpWeight: Weight = WEIGHT_PER_SECOND / 4; +} + +parameter_types! { + pub const KsmLocation: MultiLocation = MultiLocation::parent(); + pub const RelayNetwork: NetworkId = NetworkId::Kusama; + pub Ancestry: MultiLocation = Parachain(MsgQueue::parachain_id().into()).into(); +} + +pub type LocationToAccountId = ( + ParentIsDefault, + SiblingParachainConvertsVia, + AccountId32Aliases, +); + +pub type XcmOriginToCallOrigin = ( + SovereignSignedViaLocation, + SignedAccountId32AsNative, + XcmPassthrough, +); + +parameter_types! { + pub const UnitWeightCost: Weight = 1; + pub KsmPerSecond: (AssetId, u128) = (Concrete(Parent.into()), 1); + pub const MaxInstructions: u32 = 100; +} + +pub type LocalAssetTransactor = + XcmCurrencyAdapter, LocationToAccountId, AccountId, ()>; + +pub type XcmRouter = super::ParachainXcmRouter; +pub type Barrier = AllowUnpaidExecutionFrom; + +pub struct XcmConfig; +impl Config for XcmConfig { + type Call = Call; + type XcmSender = XcmRouter; + type AssetTransactor = LocalAssetTransactor; + type OriginConverter = XcmOriginToCallOrigin; + type IsReserve = NativeAsset; + type IsTeleporter = (); + type LocationInverter = LocationInverter; + type Barrier = Barrier; + type Weigher = FixedWeightBounds; + type Trader = FixedRateOfFungible; + type ResponseHandler = (); + type AssetTrap = (); + type AssetClaims = (); + type SubscriptionService = (); +} + +#[frame_support::pallet] +pub mod mock_msg_queue { + use super::*; + use frame_support::pallet_prelude::*; + + #[pallet::config] + pub trait Config: frame_system::Config { + type Event: From> + IsType<::Event>; + type XcmExecutor: ExecuteXcm; + } + + #[pallet::call] + impl Pallet {} + + #[pallet::pallet] + #[pallet::generate_store(pub(super) trait Store)] + pub struct Pallet(_); + + #[pallet::storage] + #[pallet::getter(fn parachain_id)] + pub(super) type ParachainId = StorageValue<_, ParaId, ValueQuery>; + + #[pallet::storage] + #[pallet::getter(fn received_dmp)] + /// A queue of received DMP messages + pub(super) type ReceivedDmp = StorageValue<_, Vec>, ValueQuery>; + + impl Get for Pallet { + fn get() -> ParaId { + Self::parachain_id() + } + } + + pub type MessageId = [u8; 32]; + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + // XCMP + /// Some XCM was executed OK. + Success(Option), + /// Some XCM failed. + Fail(Option, XcmError), + /// Bad XCM version used. + BadVersion(Option), + /// Bad XCM format used. + BadFormat(Option), + + // DMP + /// Downward message is invalid XCM. + InvalidFormat(MessageId), + /// Downward message is unsupported version of XCM. + UnsupportedVersion(MessageId), + /// Downward message executed with the given outcome. + ExecutedDownward(MessageId, Outcome), + } + + impl Pallet { + pub fn set_para_id(para_id: ParaId) { + ParachainId::::put(para_id); + } + + fn handle_xcmp_message( + sender: ParaId, + _sent_at: RelayBlockNumber, + xcm: VersionedXcm, + max_weight: Weight, + ) -> Result { + let hash = Encode::using_encoded(&xcm, T::Hashing::hash); + let (result, event) = match Xcm::::try_from(xcm) { + Ok(xcm) => { + let location = MultiLocation::new(1, X1(Parachain(sender.into()))); + match T::XcmExecutor::execute_xcm(location, xcm, max_weight) { + Outcome::Error(e) => (Err(e.clone()), Event::Fail(Some(hash), e)), + Outcome::Complete(w) => (Ok(w), Event::Success(Some(hash))), + // As far as the caller is concerned, this was dispatched without error, so + // we just report the weight used. + Outcome::Incomplete(w, e) => (Ok(w), Event::Fail(Some(hash), e)), + } + }, + Err(()) => (Err(XcmError::UnhandledXcmVersion), Event::BadVersion(Some(hash))), + }; + Self::deposit_event(event); + result + } + } + + impl XcmpMessageHandler for Pallet { + fn handle_xcmp_messages<'a, I: Iterator>( + iter: I, + max_weight: Weight, + ) -> Weight { + for (sender, sent_at, data) in iter { + let mut data_ref = data; + let _ = XcmpMessageFormat::decode(&mut data_ref) + .expect("Simulator encodes with versioned xcm format; qed"); + + let mut remaining_fragments = &data_ref[..]; + while !remaining_fragments.is_empty() { + if let Ok(xcm) = VersionedXcm::::decode(&mut remaining_fragments) { + let _ = Self::handle_xcmp_message(sender, sent_at, xcm, max_weight); + } else { + debug_assert!(false, "Invalid incoming XCMP message data"); + } + } + } + max_weight + } + } + + impl DmpMessageHandler for Pallet { + fn handle_dmp_messages( + iter: impl Iterator)>, + limit: Weight, + ) -> Weight { + for (_i, (_sent_at, data)) in iter.enumerate() { + let id = sp_io::hashing::blake2_256(&data[..]); + let maybe_msg = + VersionedXcm::::decode(&mut &data[..]).map(Xcm::::try_from); + match maybe_msg { + Err(_) => { + Self::deposit_event(Event::InvalidFormat(id)); + }, + Ok(Err(())) => { + Self::deposit_event(Event::UnsupportedVersion(id)); + }, + Ok(Ok(x)) => { + let outcome = T::XcmExecutor::execute_xcm(Parent.into(), x.clone(), limit); + >::append(x); + Self::deposit_event(Event::ExecutedDownward(id, outcome)); + }, + } + } + limit + } + } +} + +impl mock_msg_queue::Config for Runtime { + type Event = Event; + type XcmExecutor = XcmExecutor; +} + +pub type LocalOriginToLocation = SignedToAccountId32; + +impl pallet_xcm::Config for Runtime { + type Event = Event; + type SendXcmOrigin = EnsureXcmOrigin; + type XcmRouter = XcmRouter; + type ExecuteXcmOrigin = EnsureXcmOrigin; + type XcmExecuteFilter = Everything; + type XcmExecutor = XcmExecutor; + type XcmTeleportFilter = Nothing; + type XcmReserveTransferFilter = Everything; + type Weigher = FixedWeightBounds; + type LocationInverter = LocationInverter; + type Origin = Origin; + type Call = Call; + const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; + type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; +} + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; + +construct_runtime!( + pub enum Runtime where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system::{Pallet, Call, Storage, Config, Event}, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + MsgQueue: mock_msg_queue::{Pallet, Storage, Event}, + PolkadotXcm: pallet_xcm::{Pallet, Call, Event, Origin}, + } +); diff --git a/xcm/xcm-simulator/fuzzer/src/relay_chain.rs b/xcm/xcm-simulator/fuzzer/src/relay_chain.rs new file mode 100644 index 000000000000..a6f6f1989174 --- /dev/null +++ b/xcm/xcm-simulator/fuzzer/src/relay_chain.rs @@ -0,0 +1,189 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +//! Relay chain runtime mock. + +use frame_support::{ + construct_runtime, parameter_types, + traits::{Everything, Nothing}, + weights::Weight, +}; +use sp_core::H256; +use sp_runtime::{testing::Header, traits::IdentityLookup, AccountId32}; + +use polkadot_parachain::primitives::Id as ParaId; +use polkadot_runtime_parachains::{configuration, origin, shared, ump}; +use xcm::latest::prelude::*; +use xcm_builder::{ + AccountId32Aliases, AllowUnpaidExecutionFrom, ChildParachainAsNative, + ChildParachainConvertsVia, ChildSystemParachainAsSuperuser, + CurrencyAdapter as XcmCurrencyAdapter, FixedRateOfFungible, FixedWeightBounds, IsConcrete, + LocationInverter, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, +}; +use xcm_executor::{Config, XcmExecutor}; + +pub type AccountId = AccountId32; +pub type Balance = u128; + +parameter_types! { + pub const BlockHashCount: u64 = 250; +} + +impl frame_system::Config for Runtime { + type Origin = Origin; + type Call = Call; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Hashing = ::sp_runtime::traits::BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Header = Header; + type Event = Event; + type BlockHashCount = BlockHashCount; + type BlockWeights = (); + type BlockLength = (); + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type DbWeight = (); + type BaseCallFilter = Everything; + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); +} + +parameter_types! { + pub ExistentialDeposit: Balance = 1; + pub const MaxLocks: u32 = 50; + pub const MaxReserves: u32 = 50; +} + +impl pallet_balances::Config for Runtime { + type MaxLocks = MaxLocks; + type Balance = Balance; + type Event = Event; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type WeightInfo = (); + type MaxReserves = MaxReserves; + type ReserveIdentifier = [u8; 8]; +} + +impl shared::Config for Runtime {} + +impl configuration::Config for Runtime {} + +parameter_types! { + pub const KsmLocation: MultiLocation = Here.into(); + pub const KusamaNetwork: NetworkId = NetworkId::Kusama; + pub const AnyNetwork: NetworkId = NetworkId::Any; + pub Ancestry: MultiLocation = Here.into(); + pub UnitWeightCost: Weight = 1_000; +} + +pub type SovereignAccountOf = + (ChildParachainConvertsVia, AccountId32Aliases); + +pub type LocalAssetTransactor = + XcmCurrencyAdapter, SovereignAccountOf, AccountId, ()>; + +type LocalOriginConverter = ( + SovereignSignedViaLocation, + ChildParachainAsNative, + SignedAccountId32AsNative, + ChildSystemParachainAsSuperuser, +); + +parameter_types! { + pub const BaseXcmWeight: Weight = 1_000; + pub KsmPerSecond: (AssetId, u128) = (Concrete(KsmLocation::get()), 1); + pub const MaxInstructions: u32 = 100; +} + +pub type XcmRouter = super::RelayChainXcmRouter; +pub type Barrier = AllowUnpaidExecutionFrom; + +pub struct XcmConfig; +impl Config for XcmConfig { + type Call = Call; + type XcmSender = XcmRouter; + type AssetTransactor = LocalAssetTransactor; + type OriginConverter = LocalOriginConverter; + type IsReserve = (); + type IsTeleporter = (); + type LocationInverter = LocationInverter; + type Barrier = Barrier; + type Weigher = FixedWeightBounds; + type Trader = FixedRateOfFungible; + type ResponseHandler = (); + type AssetTrap = (); + type AssetClaims = (); + type SubscriptionService = (); +} + +pub type LocalOriginToLocation = SignedToAccountId32; + +impl pallet_xcm::Config for Runtime { + type Event = Event; + type SendXcmOrigin = xcm_builder::EnsureXcmOrigin; + type XcmRouter = XcmRouter; + // Anyone can execute XCM messages locally... + type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin; + type XcmExecuteFilter = Nothing; + type XcmExecutor = XcmExecutor; + type XcmTeleportFilter = Everything; + type XcmReserveTransferFilter = Everything; + type Weigher = FixedWeightBounds; + type LocationInverter = LocationInverter; + type Origin = Origin; + type Call = Call; + const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; + type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; +} + +parameter_types! { + pub const FirstMessageFactorPercent: u64 = 100; +} + +impl ump::Config for Runtime { + type Event = Event; + type UmpSink = ump::XcmSink, Runtime>; + type FirstMessageFactorPercent = FirstMessageFactorPercent; + type ExecuteOverweightOrigin = frame_system::EnsureRoot; +} + +impl origin::Config for Runtime {} + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; + +construct_runtime!( + pub enum Runtime where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system::{Pallet, Call, Storage, Config, Event}, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + ParasOrigin: origin::{Pallet, Origin}, + ParasUmp: ump::{Pallet, Call, Storage, Event}, + XcmPallet: pallet_xcm::{Pallet, Call, Storage, Event, Origin}, + } +); From c6516dbd72670087fa33db8dd1dd807761dda697 Mon Sep 17 00:00:00 2001 From: Vincent Ulitzsch Date: Thu, 16 Sep 2021 18:50:59 +0200 Subject: [PATCH 02/10] Update cargo.toml in xcm-simulator-fuzzer --- xcm/xcm-simulator/fuzzer/Cargo.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/xcm/xcm-simulator/fuzzer/Cargo.toml b/xcm/xcm-simulator/fuzzer/Cargo.toml index e8971008c684..fdb18e9e5f48 100644 --- a/xcm/xcm-simulator/fuzzer/Cargo.toml +++ b/xcm/xcm-simulator/fuzzer/Cargo.toml @@ -7,6 +7,7 @@ edition = "2018" [dependencies] codec = { package = "parity-scale-codec", version = "2.0.0" } +scale-info = { version = "1.0", features = ["derive"] } frame-system = { git = "https://github.com/paritytech/substrate", branch = "master" } frame-support = { git = "https://github.com/paritytech/substrate", branch = "master" } @@ -28,4 +29,4 @@ honggfuzz = "0.5.54" [[bin]] path = "src/fuzz.rs" -name = "xcm-fuzzer" \ No newline at end of file +name = "xcm-fuzzer" From 7a279b7d90b5f57169846a32a4f63493127f9d2e Mon Sep 17 00:00:00 2001 From: Vincent Ulitzsch Date: Thu, 16 Sep 2021 23:18:17 +0200 Subject: [PATCH 03/10] Add xcm-fuzzer to honggfuzz workflow --- .github/workflows/honggfuzz.yml | 39 +++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/.github/workflows/honggfuzz.yml b/.github/workflows/honggfuzz.yml index af0de3eb5017..9306fa0bdabf 100644 --- a/.github/workflows/honggfuzz.yml +++ b/.github/workflows/honggfuzz.yml @@ -5,6 +5,45 @@ on: - cron: '0 0 * * *' jobs: + xcm-fuzzer: + runs-on: ubuntu-latest + steps: + - name: Checkout sources + uses: actions/checkout@v2 + with: + fetch-depth: 1 + + - name: Install minimal stable Rust + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + + - name: Install minimal nightly Rust + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: nightly + target: wasm32-unknown-unknown + + - name: Install honggfuzz deps + run: sudo apt-get install --no-install-recommends binutils-dev libunwind8-dev + + - name: Install honggfuzz + uses: actions-rs/cargo@v1 + with: + command: install + args: honggfuzz --version "0.5.54" + + - name: Build fuzzer binaries + working-directory: xcm/xcm-simulator/fuzzer/ + run: cargo hfuzz build + + - name: Run fuzzer + working-directory: xcm/xcm-simulator/fuzzer/ + run: bash $GITHUB_WORKSPACE/scripts/github/run_fuzzer.sh xcm-fuzzer + erasure-coding-round-trip: runs-on: ubuntu-latest steps: From c8d8448e06e75ad80acbca1784c93229ce6a93a6 Mon Sep 17 00:00:00 2001 From: Shawn Tabrizi Date: Mon, 20 Sep 2021 01:44:53 -0400 Subject: [PATCH 04/10] Update Cargo.lock --- Cargo.lock | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 631c36009309..c8aa254fd08b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -130,6 +130,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "arbitrary" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "577b08a4acd7b99869f863c50011b01eb73424ccc798ecd996f2e24817adfca7" + [[package]] name = "arrayref" version = "0.3.6" @@ -2627,6 +2633,17 @@ dependencies = [ "hmac 0.8.1", ] +[[package]] +name = "honggfuzz" +version = "0.5.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bea09577d948a98a5f59b7c891e274c4fb35ad52f67782b3d0cb53b9c05301f1" +dependencies = [ + "arbitrary", + "lazy_static", + "memmap", +] + [[package]] name = "hostname" version = "0.3.1" @@ -4051,6 +4068,16 @@ version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" +[[package]] +name = "memmap" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" +dependencies = [ + "libc", + "winapi 0.3.9", +] + [[package]] name = "memmap2" version = "0.2.0" @@ -11820,6 +11847,30 @@ dependencies = [ "xcm-simulator", ] +[[package]] +name = "xcm-simulator-fuzzer" +version = "0.9.9" +dependencies = [ + "frame-support", + "frame-system", + "honggfuzz", + "pallet-balances", + "pallet-xcm", + "parity-scale-codec", + "polkadot-core-primitives", + "polkadot-parachain", + "polkadot-runtime-parachains", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "xcm", + "xcm-builder", + "xcm-executor", + "xcm-simulator", +] + [[package]] name = "yamux" version = "0.9.0" From 417631923ceba37fd79b86cb5b0ba46dd77ab035 Mon Sep 17 00:00:00 2001 From: Vincent Ulitzsch Date: Thu, 7 Oct 2021 12:25:32 +0200 Subject: [PATCH 05/10] Update xcm/xcm-simulator/fuzzer/Cargo.toml so honggfuzz shows up on top Co-authored-by: Keith Yeung --- xcm/xcm-simulator/fuzzer/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xcm/xcm-simulator/fuzzer/Cargo.toml b/xcm/xcm-simulator/fuzzer/Cargo.toml index fdb18e9e5f48..9fada9b71d55 100644 --- a/xcm/xcm-simulator/fuzzer/Cargo.toml +++ b/xcm/xcm-simulator/fuzzer/Cargo.toml @@ -7,6 +7,7 @@ edition = "2018" [dependencies] codec = { package = "parity-scale-codec", version = "2.0.0" } +honggfuzz = "0.5.54" scale-info = { version = "1.0", features = ["derive"] } frame-system = { git = "https://github.com/paritytech/substrate", branch = "master" } @@ -25,7 +26,6 @@ pallet-xcm = { path = "../../pallet-xcm" } polkadot-core-primitives = { path = "../../../core-primitives" } polkadot-runtime-parachains = { path = "../../../runtime/parachains" } polkadot-parachain = { path = "../../../parachain" } -honggfuzz = "0.5.54" [[bin]] path = "src/fuzz.rs" From 0ec390bc4425e3aa4cb6daa8dbee392950d4a488 Mon Sep 17 00:00:00 2001 From: Vincent Ulitzsch Date: Thu, 7 Oct 2021 12:33:50 +0200 Subject: [PATCH 06/10] Update relay_chain.rs in xcm-fuzzer --- xcm/xcm-simulator/fuzzer/src/relay_chain.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/xcm/xcm-simulator/fuzzer/src/relay_chain.rs b/xcm/xcm-simulator/fuzzer/src/relay_chain.rs index a6f6f1989174..cc50aec90d18 100644 --- a/xcm/xcm-simulator/fuzzer/src/relay_chain.rs +++ b/xcm/xcm-simulator/fuzzer/src/relay_chain.rs @@ -88,7 +88,9 @@ impl pallet_balances::Config for Runtime { impl shared::Config for Runtime {} -impl configuration::Config for Runtime {} +impl configuration::Config for Runtime { + type WeightInfo = configuration::weights::WeightInfo; +} parameter_types! { pub const KsmLocation: MultiLocation = Here.into(); From 47087341cc7043eb16e99465e61b435187ac342e Mon Sep 17 00:00:00 2001 From: Vincent Ulitzsch Date: Thu, 7 Oct 2021 12:34:45 +0200 Subject: [PATCH 07/10] Use MAX_XCM_DECODE_DEPTH instead of hardcoded decode limit in xcm-fuzzer --- xcm/xcm-simulator/fuzzer/src/fuzz.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/xcm/xcm-simulator/fuzzer/src/fuzz.rs b/xcm/xcm-simulator/fuzzer/src/fuzz.rs index 2cec12ac3beb..ce5bed38721c 100644 --- a/xcm/xcm-simulator/fuzzer/src/fuzz.rs +++ b/xcm/xcm-simulator/fuzzer/src/fuzz.rs @@ -26,6 +26,7 @@ use codec::{Decode, DecodeLimit, Encode}; use frame_support::assert_ok; use xcm::latest::prelude::*; +use xcm::MAX_XCM_DECODE_DEPTH; pub const ALICE: sp_runtime::AccountId32 = sp_runtime::AccountId32::new([0u8; 32]); @@ -109,7 +110,7 @@ pub type ParachainPalletXcm = pallet_xcm::Pallet; fn run_one_input(data: &[u8]) { MockNet::reset(); - if let Ok(m) = Xcm::decode_all_with_depth_limit(8, data){ + if let Ok(m) = Xcm::decode_all_with_depth_limit(MAX_XCM_DECODE_DEPTH, data){ #[cfg(not(fuzzing))] { println!("Executing message {:?}", m); From 49a136892eaaa439911e693a5613f2164b45fa57 Mon Sep 17 00:00:00 2001 From: Vincent Ulitzsch Date: Thu, 7 Oct 2021 15:27:25 +0200 Subject: [PATCH 08/10] Add comment on how to generate coverage report in xcm-fuzzer --- xcm/xcm-simulator/fuzzer/src/fuzz.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/xcm/xcm-simulator/fuzzer/src/fuzz.rs b/xcm/xcm-simulator/fuzzer/src/fuzz.rs index ce5bed38721c..e5d202c628b3 100644 --- a/xcm/xcm-simulator/fuzzer/src/fuzz.rs +++ b/xcm/xcm-simulator/fuzzer/src/fuzz.rs @@ -139,6 +139,21 @@ fn main() { } #[cfg(not(fuzzing))] { + //This code path can be used to generate a line-code coverage report in html + //that depicts which lines are executed by at least one input in the current fuzzing queue. + //To generate this code coverage report, run the following commands: + /* + ``` + export CARGO_INCREMENTAL=0 + export RUSTFLAGS="-Zprofile -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort" + export RUSTDOCFLAGS="-Cpanic=abort" + rustup override set nightly + SKIP_WASM_BUILD=1 cargo build + ./xcm/xcm-simulator/fuzzer/target/debug/xcm-fuzzer hfuzz_workspace/xcm-fuzzer/input + zip -0 ccov.zip `find ../../target/debug \( -name "*.gc*" -o -name "test-*.gc*" \) -print` + grcov ccov.zip -s / -t html --llvm --branch --ignore-not-existing -o ../../target/debug/coverage/ + ``` + */ use std::env; use std::fs; use std::fs::File; From ce5d9dc0465a415ca5f17cf56aa700028c8aa184 Mon Sep 17 00:00:00 2001 From: Shawn Tabrizi Date: Thu, 7 Oct 2021 12:21:08 -0400 Subject: [PATCH 09/10] fix warnings, fmt, and unused result --- xcm/xcm-simulator/fuzzer/src/fuzz.rs | 87 ++++++++++++---------------- 1 file changed, 37 insertions(+), 50 deletions(-) diff --git a/xcm/xcm-simulator/fuzzer/src/fuzz.rs b/xcm/xcm-simulator/fuzzer/src/fuzz.rs index e5d202c628b3..0c70bf3ac097 100644 --- a/xcm/xcm-simulator/fuzzer/src/fuzz.rs +++ b/xcm/xcm-simulator/fuzzer/src/fuzz.rs @@ -17,17 +17,13 @@ mod parachain; mod relay_chain; +use codec::DecodeLimit; use polkadot_parachain::primitives::Id as ParaId; use sp_runtime::traits::AccountIdConversion; -use xcm_simulator::{decl_test_network, decl_test_parachain, decl_test_relay_chain}; -use honggfuzz::fuzz; -use xcm_simulator::TestExt; -use codec::{Decode, DecodeLimit, Encode}; +use xcm_simulator::{decl_test_network, decl_test_parachain, decl_test_relay_chain, TestExt}; use frame_support::assert_ok; -use xcm::latest::prelude::*; -use xcm::MAX_XCM_DECODE_DEPTH; - +use xcm::{latest::prelude::*, MAX_XCM_DECODE_DEPTH}; pub const ALICE: sp_runtime::AccountId32 = sp_runtime::AccountId32::new([0u8; 32]); pub const INITIAL_BALANCE: u128 = 1_000_000_000; @@ -110,39 +106,33 @@ pub type ParachainPalletXcm = pallet_xcm::Pallet; fn run_one_input(data: &[u8]) { MockNet::reset(); - if let Ok(m) = Xcm::decode_all_with_depth_limit(MAX_XCM_DECODE_DEPTH, data){ + if let Ok(m) = Xcm::decode_all_with_depth_limit(MAX_XCM_DECODE_DEPTH, data) { #[cfg(not(fuzzing))] { println!("Executing message {:?}", m); } ParaA::execute_with(|| { - ParachainPalletXcm::send_xcm( - Here, - Parent.into(), - m, - ); - }); - Relay::execute_with(|| { - use relay_chain::{Event, System}; + assert_ok!(ParachainPalletXcm::send_xcm(Here, Parent.into(), m)); }); + Relay::execute_with(|| {}); } } fn main() { - #[cfg(fuzzing)] - { - loop { - fuzz!(|data: &[u8]| { - run_one_input(data); - }); - } - } - #[cfg(not(fuzzing))] - { + #[cfg(fuzzing)] + { + loop { + fuzz!(|data: &[u8]| { + run_one_input(data); + }); + } + } + #[cfg(not(fuzzing))] + { //This code path can be used to generate a line-code coverage report in html //that depicts which lines are executed by at least one input in the current fuzzing queue. //To generate this code coverage report, run the following commands: - /* + /* ``` export CARGO_INCREMENTAL=0 export RUSTFLAGS="-Zprofile -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort" @@ -151,29 +141,26 @@ fn main() { SKIP_WASM_BUILD=1 cargo build ./xcm/xcm-simulator/fuzzer/target/debug/xcm-fuzzer hfuzz_workspace/xcm-fuzzer/input zip -0 ccov.zip `find ../../target/debug \( -name "*.gc*" -o -name "test-*.gc*" \) -print` - grcov ccov.zip -s / -t html --llvm --branch --ignore-not-existing -o ../../target/debug/coverage/ + grcov ccov.zip -s / -t html --llvm --branch --ignore-not-existing -o ../../target/debug/coverage/ ``` */ - use std::env; - use std::fs; - use std::fs::File; - use std::io::Read; - let args: Vec<_> = env::args().collect(); - let md = fs::metadata(&args[1]).unwrap(); - let all_files = match md.is_dir() { - true => fs::read_dir(&args[1]) - .unwrap() - .map(|x| x.unwrap().path().to_str().unwrap().to_string()) - .collect::>(), - false => (&args[1..]).to_vec(), - }; - println!("All_files {:?}", all_files); - for argument in all_files { - println!("Now doing file {:?}", argument); - let mut buffer: Vec = Vec::new(); - let mut f = File::open(argument).unwrap(); - f.read_to_end(&mut buffer).unwrap(); - run_one_input(&buffer.as_slice()); - } - } + use std::{env, fs, fs::File, io::Read}; + let args: Vec<_> = env::args().collect(); + let md = fs::metadata(&args[1]).unwrap(); + let all_files = match md.is_dir() { + true => fs::read_dir(&args[1]) + .unwrap() + .map(|x| x.unwrap().path().to_str().unwrap().to_string()) + .collect::>(), + false => (&args[1..]).to_vec(), + }; + println!("All_files {:?}", all_files); + for argument in all_files { + println!("Now doing file {:?}", argument); + let mut buffer: Vec = Vec::new(); + let mut f = File::open(argument).unwrap(); + f.read_to_end(&mut buffer).unwrap(); + run_one_input(&buffer.as_slice()); + } + } } From 1b95b7cccc11d6917aa5ba62b073386c7f73429a Mon Sep 17 00:00:00 2001 From: Shawn Tabrizi Date: Mon, 11 Oct 2021 19:26:25 -0400 Subject: [PATCH 10/10] fix compiler --- xcm/xcm-simulator/fuzzer/src/fuzz.rs | 2 +- xcm/xcm-simulator/fuzzer/src/parachain.rs | 2 +- xcm/xcm-simulator/fuzzer/src/relay_chain.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/xcm/xcm-simulator/fuzzer/src/fuzz.rs b/xcm/xcm-simulator/fuzzer/src/fuzz.rs index 0c70bf3ac097..44516ab8a562 100644 --- a/xcm/xcm-simulator/fuzzer/src/fuzz.rs +++ b/xcm/xcm-simulator/fuzzer/src/fuzz.rs @@ -112,7 +112,7 @@ fn run_one_input(data: &[u8]) { println!("Executing message {:?}", m); } ParaA::execute_with(|| { - assert_ok!(ParachainPalletXcm::send_xcm(Here, Parent.into(), m)); + assert_ok!(ParachainPalletXcm::send_xcm(Here, Parent, m)); }); Relay::execute_with(|| {}); } diff --git a/xcm/xcm-simulator/fuzzer/src/parachain.rs b/xcm/xcm-simulator/fuzzer/src/parachain.rs index 0d2c74f8ba7c..3911bf8e3578 100644 --- a/xcm/xcm-simulator/fuzzer/src/parachain.rs +++ b/xcm/xcm-simulator/fuzzer/src/parachain.rs @@ -275,7 +275,7 @@ pub mod mock_msg_queue { Self::deposit_event(Event::UnsupportedVersion(id)); }, Ok(Ok(x)) => { - let outcome = T::XcmExecutor::execute_xcm(Parent.into(), x.clone(), limit); + let outcome = T::XcmExecutor::execute_xcm(Parent, x.clone(), limit); >::append(x); Self::deposit_event(Event::ExecutedDownward(id, outcome)); }, diff --git a/xcm/xcm-simulator/fuzzer/src/relay_chain.rs b/xcm/xcm-simulator/fuzzer/src/relay_chain.rs index cc50aec90d18..8dcb5f1f310b 100644 --- a/xcm/xcm-simulator/fuzzer/src/relay_chain.rs +++ b/xcm/xcm-simulator/fuzzer/src/relay_chain.rs @@ -89,7 +89,7 @@ impl pallet_balances::Config for Runtime { impl shared::Config for Runtime {} impl configuration::Config for Runtime { - type WeightInfo = configuration::weights::WeightInfo; + type WeightInfo = configuration::TestWeightInfo; } parameter_types! {