@@ -872,37 +872,79 @@ fn execute_instr(mut input: InstrContext) -> Option<InstrEffects> {
872872 let return_data = transaction_context. get_return_data ( ) . 1 . to_vec ( ) ;
873873
874874 Some ( InstrEffects {
875- custom_err : if let Err ( InstructionError :: Custom ( x) ) = result {
876- Some ( x)
875+ custom_err : if let Err ( InstructionError :: Custom ( code) ) = result {
876+ #[ cfg( feature = "core-bpf" ) ]
877+ // See comment below under `result` for special-casing of custom
878+ // errors for Core BPF programs.
879+ if program_id == & solana_sdk:: config:: program:: id ( ) && ( code == 0 || code == 1 ) {
880+ None
881+ } else {
882+ Some ( code)
883+ }
884+ #[ cfg( not( feature = "core-bpf" ) ) ]
885+ Some ( code)
877886 } else {
878887 None
879888 } ,
880889 #[ allow( clippy:: map_identity) ]
881890 result : result. err ( ) . map ( |err| {
882891 #[ cfg( feature = "core-bpf" ) ]
883- // Some errors don't directly map between builtins and their BPF
884- // versions.
885- //
886- // For example, when a builtin program exceeds the compute budget,
887- // the builtin's `DEFAULT_COMPUTE_UNITS` are deducted from the
888- // meter, and if the meter is exhuasted, the invoke context will
889- // throw `InstructionError::ComputationalBudgetExceeded`.
890- // https://github.com/anza-xyz/agave/blob/6d74d13749829d463fabccebd8203edf0cf4c500/program-runtime/src/invoke_context.rs#L73
891- // https://github.com/anza-xyz/agave/blob/6d74d13749829d463fabccebd8203edf0cf4c500/program-runtime/src/invoke_context.rs#L574
892- //
893- // However, for a BPF program, if the compute meter is exhausted,
894- // the error comes from the VM, and is converted to
895- // `InstructionError::ProgramFailedToComplete`.
896- // https://github.com/solana-labs/rbpf/blob/69a52ec6a341bb7374d387173b5e6dc56218fe0c/src/error.rs#L44
897- // https://github.com/anza-xyz/agave/blob/6d74d13749829d463fabccebd8203edf0cf4c500/program-runtime/src/invoke_context.rs#L547
898- //
899- // Therefore, some errors require reconciliation when testing a BPF
900- // program against its builtin implementation.
901- if err == InstructionError :: ProgramFailedToComplete
902- && ( input. cu_avail <= CORE_BPF_DEFAULT_COMPUTE_UNITS
903- || compute_units_consumed >= input. cu_avail )
904892 {
905- return InstructionError :: ComputationalBudgetExceeded ;
893+ // Some errors don't directly map between builtins and their BPF
894+ // versions.
895+ //
896+ // For example, when a builtin program exceeds the compute budget,
897+ // the builtin's `DEFAULT_COMPUTE_UNITS` are deducted from the
898+ // meter, and if the meter is exhuasted, the invoke context will
899+ // throw `InstructionError::ComputationalBudgetExceeded`.
900+ // https://github.com/anza-xyz/agave/blob/6d74d13749829d463fabccebd8203edf0cf4c500/program-runtime/src/invoke_context.rs#L73
901+ // https://github.com/anza-xyz/agave/blob/6d74d13749829d463fabccebd8203edf0cf4c500/program-runtime/src/invoke_context.rs#L574
902+ //
903+ // However, for a BPF program, if the compute meter is exhausted,
904+ // the error comes from the VM, and is converted to
905+ // `InstructionError::ProgramFailedToComplete`.
906+ // https://github.com/solana-labs/rbpf/blob/69a52ec6a341bb7374d387173b5e6dc56218fe0c/src/error.rs#L44
907+ // https://github.com/anza-xyz/agave/blob/6d74d13749829d463fabccebd8203edf0cf4c500/program-runtime/src/invoke_context.rs#L547
908+ //
909+ // Therefore, some errors require reconciliation when testing a BPF
910+ // program against its builtin implementation.
911+ if err == InstructionError :: ProgramFailedToComplete
912+ && ( input. cu_avail <= CORE_BPF_DEFAULT_COMPUTE_UNITS
913+ || compute_units_consumed >= input. cu_avail )
914+ {
915+ return InstructionError :: ComputationalBudgetExceeded ;
916+ }
917+ // Another such error case arises when a program performs a write
918+ // to an account, but the data it writes is the exact same data
919+ // that's currently stored in the account state.
920+ //
921+ // For builtins, the `TransactionContext` is invoked when any write
922+ // is performed, asking it whether or not a write is allowed,
923+ // regardless of the data being written. If the account is not
924+ // writable, it throws `InstructionError::ReadonlyDataModified`.
925+ //
926+ // For BPF programs, writes to readonly accounts are caught _after_
927+ // the VM finishes execution, when the loader inspects the
928+ // serialized input data region. If a write was performed that did
929+ // not modify serialized account state, then no error is thrown.
930+ //
931+ // As a result, Core BPF programs have been outfitted with custom
932+ // errors when `is_writable` checks fail. These errors are
933+ // special-cased below to avoid fixture mismatches.
934+ match err {
935+ InstructionError :: Custom ( code) => {
936+ if program_id == & solana_sdk:: config:: program:: id ( ) {
937+ // Special-cased custom error codes for the Config program.
938+ if code == 0 {
939+ return InstructionError :: ExecutableDataModified ;
940+ }
941+ if code == 1 {
942+ return InstructionError :: ReadonlyDataModified ;
943+ }
944+ }
945+ }
946+ _ => { }
947+ }
906948 }
907949 err
908950 } ) ,
0 commit comments