77 solana_log_collector:: { ic_logger_msg, LogCollector } ,
88 solana_measure:: measure:: Measure ,
99 solana_program:: {
10+ bpf_loader, bpf_loader_deprecated,
11+ bpf_loader_upgradeable:: { self , UpgradeableLoaderState } ,
1012 loader_v4:: { self , LoaderV4State , LoaderV4Status , DEPLOYMENT_COOLDOWN_IN_SLOTS } ,
1113 loader_v4_instruction:: LoaderV4Instruction ,
1214 } ,
@@ -104,13 +106,10 @@ fn process_instruction_write(
104106 ic_logger_msg ! ( log_collector, "Program is not retracted" ) ;
105107 return Err ( InstructionError :: InvalidArgument ) ;
106108 }
107- let end_offset = ( offset as usize ) . saturating_add ( bytes . len ( ) ) ;
109+ let destination_offset = ( offset as usize ) . saturating_add ( LoaderV4State :: program_data_offset ( ) ) ;
108110 program
109111 . get_data_mut ( ) ?
110- . get_mut (
111- LoaderV4State :: program_data_offset ( ) . saturating_add ( offset as usize )
112- ..LoaderV4State :: program_data_offset ( ) . saturating_add ( end_offset) ,
113- )
112+ . get_mut ( destination_offset..destination_offset. saturating_add ( bytes. len ( ) ) )
114113 . ok_or_else ( || {
115114 ic_logger_msg ! ( log_collector, "Write out of bounds" ) ;
116115 InstructionError :: AccountDataTooSmall
@@ -119,6 +118,65 @@ fn process_instruction_write(
119118 Ok ( ( ) )
120119}
121120
121+ fn process_instruction_copy (
122+ invoke_context : & mut InvokeContext ,
123+ destination_offset : u32 ,
124+ source_offset : u32 ,
125+ length : u32 ,
126+ ) -> Result < ( ) , InstructionError > {
127+ let log_collector = invoke_context. get_log_collector ( ) ;
128+ let transaction_context = & invoke_context. transaction_context ;
129+ let instruction_context = transaction_context. get_current_instruction_context ( ) ?;
130+ let mut program = instruction_context. try_borrow_instruction_account ( transaction_context, 0 ) ?;
131+ let authority_address = instruction_context
132+ . get_index_of_instruction_account_in_transaction ( 1 )
133+ . and_then ( |index| transaction_context. get_key_of_account_at_index ( index) ) ?;
134+ let source_program =
135+ instruction_context. try_borrow_instruction_account ( transaction_context, 2 ) ?;
136+ let state = check_program_account (
137+ & log_collector,
138+ instruction_context,
139+ & program,
140+ authority_address,
141+ ) ?;
142+ if !matches ! ( state. status, LoaderV4Status :: Retracted ) {
143+ ic_logger_msg ! ( log_collector, "Program is not retracted" ) ;
144+ return Err ( InstructionError :: InvalidArgument ) ;
145+ }
146+ let source_owner = & source_program. get_owner ( ) ;
147+ let source_offset =
148+ ( source_offset as usize ) . saturating_add ( if loader_v4:: check_id ( source_owner) {
149+ LoaderV4State :: program_data_offset ( )
150+ } else if bpf_loader_upgradeable:: check_id ( source_owner) {
151+ UpgradeableLoaderState :: size_of_programdata_metadata ( )
152+ } else if bpf_loader_deprecated:: check_id ( source_owner)
153+ || bpf_loader:: check_id ( source_owner)
154+ {
155+ 0
156+ } else {
157+ ic_logger_msg ! ( log_collector, "Source is not a program" ) ;
158+ return Err ( InstructionError :: InvalidArgument ) ;
159+ } ) ;
160+ let data = source_program
161+ . get_data ( )
162+ . get ( source_offset..source_offset. saturating_add ( length as usize ) )
163+ . ok_or_else ( || {
164+ ic_logger_msg ! ( log_collector, "Read out of bounds" ) ;
165+ InstructionError :: AccountDataTooSmall
166+ } ) ?;
167+ let destination_offset =
168+ ( destination_offset as usize ) . saturating_add ( LoaderV4State :: program_data_offset ( ) ) ;
169+ program
170+ . get_data_mut ( ) ?
171+ . get_mut ( destination_offset..destination_offset. saturating_add ( length as usize ) )
172+ . ok_or_else ( || {
173+ ic_logger_msg ! ( log_collector, "Write out of bounds" ) ;
174+ InstructionError :: AccountDataTooSmall
175+ } ) ?
176+ . copy_from_slice ( data) ;
177+ Ok ( ( ) )
178+ }
179+
122180fn process_instruction_truncate (
123181 invoke_context : & mut InvokeContext ,
124182 new_size : u32 ,
@@ -432,6 +490,13 @@ fn process_instruction_inner(
432490 LoaderV4Instruction :: Write { offset, bytes } => {
433491 process_instruction_write ( invoke_context, offset, bytes)
434492 }
493+ LoaderV4Instruction :: Copy {
494+ destination_offset,
495+ source_offset,
496+ length,
497+ } => {
498+ process_instruction_copy ( invoke_context, destination_offset, source_offset, length)
499+ }
435500 LoaderV4Instruction :: Truncate { new_size } => {
436501 process_instruction_truncate ( invoke_context, new_size)
437502 }
@@ -755,6 +820,141 @@ mod tests {
755820 } ) ;
756821 }
757822
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 ( loader_v4:: 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 ( loader_v4:: 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+
758958 #[ test]
759959 fn test_loader_instruction_truncate ( ) {
760960 let authority_address = Pubkey :: new_unique ( ) ;
0 commit comments