Skip to content

Commit 9403a5d

Browse files
Add MAX_INSTRUCTIONS_TO_DECODE to XCMv2 (#4978)
It was added to v4 and v3 but was missing from v2
1 parent 2f0e5a6 commit 9403a5d

2 files changed

Lines changed: 83 additions & 2 deletions

File tree

polkadot/xcm/src/v2/mod.rs

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,10 @@ use super::{
6262
};
6363
use alloc::{vec, vec::Vec};
6464
use bounded_collections::{ConstU32, WeakBoundedVec};
65-
use codec::{self, Decode, Encode, MaxEncodedLen};
65+
use codec::{
66+
self, decode_vec_with_len, Compact, Decode, Encode, Error as CodecError, Input as CodecInput,
67+
MaxEncodedLen,
68+
};
6669
use core::{fmt::Debug, result};
6770
use derivative::Derivative;
6871
use scale_info::TypeInfo;
@@ -278,14 +281,39 @@ pub const VERSION: super::Version = 2;
278281
pub type QueryId = u64;
279282

280283
/// DEPRECATED. Please use XCMv3 or XCMv4 instead.
281-
#[derive(Derivative, Default, Encode, Decode, TypeInfo)]
284+
#[derive(Derivative, Default, Encode, TypeInfo)]
282285
#[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Debug(bound = ""))]
283286
#[codec(encode_bound())]
284287
#[codec(decode_bound())]
285288
#[scale_info(bounds(), skip_type_params(RuntimeCall))]
286289
#[scale_info(replace_segment("staging_xcm", "xcm"))]
287290
pub struct Xcm<RuntimeCall>(pub Vec<Instruction<RuntimeCall>>);
288291

292+
environmental::environmental!(instructions_count: u8);
293+
294+
impl<Call> Decode for Xcm<Call> {
295+
fn decode<I: CodecInput>(input: &mut I) -> core::result::Result<Self, CodecError> {
296+
instructions_count::using_once(&mut 0, || {
297+
let number_of_instructions: u32 = <Compact<u32>>::decode(input)?.into();
298+
instructions_count::with(|count| {
299+
*count = count.saturating_add(number_of_instructions as u8);
300+
if *count > MAX_INSTRUCTIONS_TO_DECODE {
301+
return Err(CodecError::from("Max instructions exceeded"))
302+
}
303+
Ok(())
304+
})
305+
.unwrap_or(Ok(()))?;
306+
let decoded_instructions = decode_vec_with_len(input, number_of_instructions as usize)?;
307+
Ok(Self(decoded_instructions))
308+
})
309+
}
310+
}
311+
312+
/// The maximal number of instructions in an XCM before decoding fails.
313+
///
314+
/// This is a deliberate limit - not a technical one.
315+
pub const MAX_INSTRUCTIONS_TO_DECODE: u8 = 100;
316+
289317
impl<RuntimeCall> Xcm<RuntimeCall> {
290318
/// Create an empty instance.
291319
pub fn new() -> Self {
@@ -1157,3 +1185,38 @@ impl<RuntimeCall> TryFrom<NewInstruction<RuntimeCall>> for Instruction<RuntimeCa
11571185
})
11581186
}
11591187
}
1188+
1189+
#[cfg(test)]
1190+
mod tests {
1191+
use super::{prelude::*, *};
1192+
1193+
#[test]
1194+
fn decoding_respects_limit() {
1195+
let max_xcm = Xcm::<()>(vec![ClearOrigin; MAX_INSTRUCTIONS_TO_DECODE as usize]);
1196+
let encoded = max_xcm.encode();
1197+
assert!(Xcm::<()>::decode(&mut &encoded[..]).is_ok());
1198+
1199+
let big_xcm = Xcm::<()>(vec![ClearOrigin; MAX_INSTRUCTIONS_TO_DECODE as usize + 1]);
1200+
let encoded = big_xcm.encode();
1201+
assert!(Xcm::<()>::decode(&mut &encoded[..]).is_err());
1202+
1203+
let nested_xcm = Xcm::<()>(vec![
1204+
DepositReserveAsset {
1205+
assets: All.into(),
1206+
dest: Here.into(),
1207+
xcm: max_xcm,
1208+
max_assets: 1,
1209+
};
1210+
(MAX_INSTRUCTIONS_TO_DECODE / 2) as usize
1211+
]);
1212+
let encoded = nested_xcm.encode();
1213+
assert!(Xcm::<()>::decode(&mut &encoded[..]).is_err());
1214+
1215+
let even_more_nested_xcm = Xcm::<()>(vec![SetAppendix(nested_xcm); 64]);
1216+
let encoded = even_more_nested_xcm.encode();
1217+
assert_eq!(encoded.len(), 345730);
1218+
// This should not decode since the limit is 100
1219+
assert_eq!(MAX_INSTRUCTIONS_TO_DECODE, 100, "precondition");
1220+
assert!(Xcm::<()>::decode(&mut &encoded[..]).is_err());
1221+
}
1222+
}

prdoc/pr_4978.prdoc

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0
2+
# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json
3+
4+
title: Add MAX_INSTRUCTIONS_TO_DECODE to XCMv2
5+
6+
doc:
7+
- audience: Runtime User
8+
description: |
9+
Added a max number of instructions to XCMv2. If using XCMv2, you'll have to take this limit into account.
10+
It was set to 100.
11+
- audience: Runtime Dev
12+
description: |
13+
Added a max number of instructions to XCMv2. If using XCMv2, you'll have to take this limit into account.
14+
It was set to 100.
15+
16+
crates:
17+
- name: staging-xcm
18+
bump: minor

0 commit comments

Comments
 (0)