44 solana_bincode:: limited_deserialize,
55 solana_bpf_loader_program:: { deploy_program, execute} ,
66 solana_instruction:: error:: InstructionError ,
7+ solana_loader_v3_interface:: state:: UpgradeableLoaderState ,
78 solana_loader_v4_interface:: {
89 instruction:: LoaderV4Instruction ,
910 state:: { LoaderV4State , LoaderV4Status } ,
1718 } ,
1819 solana_pubkey:: Pubkey ,
1920 solana_sbpf:: { declare_builtin_function, memory_region:: MemoryMapping } ,
20- solana_sdk_ids:: loader_v4,
21+ solana_sdk_ids:: { bpf_loader , bpf_loader_deprecated , bpf_loader_upgradeable , loader_v4} ,
2122 solana_transaction_context:: { BorrowedAccount , InstructionContext } ,
2223 solana_type_overrides:: sync:: { atomic:: Ordering , Arc } ,
2324 std:: { cell:: RefCell , rc:: Rc } ,
@@ -106,13 +107,10 @@ fn process_instruction_write(
106107 ic_logger_msg ! ( log_collector, "Program is not retracted" ) ;
107108 return Err ( InstructionError :: InvalidArgument ) ;
108109 }
109- let end_offset = ( offset as usize ) . saturating_add ( bytes . len ( ) ) ;
110+ let destination_offset = ( offset as usize ) . saturating_add ( LoaderV4State :: program_data_offset ( ) ) ;
110111 program
111112 . get_data_mut ( ) ?
112- . get_mut (
113- LoaderV4State :: program_data_offset ( ) . saturating_add ( offset as usize )
114- ..LoaderV4State :: program_data_offset ( ) . saturating_add ( end_offset) ,
115- )
113+ . get_mut ( destination_offset..destination_offset. saturating_add ( bytes. len ( ) ) )
116114 . ok_or_else ( || {
117115 ic_logger_msg ! ( log_collector, "Write out of bounds" ) ;
118116 InstructionError :: AccountDataTooSmall
@@ -121,6 +119,65 @@ fn process_instruction_write(
121119 Ok ( ( ) )
122120}
123121
122+ fn process_instruction_copy (
123+ invoke_context : & mut InvokeContext ,
124+ destination_offset : u32 ,
125+ source_offset : u32 ,
126+ length : u32 ,
127+ ) -> Result < ( ) , InstructionError > {
128+ let log_collector = invoke_context. get_log_collector ( ) ;
129+ let transaction_context = & invoke_context. transaction_context ;
130+ let instruction_context = transaction_context. get_current_instruction_context ( ) ?;
131+ let mut program = instruction_context. try_borrow_instruction_account ( transaction_context, 0 ) ?;
132+ let authority_address = instruction_context
133+ . get_index_of_instruction_account_in_transaction ( 1 )
134+ . and_then ( |index| transaction_context. get_key_of_account_at_index ( index) ) ?;
135+ let source_program =
136+ instruction_context. try_borrow_instruction_account ( transaction_context, 2 ) ?;
137+ let state = check_program_account (
138+ & log_collector,
139+ instruction_context,
140+ & program,
141+ authority_address,
142+ ) ?;
143+ if !matches ! ( state. status, LoaderV4Status :: Retracted ) {
144+ ic_logger_msg ! ( log_collector, "Program is not retracted" ) ;
145+ return Err ( InstructionError :: InvalidArgument ) ;
146+ }
147+ let source_owner = & source_program. get_owner ( ) ;
148+ let source_offset =
149+ ( source_offset as usize ) . saturating_add ( if loader_v4:: check_id ( source_owner) {
150+ LoaderV4State :: program_data_offset ( )
151+ } else if bpf_loader_upgradeable:: check_id ( source_owner) {
152+ UpgradeableLoaderState :: size_of_programdata_metadata ( )
153+ } else if bpf_loader_deprecated:: check_id ( source_owner)
154+ || bpf_loader:: check_id ( source_owner)
155+ {
156+ 0
157+ } else {
158+ ic_logger_msg ! ( log_collector, "Source is not a program" ) ;
159+ return Err ( InstructionError :: InvalidArgument ) ;
160+ } ) ;
161+ let data = source_program
162+ . get_data ( )
163+ . get ( source_offset..source_offset. saturating_add ( length as usize ) )
164+ . ok_or_else ( || {
165+ ic_logger_msg ! ( log_collector, "Read out of bounds" ) ;
166+ InstructionError :: AccountDataTooSmall
167+ } ) ?;
168+ let destination_offset =
169+ ( destination_offset as usize ) . saturating_add ( LoaderV4State :: program_data_offset ( ) ) ;
170+ program
171+ . get_data_mut ( ) ?
172+ . get_mut ( destination_offset..destination_offset. saturating_add ( length as usize ) )
173+ . ok_or_else ( || {
174+ ic_logger_msg ! ( log_collector, "Write out of bounds" ) ;
175+ InstructionError :: AccountDataTooSmall
176+ } ) ?
177+ . copy_from_slice ( data) ;
178+ Ok ( ( ) )
179+ }
180+
124181fn process_instruction_truncate (
125182 invoke_context : & mut InvokeContext ,
126183 new_size : u32 ,
@@ -434,6 +491,13 @@ fn process_instruction_inner(
434491 LoaderV4Instruction :: Write { offset, bytes } => {
435492 process_instruction_write ( invoke_context, offset, bytes)
436493 }
494+ LoaderV4Instruction :: Copy {
495+ destination_offset,
496+ source_offset,
497+ length,
498+ } => {
499+ process_instruction_copy ( invoke_context, destination_offset, source_offset, length)
500+ }
437501 LoaderV4Instruction :: Truncate { new_size } => {
438502 process_instruction_truncate ( invoke_context, new_size)
439503 }
@@ -756,6 +820,141 @@ mod tests {
756820 } ) ;
757821 }
758822
823+ #[ test]
824+ fn test_loader_instruction_copy ( ) {
825+ let authority_address = Pubkey :: new_unique ( ) ;
826+ let transaction_accounts = vec ! [
827+ (
828+ Pubkey :: new_unique( ) ,
829+ load_program_account_from_elf(
830+ authority_address,
831+ LoaderV4Status :: Retracted ,
832+ "sbpfv3_return_err" ,
833+ ) ,
834+ ) ,
835+ (
836+ authority_address,
837+ AccountSharedData :: new( 0 , 0 , & Pubkey :: new_unique( ) ) ,
838+ ) ,
839+ (
840+ Pubkey :: new_unique( ) ,
841+ load_program_account_from_elf(
842+ authority_address,
843+ LoaderV4Status :: Deployed ,
844+ "sbpfv3_return_err" ,
845+ ) ,
846+ ) ,
847+ (
848+ clock:: id( ) ,
849+ create_account_shared_data_for_test( & clock:: Clock :: default ( ) ) ,
850+ ) ,
851+ (
852+ rent:: id( ) ,
853+ create_account_shared_data_for_test( & rent:: Rent :: default ( ) ) ,
854+ ) ,
855+ ] ;
856+
857+ // Overwrite existing data
858+ process_instruction (
859+ vec ! [ ] ,
860+ & bincode:: serialize ( & LoaderV4Instruction :: Copy {
861+ destination_offset : 1 ,
862+ source_offset : 2 ,
863+ length : 3 ,
864+ } )
865+ . unwrap ( ) ,
866+ transaction_accounts. clone ( ) ,
867+ & [ ( 0 , false , true ) , ( 1 , true , false ) , ( 2 , false , false ) ] ,
868+ Ok ( ( ) ) ,
869+ ) ;
870+
871+ // Empty copy
872+ process_instruction (
873+ vec ! [ ] ,
874+ & bincode:: serialize ( & LoaderV4Instruction :: Copy {
875+ destination_offset : 1 ,
876+ source_offset : 2 ,
877+ length : 0 ,
878+ } )
879+ . unwrap ( ) ,
880+ transaction_accounts. clone ( ) ,
881+ & [ ( 0 , false , true ) , ( 1 , true , false ) , ( 2 , false , false ) ] ,
882+ Ok ( ( ) ) ,
883+ ) ;
884+
885+ // Error: Program is not retracted
886+ process_instruction (
887+ vec ! [ ] ,
888+ & bincode:: serialize ( & LoaderV4Instruction :: Copy {
889+ destination_offset : 1 ,
890+ source_offset : 2 ,
891+ length : 3 ,
892+ } )
893+ . unwrap ( ) ,
894+ transaction_accounts. clone ( ) ,
895+ & [ ( 2 , false , true ) , ( 1 , true , false ) , ( 0 , false , false ) ] ,
896+ Err ( InstructionError :: InvalidArgument ) ,
897+ ) ;
898+
899+ // Error: Destination and source collide
900+ process_instruction (
901+ vec ! [ ] ,
902+ & bincode:: serialize ( & LoaderV4Instruction :: Copy {
903+ destination_offset : 1 ,
904+ source_offset : 2 ,
905+ length : 3 ,
906+ } )
907+ . unwrap ( ) ,
908+ transaction_accounts. clone ( ) ,
909+ & [ ( 2 , false , true ) , ( 1 , true , false ) , ( 2 , false , false ) ] ,
910+ Err ( InstructionError :: AccountBorrowFailed ) ,
911+ ) ;
912+
913+ // Error: Read out of bounds
914+ process_instruction (
915+ vec ! [ ] ,
916+ & bincode:: serialize ( & LoaderV4Instruction :: Copy {
917+ destination_offset : 1 ,
918+ source_offset : transaction_accounts[ 2 ]
919+ . 1
920+ . data ( )
921+ . len ( )
922+ . saturating_sub ( LoaderV4State :: program_data_offset ( ) )
923+ . saturating_sub ( 3 ) as u32 ,
924+ length : 4 ,
925+ } )
926+ . unwrap ( ) ,
927+ transaction_accounts. clone ( ) ,
928+ & [ ( 0 , false , true ) , ( 1 , true , false ) , ( 2 , false , false ) ] ,
929+ Err ( InstructionError :: AccountDataTooSmall ) ,
930+ ) ;
931+
932+ // Error: Write out of bounds
933+ process_instruction (
934+ vec ! [ ] ,
935+ & bincode:: serialize ( & LoaderV4Instruction :: Copy {
936+ destination_offset : transaction_accounts[ 0 ]
937+ . 1
938+ . data ( )
939+ . len ( )
940+ . saturating_sub ( LoaderV4State :: program_data_offset ( ) )
941+ . saturating_sub ( 3 ) as u32 ,
942+ source_offset : 2 ,
943+ length : 4 ,
944+ } )
945+ . unwrap ( ) ,
946+ transaction_accounts. clone ( ) ,
947+ & [ ( 0 , false , true ) , ( 1 , true , false ) , ( 2 , false , false ) ] ,
948+ Err ( InstructionError :: AccountDataTooSmall ) ,
949+ ) ;
950+
951+ test_loader_instruction_general_errors ( LoaderV4Instruction :: Copy {
952+ destination_offset : 1 ,
953+ source_offset : 2 ,
954+ length : 3 ,
955+ } ) ;
956+ }
957+
759958 #[ test]
760959 fn test_loader_instruction_truncate ( ) {
761960 let authority_address = Pubkey :: new_unique ( ) ;
0 commit comments