Skip to content
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

Add MAX_INSTRUCTIONS_TO_DECODE to XCMv2 #4978

Merged
merged 2 commits into from
Jul 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 65 additions & 2 deletions polkadot/xcm/src/v2/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -278,14 +281,39 @@ 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())]
#[scale_info(bounds(), skip_type_params(RuntimeCall))]
#[scale_info(replace_segment("staging_xcm", "xcm"))]
pub struct Xcm<RuntimeCall>(pub Vec<Instruction<RuntimeCall>>);

environmental::environmental!(instructions_count: u8);

impl<Call> Decode for Xcm<Call> {
fn decode<I: CodecInput>(input: &mut I) -> core::result::Result<Self, CodecError> {
instructions_count::using_once(&mut 0, || {
let number_of_instructions: u32 = <Compact<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<RuntimeCall> Xcm<RuntimeCall> {
/// Create an empty instance.
pub fn new() -> Self {
Expand Down Expand Up @@ -1157,3 +1185,38 @@ impl<RuntimeCall> TryFrom<NewInstruction<RuntimeCall>> for Instruction<RuntimeCa
})
}
}

#[cfg(test)]
mod tests {
use super::{prelude::*, *};

#[test]
fn decoding_respects_limit() {
let max_xcm = Xcm::<()>(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());
}
}
18 changes: 18 additions & 0 deletions prdoc/pr_4978.prdoc
Original file line number Diff line number Diff line change
@@ -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
Loading