7
7
solana_log_collector:: { ic_logger_msg, LogCollector } ,
8
8
solana_measure:: measure:: Measure ,
9
9
solana_program:: {
10
+ bpf_loader, bpf_loader_deprecated,
11
+ bpf_loader_upgradeable:: { self , UpgradeableLoaderState } ,
10
12
loader_v4:: { self , LoaderV4State , LoaderV4Status , DEPLOYMENT_COOLDOWN_IN_SLOTS } ,
11
13
loader_v4_instruction:: LoaderV4Instruction ,
12
14
} ,
@@ -104,13 +106,10 @@ fn process_instruction_write(
104
106
ic_logger_msg ! ( log_collector, "Program is not retracted" ) ;
105
107
return Err ( InstructionError :: InvalidArgument ) ;
106
108
}
107
- let end_offset = ( offset as usize ) . saturating_add ( bytes . len ( ) ) ;
109
+ let destination_offset = ( offset as usize ) . saturating_add ( LoaderV4State :: program_data_offset ( ) ) ;
108
110
program
109
111
. 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 ( ) ) )
114
113
. ok_or_else ( || {
115
114
ic_logger_msg ! ( log_collector, "Write out of bounds" ) ;
116
115
InstructionError :: AccountDataTooSmall
@@ -119,6 +118,65 @@ fn process_instruction_write(
119
118
Ok ( ( ) )
120
119
}
121
120
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
+
122
180
fn process_instruction_truncate (
123
181
invoke_context : & mut InvokeContext ,
124
182
new_size : u32 ,
@@ -432,6 +490,13 @@ fn process_instruction_inner(
432
490
LoaderV4Instruction :: Write { offset, bytes } => {
433
491
process_instruction_write ( invoke_context, offset, bytes)
434
492
}
493
+ LoaderV4Instruction :: Copy {
494
+ destination_offset,
495
+ source_offset,
496
+ length,
497
+ } => {
498
+ process_instruction_copy ( invoke_context, destination_offset, source_offset, length)
499
+ }
435
500
LoaderV4Instruction :: Truncate { new_size } => {
436
501
process_instruction_truncate ( invoke_context, new_size)
437
502
}
@@ -755,6 +820,141 @@ mod tests {
755
820
} ) ;
756
821
}
757
822
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
+
758
958
#[ test]
759
959
fn test_loader_instruction_truncate ( ) {
760
960
let authority_address = Pubkey :: new_unique ( ) ;
0 commit comments