@@ -15,6 +15,8 @@ use std::mem::size_of;
1515pub const MIN_SIGNERS : usize = 1 ;
1616/// Maximum number of multisignature signers (max N)
1717pub const MAX_SIGNERS : usize = 11 ;
18+ /// Serialized length of a u64, for unpacking
19+ const U64_BYTES : usize = 8 ;
1820
1921/// Instructions supported by the token program.
2022#[ repr( C ) ]
@@ -519,47 +521,19 @@ impl<'a> TokenInstruction<'a> {
519521 10 => Self :: FreezeAccount ,
520522 11 => Self :: ThawAccount ,
521523 12 => {
522- let ( amount, rest) = rest. split_at ( 8 ) ;
523- let amount = amount
524- . try_into ( )
525- . ok ( )
526- . map ( u64:: from_le_bytes)
527- . ok_or ( InvalidInstruction ) ?;
528- let ( & decimals, _rest) = rest. split_first ( ) . ok_or ( InvalidInstruction ) ?;
529-
524+ let ( amount, decimals, _rest) = Self :: unpack_amount_decimals ( rest) ?;
530525 Self :: TransferChecked { amount, decimals }
531526 }
532527 13 => {
533- let ( amount, rest) = rest. split_at ( 8 ) ;
534- let amount = amount
535- . try_into ( )
536- . ok ( )
537- . map ( u64:: from_le_bytes)
538- . ok_or ( InvalidInstruction ) ?;
539- let ( & decimals, _rest) = rest. split_first ( ) . ok_or ( InvalidInstruction ) ?;
540-
528+ let ( amount, decimals, _rest) = Self :: unpack_amount_decimals ( rest) ?;
541529 Self :: ApproveChecked { amount, decimals }
542530 }
543531 14 => {
544- let ( amount, rest) = rest. split_at ( 8 ) ;
545- let amount = amount
546- . try_into ( )
547- . ok ( )
548- . map ( u64:: from_le_bytes)
549- . ok_or ( InvalidInstruction ) ?;
550- let ( & decimals, _rest) = rest. split_first ( ) . ok_or ( InvalidInstruction ) ?;
551-
532+ let ( amount, decimals, _rest) = Self :: unpack_amount_decimals ( rest) ?;
552533 Self :: MintToChecked { amount, decimals }
553534 }
554535 15 => {
555- let ( amount, rest) = rest. split_at ( 8 ) ;
556- let amount = amount
557- . try_into ( )
558- . ok ( )
559- . map ( u64:: from_le_bytes)
560- . ok_or ( InvalidInstruction ) ?;
561- let ( & decimals, _rest) = rest. split_first ( ) . ok_or ( InvalidInstruction ) ?;
562-
536+ let ( amount, decimals, _rest) = Self :: unpack_amount_decimals ( rest) ?;
563537 Self :: BurnChecked { amount, decimals }
564538 }
565539 16 => {
@@ -588,12 +562,7 @@ impl<'a> TokenInstruction<'a> {
588562 21 => Self :: GetAccountDataSize ,
589563 22 => Self :: InitializeImmutableOwner ,
590564 23 => {
591- let ( amount, _rest) = rest. split_at ( 8 ) ;
592- let amount = amount
593- . try_into ( )
594- . ok ( )
595- . map ( u64:: from_le_bytes)
596- . ok_or ( InvalidInstruction ) ?;
565+ let ( amount, _rest) = Self :: unpack_u64 ( rest) ?;
597566 Self :: AmountToUiAmount { amount }
598567 }
599568 24 => {
@@ -745,6 +714,21 @@ impl<'a> TokenInstruction<'a> {
745714 COption :: None => buf. push ( 0 ) ,
746715 }
747716 }
717+
718+ fn unpack_u64 ( input : & [ u8 ] ) -> Result < ( u64 , & [ u8 ] ) , ProgramError > {
719+ let value = input
720+ . get ( ..U64_BYTES )
721+ . and_then ( |slice| slice. try_into ( ) . ok ( ) )
722+ . map ( u64:: from_le_bytes)
723+ . ok_or ( TokenError :: InvalidInstruction ) ?;
724+ Ok ( ( value, & input[ U64_BYTES ..] ) )
725+ }
726+
727+ fn unpack_amount_decimals ( input : & [ u8 ] ) -> Result < ( u64 , u8 , & [ u8 ] ) , ProgramError > {
728+ let ( amount, rest) = Self :: unpack_u64 ( input) ?;
729+ let ( & decimals, rest) = rest. split_first ( ) . ok_or ( TokenError :: InvalidInstruction ) ?;
730+ Ok ( ( amount, decimals, rest) )
731+ }
748732}
749733
750734/// Specifies the authority type for SetAuthority instructions
@@ -1447,7 +1431,7 @@ pub fn is_valid_signer_index(index: usize) -> bool {
14471431
14481432#[ cfg( test) ]
14491433mod test {
1450- use super :: * ;
1434+ use { super :: * , proptest :: prelude :: * } ;
14511435
14521436 #[ test]
14531437 fn test_instruction_packing ( ) {
@@ -1689,4 +1673,25 @@ mod test {
16891673 let unpacked = TokenInstruction :: unpack ( & expect) . unwrap ( ) ;
16901674 assert_eq ! ( unpacked, check) ;
16911675 }
1676+
1677+ #[ test]
1678+ fn test_instruction_unpack_panic ( ) {
1679+ for i in 0 ..255u8 {
1680+ for j in 1 ..10 {
1681+ let mut data = vec ! [ 0 ; j] ;
1682+ data[ 0 ] = i;
1683+ let _no_panic = TokenInstruction :: unpack ( & data) ;
1684+ }
1685+ }
1686+ }
1687+
1688+ proptest ! {
1689+ #![ proptest_config( ProptestConfig :: with_cases( 1024 ) ) ]
1690+ #[ test]
1691+ fn test_instruction_unpack_proptest(
1692+ data in prop:: collection:: vec( any:: <u8 >( ) , 0 ..255 )
1693+ ) {
1694+ let _no_panic = TokenInstruction :: unpack( & data) ;
1695+ }
1696+ }
16921697}
0 commit comments