@@ -62,7 +62,10 @@ use super::{
6262} ;
6363use alloc:: { vec, vec:: Vec } ;
6464use 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+ } ;
6669use core:: { fmt:: Debug , result} ;
6770use derivative:: Derivative ;
6871use scale_info:: TypeInfo ;
@@ -278,14 +281,39 @@ pub const VERSION: super::Version = 2;
278281pub 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" ) ) ]
287290pub 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+
289317impl < 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+ }
0 commit comments