From 3fab97288e8e88f8a26782929b7735ba12342205 Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Tue, 9 Jul 2024 15:49:01 +0200 Subject: [PATCH] Add `MAX_INSTRUCTIONS_TO_DECODE` to XCMv2 (#4978) It was added to v4 and v3 but was missing from v2 --- polkadot/xcm/src/v2/mod.rs | 67 ++++++++++++++++++++++++++++++++++++-- prdoc/pr_4978.prdoc | 18 ++++++++++ 2 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 prdoc/pr_4978.prdoc diff --git a/polkadot/xcm/src/v2/mod.rs b/polkadot/xcm/src/v2/mod.rs index fe7f05dd887c..1afc120f500c 100644 --- a/polkadot/xcm/src/v2/mod.rs +++ b/polkadot/xcm/src/v2/mod.rs @@ -62,7 +62,10 @@ use super::{ }; use alloc::{vec, vec::Vec}; use bounded_collections::{ConstU32, WeakBoundedVec}; -use codec::{self, Decode, Encode, MaxEncodedLen}; +use codec::{ + self, decode_vec_with_len, Compact, Decode, Encode, Error as CodecError, Input as CodecInput, + MaxEncodedLen, +}; use core::{fmt::Debug, result}; use derivative::Derivative; use scale_info::TypeInfo; @@ -278,7 +281,7 @@ pub const VERSION: super::Version = 2; pub type QueryId = u64; /// DEPRECATED. Please use XCMv3 or XCMv4 instead. -#[derive(Derivative, Default, Encode, Decode, TypeInfo)] +#[derive(Derivative, Default, Encode, TypeInfo)] #[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Debug(bound = ""))] #[codec(encode_bound())] #[codec(decode_bound())] @@ -286,6 +289,31 @@ pub type QueryId = u64; #[scale_info(replace_segment("staging_xcm", "xcm"))] pub struct Xcm(pub Vec>); +environmental::environmental!(instructions_count: u8); + +impl Decode for Xcm { + fn decode(input: &mut I) -> core::result::Result { + instructions_count::using_once(&mut 0, || { + let number_of_instructions: u32 = >::decode(input)?.into(); + instructions_count::with(|count| { + *count = count.saturating_add(number_of_instructions as u8); + if *count > MAX_INSTRUCTIONS_TO_DECODE { + return Err(CodecError::from("Max instructions exceeded")) + } + Ok(()) + }) + .unwrap_or(Ok(()))?; + let decoded_instructions = decode_vec_with_len(input, number_of_instructions as usize)?; + Ok(Self(decoded_instructions)) + }) + } +} + +/// The maximal number of instructions in an XCM before decoding fails. +/// +/// This is a deliberate limit - not a technical one. +pub const MAX_INSTRUCTIONS_TO_DECODE: u8 = 100; + impl Xcm { /// Create an empty instance. pub fn new() -> Self { @@ -1157,3 +1185,38 @@ impl TryFrom> for Instruction(vec![ClearOrigin; MAX_INSTRUCTIONS_TO_DECODE as usize]); + let encoded = max_xcm.encode(); + assert!(Xcm::<()>::decode(&mut &encoded[..]).is_ok()); + + let big_xcm = Xcm::<()>(vec![ClearOrigin; MAX_INSTRUCTIONS_TO_DECODE as usize + 1]); + let encoded = big_xcm.encode(); + assert!(Xcm::<()>::decode(&mut &encoded[..]).is_err()); + + let nested_xcm = Xcm::<()>(vec![ + DepositReserveAsset { + assets: All.into(), + dest: Here.into(), + xcm: max_xcm, + max_assets: 1, + }; + (MAX_INSTRUCTIONS_TO_DECODE / 2) as usize + ]); + let encoded = nested_xcm.encode(); + assert!(Xcm::<()>::decode(&mut &encoded[..]).is_err()); + + let even_more_nested_xcm = Xcm::<()>(vec![SetAppendix(nested_xcm); 64]); + let encoded = even_more_nested_xcm.encode(); + assert_eq!(encoded.len(), 345730); + // This should not decode since the limit is 100 + assert_eq!(MAX_INSTRUCTIONS_TO_DECODE, 100, "precondition"); + assert!(Xcm::<()>::decode(&mut &encoded[..]).is_err()); + } +} diff --git a/prdoc/pr_4978.prdoc b/prdoc/pr_4978.prdoc new file mode 100644 index 000000000000..1f86d512f2c7 --- /dev/null +++ b/prdoc/pr_4978.prdoc @@ -0,0 +1,18 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Add MAX_INSTRUCTIONS_TO_DECODE to XCMv2 + +doc: + - audience: Runtime User + description: | + Added a max number of instructions to XCMv2. If using XCMv2, you'll have to take this limit into account. + It was set to 100. + - audience: Runtime Dev + description: | + Added a max number of instructions to XCMv2. If using XCMv2, you'll have to take this limit into account. + It was set to 100. + +crates: + - name: staging-xcm + bump: minor