From 03abd3ddd70b1e362098be5bbf253e4169891d4c Mon Sep 17 00:00:00 2001 From: Jack May Date: Tue, 26 May 2020 01:02:31 -0700 Subject: [PATCH] Prevent privilege escalation (#10232) automerge --- programs/bpf/benches/bpf_loader.rs | 3 +- programs/bpf/c/src/invoke/invoke.c | 251 ++++++++++------- programs/bpf/c/src/invoked/instruction.h | 15 +- programs/bpf/c/src/invoked/invoked.c | 3 + programs/bpf/rust/invoke/src/lib.rs | 268 +++++++++++-------- programs/bpf/rust/invoked/src/instruction.rs | 3 +- programs/bpf/rust/invoked/src/lib.rs | 3 + programs/bpf/tests/programs.rs | 56 +++- programs/bpf_loader/src/lib.rs | 19 +- programs/bpf_loader/src/syscalls.rs | 74 ++++- runtime/src/message_processor.rs | 146 +--------- sdk/src/entrypoint_native.rs | 1 - sdk/src/instruction.rs | 8 - 13 files changed, 458 insertions(+), 392 deletions(-) diff --git a/programs/bpf/benches/bpf_loader.rs b/programs/bpf/benches/bpf_loader.rs index cd1101dbdf..28008650f6 100644 --- a/programs/bpf/benches/bpf_loader.rs +++ b/programs/bpf/benches/bpf_loader.rs @@ -80,7 +80,7 @@ fn bench_program_alu(bencher: &mut Bencher) { let mut invoke_context = MockInvokeContext::default(); let elf = load_elf().unwrap(); - let (mut vm, _) = solana_bpf_loader_program::create_vm(&elf, &mut invoke_context).unwrap(); + let (mut vm, _) = solana_bpf_loader_program::create_vm(&elf, &[], &mut invoke_context).unwrap(); println!("Interpreted:"); assert_eq!( @@ -145,7 +145,6 @@ impl InvokeContext for MockInvokeContext { &mut self, _message: &Message, _instruction: &CompiledInstruction, - _signers: &[Pubkey], _accounts: &[Rc>], ) -> Result<(), InstructionError> { Ok(()) diff --git a/programs/bpf/c/src/invoke/invoke.c b/programs/bpf/c/src/invoke/invoke.c index 0439ae881f..e94038b821 100644 --- a/programs/bpf/c/src/invoke/invoke.c +++ b/programs/bpf/c/src/invoke/invoke.c @@ -4,6 +4,10 @@ #include "../invoked/instruction.h" #include +static const uint8_t TEST_SUCCESS = 1; +static const uint8_t TEST_PRIVILEGE_ESCALATION_SIGNER = 2; +static const uint8_t TEST_PRIVILEGE_ESCALATION_WRITABLE = 3; + static const int MINT_INDEX = 0; static const int ARGUMENT_INDEX = 1; static const int INVOKED_PROGRAM_INDEX = 2; @@ -26,127 +30,168 @@ extern uint64_t entrypoint(const uint8_t *input) { return ERROR_INVALID_ARGUMENT; } - sol_log("Call system program"); - { - sol_assert(*accounts[FROM_INDEX].lamports = 43); - sol_assert(*accounts[ARGUMENT_INDEX].lamports = 41); - SolAccountMeta arguments[] = {{accounts[FROM_INDEX].key, false, true}, - {accounts[ARGUMENT_INDEX].key, false, false}}; - uint8_t data[] = {2, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0}; - const SolInstruction instruction = {accounts[SYSTEM_PROGRAM_INDEX].key, - arguments, SOL_ARRAY_SIZE(arguments), - data, SOL_ARRAY_SIZE(data)}; - sol_assert(SUCCESS == - sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts))); - sol_assert(*accounts[FROM_INDEX].lamports = 42); - sol_assert(*accounts[ARGUMENT_INDEX].lamports = 42); - } - - sol_log("Test data translation"); - { - for (int i = 0; i < accounts[ARGUMENT_INDEX].data_len; i++) { - accounts[ARGUMENT_INDEX].data[i] = i; + switch (params.data[0]) { + case TEST_SUCCESS: { + sol_log("Call system program"); + { + sol_assert(*accounts[FROM_INDEX].lamports = 43); + sol_assert(*accounts[ARGUMENT_INDEX].lamports = 41); + SolAccountMeta arguments[] = { + {accounts[FROM_INDEX].key, false, true}, + {accounts[ARGUMENT_INDEX].key, false, false}}; + uint8_t data[] = {2, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0}; + const SolInstruction instruction = {accounts[SYSTEM_PROGRAM_INDEX].key, + arguments, SOL_ARRAY_SIZE(arguments), + data, SOL_ARRAY_SIZE(data)}; + sol_assert(SUCCESS == + sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts))); + sol_assert(*accounts[FROM_INDEX].lamports = 42); + sol_assert(*accounts[ARGUMENT_INDEX].lamports = 42); } - SolAccountMeta arguments[] = { - {accounts[ARGUMENT_INDEX].key, true, true}, - {accounts[INVOKED_ARGUMENT_INDEX].key, true, true}, - {accounts[INVOKED_PROGRAM_INDEX].key, false, false}, - {accounts[INVOKED_PROGRAM_DUP_INDEX].key, false, false}}; - uint8_t data[] = {TEST_VERIFY_TRANSLATIONS, 1, 2, 3, 4, 5}; - const SolInstruction instruction = {accounts[INVOKED_PROGRAM_INDEX].key, - arguments, SOL_ARRAY_SIZE(arguments), - data, SOL_ARRAY_SIZE(data)}; + sol_log("Test data translation"); + { + for (int i = 0; i < accounts[ARGUMENT_INDEX].data_len; i++) { + accounts[ARGUMENT_INDEX].data[i] = i; + } - sol_assert(SUCCESS == - sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts))); + SolAccountMeta arguments[] = { + {accounts[ARGUMENT_INDEX].key, true, true}, + {accounts[INVOKED_ARGUMENT_INDEX].key, true, true}, + {accounts[INVOKED_PROGRAM_INDEX].key, false, false}, + {accounts[INVOKED_PROGRAM_DUP_INDEX].key, false, false}}; + uint8_t data[] = {TEST_VERIFY_TRANSLATIONS, 1, 2, 3, 4, 5}; + const SolInstruction instruction = {accounts[INVOKED_PROGRAM_INDEX].key, + arguments, SOL_ARRAY_SIZE(arguments), + data, SOL_ARRAY_SIZE(data)}; + + sol_assert(SUCCESS == + sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts))); + } + + sol_log("Test return error"); + { + SolAccountMeta arguments[] = {{accounts[ARGUMENT_INDEX].key, true, true}}; + uint8_t data[] = {TEST_RETURN_ERROR}; + const SolInstruction instruction = {accounts[INVOKED_PROGRAM_INDEX].key, + arguments, SOL_ARRAY_SIZE(arguments), + data, SOL_ARRAY_SIZE(data)}; + + sol_assert(42 == + sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts))); + } + + sol_log("Test derived signers"); + { + sol_assert(!accounts[DERIVED_KEY1_INDEX].is_signer); + sol_assert(!accounts[DERIVED_KEY2_INDEX].is_signer); + sol_assert(!accounts[DERIVED_KEY3_INDEX].is_signer); + + SolAccountMeta arguments[] = { + {accounts[INVOKED_PROGRAM_INDEX].key, false, false}, + {accounts[DERIVED_KEY1_INDEX].key, true, true}, + {accounts[DERIVED_KEY2_INDEX].key, true, false}, + {accounts[DERIVED_KEY3_INDEX].key, false, false}}; + uint8_t data[] = {TEST_DERIVED_SIGNERS}; + const SolInstruction instruction = {accounts[INVOKED_PROGRAM_INDEX].key, + arguments, SOL_ARRAY_SIZE(arguments), + data, SOL_ARRAY_SIZE(data)}; + char seed1[] = "You pass butter"; + char seed2[] = "Lil'"; + char seed3[] = "Bits"; + const SolSignerSeed seeds1[] = {{seed1, sol_strlen(seed1)}}; + const SolSignerSeed seeds2[] = {{seed2, sol_strlen(seed2)}, + {seed3, sol_strlen(seed3)}}; + const SolSignerSeeds signers_seeds[] = {{seeds1, SOL_ARRAY_SIZE(seeds1)}, + {seeds2, SOL_ARRAY_SIZE(seeds2)}}; + sol_assert(SUCCESS == sol_invoke_signed(&instruction, accounts, + SOL_ARRAY_SIZE(accounts), + signers_seeds, + SOL_ARRAY_SIZE(signers_seeds))); + } + + sol_log("Test readonly with writable account"); + { + SolAccountMeta arguments[] = { + {accounts[INVOKED_ARGUMENT_INDEX].key, true, false}}; + uint8_t data[] = {TEST_VERIFY_WRITER}; + const SolInstruction instruction = {accounts[INVOKED_PROGRAM_INDEX].key, + arguments, SOL_ARRAY_SIZE(arguments), + data, SOL_ARRAY_SIZE(data)}; + + sol_assert(SUCCESS == + sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts))); + } + + sol_log("Test invoke"); + { + sol_assert(accounts[ARGUMENT_INDEX].is_signer); + + *accounts[ARGUMENT_INDEX].lamports -= 5; + *accounts[INVOKED_ARGUMENT_INDEX].lamports += 5; + + SolAccountMeta arguments[] = { + {accounts[INVOKED_ARGUMENT_INDEX].key, true, true}, + {accounts[ARGUMENT_INDEX].key, true, true}}; + uint8_t data[] = {TEST_NESTED_INVOKE}; + const SolInstruction instruction = {accounts[INVOKED_PROGRAM_INDEX].key, + arguments, SOL_ARRAY_SIZE(arguments), + data, SOL_ARRAY_SIZE(data)}; + + sol_log("First invoke"); + sol_assert(SUCCESS == + sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts))); + sol_log("2nd invoke from first program"); + sol_assert(SUCCESS == + sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts))); + + sol_assert(*accounts[ARGUMENT_INDEX].lamports == 42 - 5 + 1 + 1); + sol_assert(*accounts[INVOKED_ARGUMENT_INDEX].lamports == 10 + 5 - 1 - 1); + } + + sol_log("Verify data values are retained and updated"); + for (int i = 0; i < accounts[ARGUMENT_INDEX].data_len; i++) { + sol_assert(accounts[ARGUMENT_INDEX].data[i] == i); + } + for (int i = 0; i < accounts[INVOKED_ARGUMENT_INDEX].data_len; i++) { + sol_assert(accounts[INVOKED_ARGUMENT_INDEX].data[i] == i); + } + break; } - - sol_log("Test return error"); - { - SolAccountMeta arguments[] = {{accounts[ARGUMENT_INDEX].key, true, true}}; - uint8_t data[] = {TEST_RETURN_ERROR}; - const SolInstruction instruction = {accounts[INVOKED_PROGRAM_INDEX].key, - arguments, SOL_ARRAY_SIZE(arguments), - data, SOL_ARRAY_SIZE(data)}; - - sol_assert(42 == - sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts))); - } - - sol_log("Test derived signers"); - { - sol_assert(!accounts[DERIVED_KEY1_INDEX].is_signer); - sol_assert(!accounts[DERIVED_KEY2_INDEX].is_signer); - sol_assert(!accounts[DERIVED_KEY3_INDEX].is_signer); - + case TEST_PRIVILEGE_ESCALATION_SIGNER: { + sol_log("Test privilege escalation signer"); SolAccountMeta arguments[] = { - {accounts[INVOKED_PROGRAM_INDEX].key, false, false}, - {accounts[DERIVED_KEY1_INDEX].key, true, true}, - {accounts[DERIVED_KEY2_INDEX].key, true, false}, {accounts[DERIVED_KEY3_INDEX].key, false, false}}; - uint8_t data[] = {TEST_DERIVED_SIGNERS}; + uint8_t data[] = {TEST_VERIFY_PRIVILEGE_ESCALATION}; const SolInstruction instruction = {accounts[INVOKED_PROGRAM_INDEX].key, arguments, SOL_ARRAY_SIZE(arguments), data, SOL_ARRAY_SIZE(data)}; - char seed1[] = "You pass butter"; - char seed2[] = "Lil'"; - char seed3[] = "Bits"; - const SolSignerSeed seeds1[] = {{seed1, sol_strlen(seed1)}}; - const SolSignerSeed seeds2[] = {{seed2, sol_strlen(seed2)}, - {seed3, sol_strlen(seed3)}}; - const SolSignerSeeds signers_seeds[] = {{seeds1, SOL_ARRAY_SIZE(seeds1)}, - {seeds2, SOL_ARRAY_SIZE(seeds2)}}; - sol_assert(SUCCESS == sol_invoke_signed( - &instruction, accounts, SOL_ARRAY_SIZE(accounts), - signers_seeds, SOL_ARRAY_SIZE(signers_seeds))); - } + sol_assert(SUCCESS == + sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts))); - sol_log("Test readonly with writable account"); - { + instruction.accounts[0].is_signer = true; + sol_assert(SUCCESS != + sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts))); + break; + } + case TEST_PRIVILEGE_ESCALATION_WRITABLE: { + sol_log("Test privilege escalation writable"); SolAccountMeta arguments[] = { - {accounts[INVOKED_ARGUMENT_INDEX].key, true, false}}; - uint8_t data[] = {TEST_VERIFY_WRITER}; + {accounts[DERIVED_KEY3_INDEX].key, false, false}}; + uint8_t data[] = {TEST_VERIFY_PRIVILEGE_ESCALATION}; const SolInstruction instruction = {accounts[INVOKED_PROGRAM_INDEX].key, arguments, SOL_ARRAY_SIZE(arguments), data, SOL_ARRAY_SIZE(data)}; - - sol_assert(SUCCESS == - sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts))); - } - - sol_log("Test invoke"); - { - sol_assert(accounts[ARGUMENT_INDEX].is_signer); - - *accounts[ARGUMENT_INDEX].lamports -= 5; - *accounts[INVOKED_ARGUMENT_INDEX].lamports += 5; - - SolAccountMeta arguments[] = { - {accounts[INVOKED_ARGUMENT_INDEX].key, true, true}, - {accounts[ARGUMENT_INDEX].key, true, true}}; - uint8_t data[] = {TEST_NESTED_INVOKE}; - const SolInstruction instruction = {accounts[INVOKED_PROGRAM_INDEX].key, - arguments, SOL_ARRAY_SIZE(arguments), - data, SOL_ARRAY_SIZE(data)}; - - sol_log("Fist invoke"); - sol_assert(SUCCESS == - sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts))); - sol_log("2nd invoke from first program"); sol_assert(SUCCESS == sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts))); - sol_assert(*accounts[ARGUMENT_INDEX].lamports == 42 - 5 + 1 + 1); - sol_assert(*accounts[INVOKED_ARGUMENT_INDEX].lamports == 10 + 5 - 1 - 1); + instruction.accounts[0].is_writable = true; + sol_assert(SUCCESS != + sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts))); + break; } - - sol_log("Verify data values are retained and updated"); - for (int i = 0; i < accounts[ARGUMENT_INDEX].data_len; i++) { - sol_assert(accounts[ARGUMENT_INDEX].data[i] == i); - } - for (int i = 0; i < accounts[INVOKED_ARGUMENT_INDEX].data_len; i++) { - sol_assert(accounts[INVOKED_ARGUMENT_INDEX].data[i] == i); + default: + sol_panic(); } return SUCCESS; diff --git a/programs/bpf/c/src/invoked/instruction.h b/programs/bpf/c/src/invoked/instruction.h index c63da79df6..2e4b25479e 100644 --- a/programs/bpf/c/src/invoked/instruction.h +++ b/programs/bpf/c/src/invoked/instruction.h @@ -2,9 +2,12 @@ * @brief Instruction definitions for the invoked program */ -const int TEST_VERIFY_TRANSLATIONS = 0; -const int TEST_RETURN_ERROR = 1; -const int TEST_DERIVED_SIGNERS = 2; -const int TEST_VERIFY_NESTED_SIGNERS = 3; -const int TEST_VERIFY_WRITER = 4; -const int TEST_NESTED_INVOKE = 5; +#include + +const uint8_t TEST_VERIFY_TRANSLATIONS = 0; +const uint8_t TEST_RETURN_ERROR = 1; +const uint8_t TEST_DERIVED_SIGNERS = 2; +const uint8_t TEST_VERIFY_NESTED_SIGNERS = 3; +const uint8_t TEST_VERIFY_WRITER = 4; +const uint8_t TEST_VERIFY_PRIVILEGE_ESCALATION = 5; +const uint8_t TEST_NESTED_INVOKE = 6; diff --git a/programs/bpf/c/src/invoked/invoked.c b/programs/bpf/c/src/invoked/invoked.c index c12f1b1786..91e020e719 100644 --- a/programs/bpf/c/src/invoked/invoked.c +++ b/programs/bpf/c/src/invoked/invoked.c @@ -136,6 +136,9 @@ extern uint64_t entrypoint(const uint8_t *input) { sol_assert(accounts[ARGUMENT_INDEX].is_writable); break; } + case TEST_VERIFY_PRIVILEGE_ESCALATION: { + sol_log("Success"); + } case TEST_NESTED_INVOKE: { sol_log("invoke"); diff --git a/programs/bpf/rust/invoke/src/lib.rs b/programs/bpf/rust/invoke/src/lib.rs index f80f0466a0..b7a25b05ab 100644 --- a/programs/bpf/rust/invoke/src/lib.rs +++ b/programs/bpf/rust/invoke/src/lib.rs @@ -16,6 +16,10 @@ use solana_sdk::{ system_instruction, }; +const TEST_SUCCESS: u8 = 1; +const TEST_PRIVILEGE_ESCALATION_SIGNER: u8 = 2; +const TEST_PRIVILEGE_ESCALATION_WRITABLE: u8 = 3; + // const MINT_INDEX: usize = 0; const ARGUMENT_INDEX: usize = 1; const INVOKED_PROGRAM_INDEX: usize = 2; @@ -32,128 +36,166 @@ entrypoint!(process_instruction); fn process_instruction( _program_id: &Pubkey, accounts: &[AccountInfo], - _instruction_data: &[u8], + instruction_data: &[u8], ) -> ProgramResult { info!("invoke Rust program"); - info!("Call system program"); - { - assert_eq!(accounts[FROM_INDEX].lamports(), 43); - assert_eq!(accounts[ARGUMENT_INDEX].lamports(), 41); - let instruction = - system_instruction::transfer(accounts[FROM_INDEX].key, accounts[ARGUMENT_INDEX].key, 1); - invoke(&instruction, accounts)?; - assert_eq!(accounts[FROM_INDEX].lamports(), 42); - assert_eq!(accounts[ARGUMENT_INDEX].lamports(), 42); - } + match instruction_data[0] { + TEST_SUCCESS => { + info!("Call system program"); + { + assert_eq!(accounts[FROM_INDEX].lamports(), 43); + assert_eq!(accounts[ARGUMENT_INDEX].lamports(), 41); + let instruction = system_instruction::transfer( + accounts[FROM_INDEX].key, + accounts[ARGUMENT_INDEX].key, + 1, + ); + invoke(&instruction, accounts)?; + assert_eq!(accounts[FROM_INDEX].lamports(), 42); + assert_eq!(accounts[ARGUMENT_INDEX].lamports(), 42); + } - info!("Test data translation"); - { - { - let mut data = accounts[ARGUMENT_INDEX].try_borrow_mut_data()?; - for i in 0..100 { - data[i as usize] = i; + info!("Test data translation"); + { + { + let mut data = accounts[ARGUMENT_INDEX].try_borrow_mut_data()?; + for i in 0..100 { + data[i as usize] = i; + } + } + + let instruction = create_instruction( + *accounts[INVOKED_PROGRAM_INDEX].key, + &[ + (accounts[ARGUMENT_INDEX].key, true, true), + (accounts[INVOKED_ARGUMENT_INDEX].key, true, true), + (accounts[INVOKED_PROGRAM_INDEX].key, false, false), + (accounts[INVOKED_PROGRAM_DUP_INDEX].key, false, false), + ], + vec![TEST_VERIFY_TRANSLATIONS, 1, 2, 3, 4, 5], + ); + invoke(&instruction, accounts)?; + } + + info!("Test return error"); + { + let instruction = create_instruction( + *accounts[INVOKED_PROGRAM_INDEX].key, + &[(accounts[ARGUMENT_INDEX].key, true, true)], + vec![TEST_RETURN_ERROR], + ); + assert_eq!( + invoke(&instruction, accounts), + Err(ProgramError::Custom(42)) + ); + } + + info!("Test derived signers"); + { + assert!(!accounts[DERIVED_KEY1_INDEX].is_signer); + assert!(!accounts[DERIVED_KEY2_INDEX].is_signer); + assert!(!accounts[DERIVED_KEY3_INDEX].is_signer); + + let invoked_instruction = create_instruction( + *accounts[INVOKED_PROGRAM_INDEX].key, + &[ + (accounts[INVOKED_PROGRAM_INDEX].key, false, false), + (accounts[DERIVED_KEY1_INDEX].key, true, true), + (accounts[DERIVED_KEY2_INDEX].key, true, false), + (accounts[DERIVED_KEY3_INDEX].key, false, false), + ], + vec![TEST_DERIVED_SIGNERS], + ); + invoke_signed( + &invoked_instruction, + accounts, + &[&["You pass butter"], &["Lil'", "Bits"]], + )?; + } + + info!("Test readonly with writable account"); + { + let invoked_instruction = create_instruction( + *accounts[INVOKED_PROGRAM_INDEX].key, + &[(accounts[ARGUMENT_INDEX].key, false, true)], + vec![TEST_VERIFY_WRITER], + ); + invoke(&invoked_instruction, accounts)?; + } + + info!("Test nested invoke"); + { + assert!(accounts[ARGUMENT_INDEX].is_signer); + + **accounts[ARGUMENT_INDEX].lamports.borrow_mut() -= 5; + **accounts[INVOKED_ARGUMENT_INDEX].lamports.borrow_mut() += 5; + + info!("First invoke"); + let instruction = create_instruction( + *accounts[INVOKED_PROGRAM_INDEX].key, + &[ + (accounts[ARGUMENT_INDEX].key, true, true), + (accounts[INVOKED_ARGUMENT_INDEX].key, true, true), + (accounts[INVOKED_PROGRAM_DUP_INDEX].key, false, false), + (accounts[INVOKED_PROGRAM_DUP_INDEX].key, false, false), + ], + vec![TEST_NESTED_INVOKE], + ); + invoke(&instruction, accounts)?; + info!("2nd invoke from first program"); + invoke(&instruction, accounts)?; + + assert_eq!(accounts[ARGUMENT_INDEX].lamports(), 42 - 5 + 1 + 1 + 1 + 1); + assert_eq!( + accounts[INVOKED_ARGUMENT_INDEX].lamports(), + 10 + 5 - 1 - 1 - 1 - 1 + ); + } + + info!("Verify data values are retained and updated"); + { + let data = accounts[ARGUMENT_INDEX].try_borrow_data()?; + for i in 0..100 { + assert_eq!(data[i as usize], i); + } + let data = accounts[INVOKED_ARGUMENT_INDEX].try_borrow_data()?; + for i in 0..10 { + assert_eq!(data[i as usize], i); + } } } + TEST_PRIVILEGE_ESCALATION_SIGNER => { + info!("Test privilege escalation signer"); + let mut invoked_instruction = create_instruction( + *accounts[INVOKED_PROGRAM_INDEX].key, + &[(accounts[DERIVED_KEY3_INDEX].key, false, false)], + vec![TEST_VERIFY_PRIVILEGE_ESCALATION], + ); + invoke(&invoked_instruction, accounts)?; - let instruction = create_instruction( - *accounts[INVOKED_PROGRAM_INDEX].key, - &[ - (accounts[ARGUMENT_INDEX].key, true, true), - (accounts[INVOKED_ARGUMENT_INDEX].key, true, true), - (accounts[INVOKED_PROGRAM_INDEX].key, false, false), - (accounts[INVOKED_PROGRAM_DUP_INDEX].key, false, false), - ], - vec![TEST_VERIFY_TRANSLATIONS, 1, 2, 3, 4, 5], - ); - invoke(&instruction, accounts)?; - } - - info!("Test return error"); - { - let instruction = create_instruction( - *accounts[INVOKED_PROGRAM_INDEX].key, - &[(accounts[ARGUMENT_INDEX].key, true, true)], - vec![TEST_RETURN_ERROR], - ); - assert_eq!( - invoke(&instruction, accounts), - Err(ProgramError::Custom(42)) - ); - } - - info!("Test derived signers"); - { - assert!(!accounts[DERIVED_KEY1_INDEX].is_signer); - assert!(!accounts[DERIVED_KEY2_INDEX].is_signer); - assert!(!accounts[DERIVED_KEY3_INDEX].is_signer); - - let invoked_instruction = create_instruction( - *accounts[INVOKED_PROGRAM_INDEX].key, - &[ - (accounts[INVOKED_PROGRAM_INDEX].key, false, false), - (accounts[DERIVED_KEY1_INDEX].key, true, true), - (accounts[DERIVED_KEY2_INDEX].key, true, false), - (accounts[DERIVED_KEY3_INDEX].key, false, false), - ], - vec![TEST_DERIVED_SIGNERS], - ); - invoke_signed( - &invoked_instruction, - accounts, - &[&["You pass butter"], &["Lil'", "Bits"]], - )?; - } - - info!("Test readonly with writable account"); - { - let invoked_instruction = create_instruction( - *accounts[INVOKED_PROGRAM_INDEX].key, - &[(accounts[ARGUMENT_INDEX].key, false, true)], - vec![TEST_VERIFY_WRITER], - ); - invoke(&invoked_instruction, accounts)?; - } - - info!("Test nested invoke"); - { - assert!(accounts[ARGUMENT_INDEX].is_signer); - - **accounts[ARGUMENT_INDEX].lamports.borrow_mut() -= 5; - **accounts[INVOKED_ARGUMENT_INDEX].lamports.borrow_mut() += 5; - - info!("Fist invoke"); - let instruction = create_instruction( - *accounts[INVOKED_PROGRAM_INDEX].key, - &[ - (accounts[ARGUMENT_INDEX].key, true, true), - (accounts[INVOKED_ARGUMENT_INDEX].key, true, true), - (accounts[INVOKED_PROGRAM_DUP_INDEX].key, false, false), - (accounts[INVOKED_PROGRAM_DUP_INDEX].key, false, false), - ], - vec![TEST_NESTED_INVOKE], - ); - invoke(&instruction, accounts)?; - info!("2nd invoke from first program"); - invoke(&instruction, accounts)?; - - assert_eq!(accounts[ARGUMENT_INDEX].lamports(), 42 - 5 + 1 + 1 + 1 + 1); - assert_eq!( - accounts[INVOKED_ARGUMENT_INDEX].lamports(), - 10 + 5 - 1 - 1 - 1 - 1 - ); - } - - info!("Verify data values are retained and updated"); - { - let data = accounts[ARGUMENT_INDEX].try_borrow_data()?; - for i in 0..100 { - assert_eq!(data[i as usize], i); + invoked_instruction.accounts[0].is_signer = true; + assert_eq!( + invoke(&invoked_instruction, accounts), + Err(ProgramError::Custom(0x0b9f_0002)) + ); } - let data = accounts[INVOKED_ARGUMENT_INDEX].try_borrow_data()?; - for i in 0..10 { - assert_eq!(data[i as usize], i); + TEST_PRIVILEGE_ESCALATION_WRITABLE => { + info!("Test privilege escalation writable"); + let mut invoked_instruction = create_instruction( + *accounts[INVOKED_PROGRAM_INDEX].key, + &[(accounts[DERIVED_KEY3_INDEX].key, false, false)], + vec![TEST_VERIFY_PRIVILEGE_ESCALATION], + ); + invoke(&invoked_instruction, accounts)?; + + invoked_instruction.accounts[0].is_writable = true; + assert_eq!( + invoke(&invoked_instruction, accounts), + Err(ProgramError::Custom(0x0b9f_0002)) + ); } + _ => panic!(), } Ok(()) diff --git a/programs/bpf/rust/invoked/src/instruction.rs b/programs/bpf/rust/invoked/src/instruction.rs index f42706654d..a0599c0a52 100644 --- a/programs/bpf/rust/invoked/src/instruction.rs +++ b/programs/bpf/rust/invoked/src/instruction.rs @@ -10,7 +10,8 @@ pub const TEST_RETURN_ERROR: u8 = 1; pub const TEST_DERIVED_SIGNERS: u8 = 2; pub const TEST_VERIFY_NESTED_SIGNERS: u8 = 3; pub const TEST_VERIFY_WRITER: u8 = 4; -pub const TEST_NESTED_INVOKE: u8 = 5; +pub const TEST_VERIFY_PRIVILEGE_ESCALATION: u8 = 5; +pub const TEST_NESTED_INVOKE: u8 = 6; pub fn create_instruction( program_id: Pubkey, diff --git a/programs/bpf/rust/invoked/src/lib.rs b/programs/bpf/rust/invoked/src/lib.rs index 85aa0e8374..f301f264c4 100644 --- a/programs/bpf/rust/invoked/src/lib.rs +++ b/programs/bpf/rust/invoked/src/lib.rs @@ -151,6 +151,9 @@ fn process_instruction( assert!(!accounts[ARGUMENT_INDEX].is_writable); } + TEST_VERIFY_PRIVILEGE_ESCALATION => { + info!("Success"); + } TEST_NESTED_INVOKE => { info!("nested invoke"); diff --git a/programs/bpf/tests/programs.rs b/programs/bpf/tests/programs.rs index ae504a6763..d342b8e841 100644 --- a/programs/bpf/tests/programs.rs +++ b/programs/bpf/tests/programs.rs @@ -309,6 +309,10 @@ mod bpf { fn test_program_bpf_invoke() { solana_logger::setup(); + const TEST_SUCCESS: u8 = 1; + const TEST_PRIVILEGE_ESCALATION_SIGNER: u8 = 2; + const TEST_PRIVILEGE_ESCALATION_WRITABLE: u8 = 3; + let mut programs = Vec::new(); #[cfg(feature = "bpf_c")] { @@ -369,9 +373,11 @@ mod bpf { AccountMeta::new(from_keypair.pubkey(), true), ]; - let instruction = Instruction::new(invoke_program_id, &1u8, account_metas); - let message = Message::new(&[instruction]); + // success cases + let instruction = + Instruction::new(invoke_program_id, &TEST_SUCCESS, account_metas.clone()); + let message = Message::new(&[instruction]); assert!(bank_client .send_message( &[ @@ -383,6 +389,52 @@ mod bpf { message, ) .is_ok()); + + // failure cases + + let instruction = Instruction::new( + invoke_program_id, + &TEST_PRIVILEGE_ESCALATION_SIGNER, + account_metas.clone(), + ); + let message = Message::new(&[instruction]); + assert_eq!( + bank_client + .send_message( + &[ + &mint_keypair, + &argument_keypair, + &invoked_argument_keypair, + &from_keypair + ], + message, + ) + .unwrap_err() + .unwrap(), + TransactionError::InstructionError(0, InstructionError::Custom(194969602)) + ); + + let instruction = Instruction::new( + invoke_program_id, + &TEST_PRIVILEGE_ESCALATION_WRITABLE, + account_metas.clone(), + ); + let message = Message::new(&[instruction]); + assert_eq!( + bank_client + .send_message( + &[ + &mint_keypair, + &argument_keypair, + &invoked_argument_keypair, + &from_keypair + ], + message, + ) + .unwrap_err() + .unwrap(), + TransactionError::InstructionError(0, InstructionError::Custom(194969602)) + ); } } } diff --git a/programs/bpf_loader/src/lib.rs b/programs/bpf_loader/src/lib.rs index 3533368ad1..1a0f98268f 100644 --- a/programs/bpf_loader/src/lib.rs +++ b/programs/bpf_loader/src/lib.rs @@ -57,6 +57,7 @@ impl UserDefinedError for BPFError {} pub fn create_vm<'a>( prog: &'a [u8], + parameter_accounts: &'a [KeyedAccount<'a>], invoke_context: &'a mut dyn InvokeContext, ) -> Result<(EbpfVm<'a, BPFError>, MemoryRegion), EbpfError> { let mut vm = EbpfVm::new(None)?; @@ -64,7 +65,7 @@ pub fn create_vm<'a>( vm.set_max_instruction_count(100_000)?; vm.set_elf(&prog)?; - let heap_region = syscalls::register_syscalls(&mut vm, invoke_context)?; + let heap_region = syscalls::register_syscalls(&mut vm, parameter_accounts, invoke_context)?; Ok((vm, heap_region)) } @@ -182,13 +183,14 @@ pub fn process_instruction( )?; { let program_account = program.try_account_ref_mut()?; - let (mut vm, heap_region) = match create_vm(&program_account.data, invoke_context) { - Ok(info) => info, - Err(e) => { - warn!("Failed to create BPF VM: {}", e); - return Err(BPFLoaderError::VirtualMachineCreationFailed.into()); - } - }; + let (mut vm, heap_region) = + match create_vm(&program_account.data, ¶meter_accounts, invoke_context) { + Ok(info) => info, + Err(e) => { + warn!("Failed to create BPF VM: {}", e); + return Err(BPFLoaderError::VirtualMachineCreationFailed.into()); + } + }; info!("Call BPF program {}", program.unsigned_key()); match vm.execute_program(parameter_bytes.as_slice(), &[], &[heap_region]) { @@ -274,7 +276,6 @@ mod tests { &mut self, _message: &Message, _instruction: &CompiledInstruction, - _signers: &[Pubkey], _accounts: &[Rc>], ) -> Result<(), InstructionError> { Ok(()) diff --git a/programs/bpf_loader/src/syscalls.rs b/programs/bpf_loader/src/syscalls.rs index 590380285d..80194977e4 100644 --- a/programs/bpf_loader/src/syscalls.rs +++ b/programs/bpf_loader/src/syscalls.rs @@ -9,6 +9,7 @@ use solana_rbpf::{ use solana_runtime::{builtin_programs::get_builtin_programs, message_processor::MessageProcessor}; use solana_sdk::{ account::Account, + account::KeyedAccount, account_info::AccountInfo, bpf_loader, entrypoint::SUCCESS, @@ -48,6 +49,8 @@ pub enum SyscallError { ProgramNotSupported, #[error("{0}")] InstructionError(InstructionError), + #[error("Cross-program invocation with unauthorized signer or writable account")] + PrivilegeEscalation, } impl From for EbpfError { fn from(error: SyscallError) -> Self { @@ -69,6 +72,7 @@ const DEFAULT_HEAP_SIZE: usize = 32 * 1024; pub fn register_syscalls<'a>( vm: &mut EbpfVm<'a, BPFError>, + callers_keyed_accounts: &'a [KeyedAccount<'a>], invoke_context: &'a mut dyn InvokeContext, ) -> Result> { // Syscall function common across languages @@ -83,12 +87,14 @@ pub fn register_syscalls<'a>( vm.register_syscall_with_context_ex( "sol_invoke_signed_c", Box::new(SyscallProcessSolInstructionC { + callers_keyed_accounts, invoke_context: invoke_context.clone(), }), )?; vm.register_syscall_with_context_ex( "sol_invoke_signed_rust", Box::new(SyscallProcessInstructionRust { + callers_keyed_accounts, invoke_context: invoke_context.clone(), }), )?; @@ -301,6 +307,7 @@ pub type TranslatedAccounts<'a> = (Vec>>, Vec<(&'a mut u64, /// Implemented by language specific data structure translators trait SyscallProcessInstruction<'a> { fn get_context_mut(&self) -> Result, EbpfError>; + fn get_callers_keyed_accounts(&self) -> &'a [KeyedAccount<'a>]; fn translate_instruction( &self, addr: u64, @@ -325,6 +332,7 @@ trait SyscallProcessInstruction<'a> { /// Cross-program invocation called from Rust pub struct SyscallProcessInstructionRust<'a> { + callers_keyed_accounts: &'a [KeyedAccount<'a>], invoke_context: Rc>, } impl<'a> SyscallProcessInstruction<'a> for SyscallProcessInstructionRust<'a> { @@ -333,6 +341,9 @@ impl<'a> SyscallProcessInstruction<'a> for SyscallProcessInstructionRust<'a> { .try_borrow_mut() .map_err(|_| SyscallError::InvokeContextBorrowFailed.into()) } + fn get_callers_keyed_accounts(&self) -> &'a [KeyedAccount<'a>] { + self.callers_keyed_accounts + } fn translate_instruction( &self, addr: u64, @@ -519,6 +530,7 @@ struct SolSignerSeedsC { /// Cross-program invocation called from C pub struct SyscallProcessSolInstructionC<'a> { + callers_keyed_accounts: &'a [KeyedAccount<'a>], invoke_context: Rc>, } impl<'a> SyscallProcessInstruction<'a> for SyscallProcessSolInstructionC<'a> { @@ -527,7 +539,9 @@ impl<'a> SyscallProcessInstruction<'a> for SyscallProcessSolInstructionC<'a> { .try_borrow_mut() .map_err(|_| SyscallError::InvokeContextBorrowFailed.into()) } - + fn get_callers_keyed_accounts(&self) -> &'a [KeyedAccount<'a>] { + self.callers_keyed_accounts + } fn translate_instruction( &self, addr: u64, @@ -673,6 +687,44 @@ impl<'a> SyscallObject for SyscallProcessSolInstructionC<'a> { } } +fn verify_instruction<'a>( + syscall: &dyn SyscallProcessInstruction<'a>, + instruction: &Instruction, + signers: &[Pubkey], +) -> Result<(), EbpfError> { + let callers_keyed_accounts = syscall.get_callers_keyed_accounts(); + + // Check for privilege escalation + for account in instruction.accounts.iter() { + let keyed_account = callers_keyed_accounts + .iter() + .find_map(|keyed_account| { + if &account.pubkey == keyed_account.unsigned_key() { + Some(keyed_account) + } else { + None + } + }) + .ok_or(SyscallError::InstructionError( + InstructionError::MissingAccount, + ))?; + // Readonly account cannot become writable + if account.is_writable && !keyed_account.is_writable() { + return Err(SyscallError::PrivilegeEscalation.into()); + } + + if account.is_signer && // If message indicates account is signed + !( // one of the following needs to be true: + keyed_account.signer_key().is_some() // Signed in the parent instruction + || signers.contains(&account.pubkey) // Signed by the program + ) { + return Err(SyscallError::PrivilegeEscalation.into()); + } + } + + Ok(()) +} + /// Call process instruction, common to both Rust and C fn call<'a>( syscall: &mut dyn SyscallProcessInstruction<'a>, @@ -689,12 +741,19 @@ fn call<'a>( // Translate data passed from the VM let instruction = syscall.translate_instruction(instruction_addr, ro_regions)?; - let message = Message::new_with_payer(&[instruction], None); - let callee_program_id_index = message.instructions[0].program_id_index as usize; - let callee_program_id = message.account_keys[callee_program_id_index]; let caller_program_id = invoke_context .get_caller() .map_err(SyscallError::InstructionError)?; + let signers = syscall.translate_signers( + caller_program_id, + signers_seeds_addr, + signers_seeds_len as usize, + ro_regions, + )?; + verify_instruction(syscall, &instruction, &signers)?; + let message = Message::new_with_payer(&[instruction], None); + let callee_program_id_index = message.instructions[0].program_id_index as usize; + let callee_program_id = message.account_keys[callee_program_id_index]; let (accounts, refs) = syscall.translate_accounts( &message, account_infos_addr, @@ -702,12 +761,6 @@ fn call<'a>( ro_regions, rw_regions, )?; - let signers = syscall.translate_signers( - caller_program_id, - signers_seeds_addr, - signers_seeds_len as usize, - ro_regions, - )?; // Process instruction @@ -725,7 +778,6 @@ fn call<'a>( &message, &executable_accounts, &accounts, - &signers, *(&mut *invoke_context), ) { Ok(()) => (), diff --git a/runtime/src/message_processor.rs b/runtime/src/message_processor.rs index eeb3646f86..c9655dbd56 100644 --- a/runtime/src/message_processor.rs +++ b/runtime/src/message_processor.rs @@ -122,31 +122,6 @@ impl PreAccount { Ok(()) } - pub fn verify_cross_program( - &self, - is_writable: bool, - is_signer: bool, - signers: &[Pubkey], - program_id: &Pubkey, - rent: &Rent, - post: &Account, - ) -> Result<(), InstructionError> { - // Readonly account cannot become writable - if is_writable && !self.is_writable { - return Err(InstructionError::WritableModified); - } - - if is_signer && // If message indicates account is signed - !( // one of the following needs to be true: - self.is_signer // Signed in the original transaction - || signers.contains(&self.key) // Signed by the program - ) { - return Err(InstructionError::SignerModified); - } - - self.verify(program_id, rent, post) - } - pub fn update(&mut self, account: &Account) { self.lamports = account.lamports; if self.data.len() != account.data.len() { @@ -213,7 +188,6 @@ impl InvokeContext for ThisInvokeContext { &mut self, message: &Message, instruction: &CompiledInstruction, - signers: &[Pubkey], accounts: &[Rc>], ) -> Result<(), InstructionError> { match self.program_ids.last() { @@ -221,10 +195,9 @@ impl InvokeContext for ThisInvokeContext { message, instruction, &mut self.pre_accounts, + accounts, key, &self.rent, - signers, - accounts, ), None => Err(InstructionError::GenericError), // Should never happen } @@ -353,13 +326,12 @@ impl MessageProcessor { message: &Message, executable_accounts: &[(Pubkey, RefCell)], accounts: &[Rc>], - signers: &[Pubkey], invoke_context: &mut dyn InvokeContext, ) -> Result<(), InstructionError> { let instruction = &message.instructions[0]; // Verify the calling program hasn't misbehaved - invoke_context.verify_and_update(message, instruction, signers, accounts)?; + invoke_context.verify_and_update(message, instruction, accounts)?; // Construct keyed accounts let keyed_accounts = @@ -371,7 +343,7 @@ impl MessageProcessor { self.process_instruction(&keyed_accounts, &instruction.data, invoke_context); if result.is_ok() { // Verify the called program has not misbehaved - result = invoke_context.verify_and_update(message, instruction, signers, accounts); + result = invoke_context.verify_and_update(message, instruction, accounts); } invoke_context.pop(); @@ -419,7 +391,7 @@ impl MessageProcessor { pre_accounts: &[PreAccount], executable_accounts: &[(Pubkey, RefCell)], accounts: &[Rc>], - rent_collector: &RentCollector, + rent: &Rent, ) -> Result<(), InstructionError> { // Verify all executable accounts have zero outstanding refs Self::verify_account_references(executable_accounts)?; @@ -433,7 +405,7 @@ impl MessageProcessor { let account = accounts[account_index] .try_borrow_mut() .map_err(|_| InstructionError::AccountBorrowOutstanding)?; - pre_accounts[unique_index].verify(&program_id, &rent_collector.rent, &account)?; + pre_accounts[unique_index].verify(&program_id, rent, &account)?; pre_sum += u128::from(pre_accounts[unique_index].lamports()); post_sum += u128::from(account.lamports); Ok(()) @@ -453,10 +425,9 @@ impl MessageProcessor { message: &Message, instruction: &CompiledInstruction, pre_accounts: &mut [PreAccount], + accounts: &[Rc>], program_id: &Pubkey, rent: &Rent, - signers: &[Pubkey], - accounts: &[Rc>], ) -> Result<(), InstructionError> { // Verify the per-account instruction results let (mut pre_sum, mut post_sum) = (0_u128, 0_u128); @@ -471,14 +442,7 @@ impl MessageProcessor { .try_borrow_mut() .map_err(|_| InstructionError::AccountBorrowOutstanding)?; - pre_account.verify_cross_program( - message.is_writable(account_index), - message.is_signer(account_index), - signers, - &program_id, - &rent, - &account, - )?; + pre_account.verify(&program_id, &rent, &account)?; pre_sum += u128::from(pre_account.lamports()); post_sum += u128::from(account.lamports); @@ -525,7 +489,7 @@ impl MessageProcessor { &invoke_context.pre_accounts, executable_accounts, accounts, - rent_collector, + &rent_collector.rent, )?; Ok(()) } @@ -622,7 +586,6 @@ mod tests { .verify_and_update( &message, &message.instructions[0], - &[], &accounts[not_owned_index..owned_index + 1], ) .unwrap(); @@ -638,7 +601,6 @@ mod tests { invoke_context.verify_and_update( &message, &message.instructions[0], - &[], &accounts[not_owned_index..owned_index + 1], ), Err(InstructionError::ExternalAccountDataModified) @@ -685,23 +647,16 @@ mod tests { ); } - struct Change<'a> { + struct Change { program_id: Pubkey, - message_is_writable: bool, - message_is_signer: bool, - signers: &'a [Pubkey], rent: Rent, pre: PreAccount, post: Account, } - impl<'a> Change<'a> { + impl Change { pub fn new(owner: &Pubkey, program_id: &Pubkey) -> Self { Self { - // key: Pubkey::new_rand(), program_id: *program_id, - message_is_writable: false, - message_is_signer: false, - signers: &[], rent: Rent::default(), pre: PreAccount::new( &Pubkey::new_rand(), @@ -721,26 +676,10 @@ mod tests { }, } } - pub fn new_cross_program(owner: &Pubkey, program_id: &Pubkey, key: &Pubkey) -> Self { - let mut change = Change::new(owner, program_id); - change.pre.key = *key; - change - } pub fn read_only(mut self) -> Self { self.pre.is_writable = false; self } - pub fn writable(mut self, pre: bool, message_is_writable: bool) -> Self { - self.pre.is_writable = pre; - self.message_is_writable = message_is_writable; - self - } - pub fn signer(mut self, pre: bool, message_is_signer: bool, signers: &'a [Pubkey]) -> Self { - self.pre.is_signer = pre; - self.message_is_signer = message_is_signer; - self.signers = signers; - self - } pub fn executable(mut self, pre: bool, post: bool) -> Self { self.pre.is_executable = pre; self.post.executable = post; @@ -768,16 +707,6 @@ mod tests { pub fn verify(&self) -> Result<(), InstructionError> { self.pre.verify(&self.program_id, &self.rent, &self.post) } - pub fn verify_cross_program(&self) -> Result<(), InstructionError> { - self.pre.verify_cross_program( - self.message_is_writable, - self.message_is_signer, - self.signers, - &self.program_id, - &self.rent, - &self.post, - ) - } } #[test] @@ -940,59 +869,6 @@ mod tests { ); } - #[test] - fn test_verify_account_changes_writable() { - let owner = Pubkey::new_rand(); - let system_program_id = system_program::id(); - - assert_eq!( - Change::new(&owner, &system_program_id) - .writable(true, false) - .verify_cross_program(), - Ok(()), - "account can we changed to readonly" - ); - - assert_eq!( - Change::new(&owner, &system_program_id) - .writable(false, true) - .verify_cross_program(), - Err(InstructionError::WritableModified), - "account cannot be changed to writable" - ); - } - - #[test] - fn test_verify_account_changes_signer() { - let owner = Pubkey::new_rand(); - let system_program_id = system_program::id(); - let key = Pubkey::new_rand(); - - assert_eq!( - Change::new_cross_program(&owner, &system_program_id, &key) - .signer(false, true, &[key]) - .verify_cross_program(), - Ok(()), - "account signed by a signer" - ); - - assert_eq!( - Change::new_cross_program(&owner, &system_program_id, &key) - .signer(false, true, &[]) - .verify_cross_program(), - Err(InstructionError::SignerModified), - "account cannot be changed to signed if no signer" - ); - - assert_eq!( - Change::new_cross_program(&owner, &system_program_id, &key) - .signer(false, true, &[Pubkey::new_rand(), Pubkey::new_rand()]) - .verify_cross_program(), - Err(InstructionError::SignerModified), - "account cannot be changed to signed if no signer exists" - ); - } - #[test] fn test_verify_account_changes_data_len() { let alice_program_id = Pubkey::new_rand(); @@ -1446,7 +1322,6 @@ mod tests { &message, &executable_accounts, &accounts, - &[], &mut invoke_context, ), Err(InstructionError::ExternalAccountDataModified) @@ -1474,7 +1349,6 @@ mod tests { &message, &executable_accounts, &accounts, - &[], &mut invoke_context, ), case.1 diff --git a/sdk/src/entrypoint_native.rs b/sdk/src/entrypoint_native.rs index 33c7460567..e3c497b96f 100644 --- a/sdk/src/entrypoint_native.rs +++ b/sdk/src/entrypoint_native.rs @@ -154,7 +154,6 @@ pub trait InvokeContext { &mut self, message: &Message, instruction: &CompiledInstruction, - signers: &[Pubkey], accounts: &[Rc>], ) -> Result<(), InstructionError>; fn get_caller(&self) -> Result<&Pubkey, InstructionError>; diff --git a/sdk/src/instruction.rs b/sdk/src/instruction.rs index 16db18a6e9..23643c0f20 100644 --- a/sdk/src/instruction.rs +++ b/sdk/src/instruction.rs @@ -140,14 +140,6 @@ pub enum InstructionError { #[error("Unsupported program id")] UnsupportedProgramId, - /// Writable bit on account info changed, but shouldn't have - #[error("Writable bit on account info changed, but shouldn't have")] - WritableModified, - - /// Signer bit on account info changed, but shouldn't have - #[error("Signer bit on account info changed, but shouldn't have")] - SignerModified, - /// Cross-program invocation call depth too deep #[error("Cross-program invocation call depth too deep")] CallDepth,