Prevent privilege escalation (#10232)

automerge
This commit is contained in:
Jack May 2020-05-26 01:02:31 -07:00 committed by GitHub
parent 5d96fcec63
commit 03abd3ddd7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 458 additions and 392 deletions

View File

@ -80,7 +80,7 @@ fn bench_program_alu(bencher: &mut Bencher) {
let mut invoke_context = MockInvokeContext::default(); let mut invoke_context = MockInvokeContext::default();
let elf = load_elf().unwrap(); 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:"); println!("Interpreted:");
assert_eq!( assert_eq!(
@ -145,7 +145,6 @@ impl InvokeContext for MockInvokeContext {
&mut self, &mut self,
_message: &Message, _message: &Message,
_instruction: &CompiledInstruction, _instruction: &CompiledInstruction,
_signers: &[Pubkey],
_accounts: &[Rc<RefCell<Account>>], _accounts: &[Rc<RefCell<Account>>],
) -> Result<(), InstructionError> { ) -> Result<(), InstructionError> {
Ok(()) Ok(())

View File

@ -4,6 +4,10 @@
#include "../invoked/instruction.h" #include "../invoked/instruction.h"
#include <solana_sdk.h> #include <solana_sdk.h>
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 MINT_INDEX = 0;
static const int ARGUMENT_INDEX = 1; static const int ARGUMENT_INDEX = 1;
static const int INVOKED_PROGRAM_INDEX = 2; static const int INVOKED_PROGRAM_INDEX = 2;
@ -26,127 +30,168 @@ extern uint64_t entrypoint(const uint8_t *input) {
return ERROR_INVALID_ARGUMENT; return ERROR_INVALID_ARGUMENT;
} }
sol_log("Call system program"); switch (params.data[0]) {
{ case TEST_SUCCESS: {
sol_assert(*accounts[FROM_INDEX].lamports = 43); sol_log("Call system program");
sol_assert(*accounts[ARGUMENT_INDEX].lamports = 41); {
SolAccountMeta arguments[] = {{accounts[FROM_INDEX].key, false, true}, sol_assert(*accounts[FROM_INDEX].lamports = 43);
{accounts[ARGUMENT_INDEX].key, false, false}}; sol_assert(*accounts[ARGUMENT_INDEX].lamports = 41);
uint8_t data[] = {2, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0}; SolAccountMeta arguments[] = {
const SolInstruction instruction = {accounts[SYSTEM_PROGRAM_INDEX].key, {accounts[FROM_INDEX].key, false, true},
arguments, SOL_ARRAY_SIZE(arguments), {accounts[ARGUMENT_INDEX].key, false, false}};
data, SOL_ARRAY_SIZE(data)}; uint8_t data[] = {2, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0};
sol_assert(SUCCESS == const SolInstruction instruction = {accounts[SYSTEM_PROGRAM_INDEX].key,
sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts))); arguments, SOL_ARRAY_SIZE(arguments),
sol_assert(*accounts[FROM_INDEX].lamports = 42); data, SOL_ARRAY_SIZE(data)};
sol_assert(*accounts[ARGUMENT_INDEX].lamports = 42); sol_assert(SUCCESS ==
} sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts)));
sol_assert(*accounts[FROM_INDEX].lamports = 42);
sol_log("Test data translation"); sol_assert(*accounts[ARGUMENT_INDEX].lamports = 42);
{
for (int i = 0; i < accounts[ARGUMENT_INDEX].data_len; i++) {
accounts[ARGUMENT_INDEX].data[i] = i;
} }
SolAccountMeta arguments[] = { sol_log("Test data translation");
{accounts[ARGUMENT_INDEX].key, true, true}, {
{accounts[INVOKED_ARGUMENT_INDEX].key, true, true}, for (int i = 0; i < accounts[ARGUMENT_INDEX].data_len; i++) {
{accounts[INVOKED_PROGRAM_INDEX].key, false, false}, accounts[ARGUMENT_INDEX].data[i] = i;
{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 == SolAccountMeta arguments[] = {
sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts))); {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;
} }
case TEST_PRIVILEGE_ESCALATION_SIGNER: {
sol_log("Test return error"); sol_log("Test privilege escalation signer");
{
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[] = { 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}}; {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, const SolInstruction instruction = {accounts[INVOKED_PROGRAM_INDEX].key,
arguments, SOL_ARRAY_SIZE(arguments), arguments, SOL_ARRAY_SIZE(arguments),
data, SOL_ARRAY_SIZE(data)}; data, SOL_ARRAY_SIZE(data)};
char seed1[] = "You pass butter"; sol_assert(SUCCESS ==
char seed2[] = "Lil'"; sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts)));
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"); 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[] = { SolAccountMeta arguments[] = {
{accounts[INVOKED_ARGUMENT_INDEX].key, true, false}}; {accounts[DERIVED_KEY3_INDEX].key, false, false}};
uint8_t data[] = {TEST_VERIFY_WRITER}; uint8_t data[] = {TEST_VERIFY_PRIVILEGE_ESCALATION};
const SolInstruction instruction = {accounts[INVOKED_PROGRAM_INDEX].key, const SolInstruction instruction = {accounts[INVOKED_PROGRAM_INDEX].key,
arguments, SOL_ARRAY_SIZE(arguments), arguments, SOL_ARRAY_SIZE(arguments),
data, SOL_ARRAY_SIZE(data)}; 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_assert(SUCCESS ==
sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts))); sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts)));
sol_assert(*accounts[ARGUMENT_INDEX].lamports == 42 - 5 + 1 + 1); instruction.accounts[0].is_writable = true;
sol_assert(*accounts[INVOKED_ARGUMENT_INDEX].lamports == 10 + 5 - 1 - 1); sol_assert(SUCCESS !=
sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts)));
break;
} }
default:
sol_log("Verify data values are retained and updated"); sol_panic();
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);
} }
return SUCCESS; return SUCCESS;

View File

@ -2,9 +2,12 @@
* @brief Instruction definitions for the invoked program * @brief Instruction definitions for the invoked program
*/ */
const int TEST_VERIFY_TRANSLATIONS = 0; #include <solana_sdk.h>
const int TEST_RETURN_ERROR = 1;
const int TEST_DERIVED_SIGNERS = 2; const uint8_t TEST_VERIFY_TRANSLATIONS = 0;
const int TEST_VERIFY_NESTED_SIGNERS = 3; const uint8_t TEST_RETURN_ERROR = 1;
const int TEST_VERIFY_WRITER = 4; const uint8_t TEST_DERIVED_SIGNERS = 2;
const int TEST_NESTED_INVOKE = 5; 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;

View File

@ -136,6 +136,9 @@ extern uint64_t entrypoint(const uint8_t *input) {
sol_assert(accounts[ARGUMENT_INDEX].is_writable); sol_assert(accounts[ARGUMENT_INDEX].is_writable);
break; break;
} }
case TEST_VERIFY_PRIVILEGE_ESCALATION: {
sol_log("Success");
}
case TEST_NESTED_INVOKE: { case TEST_NESTED_INVOKE: {
sol_log("invoke"); sol_log("invoke");

View File

@ -16,6 +16,10 @@ use solana_sdk::{
system_instruction, 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 MINT_INDEX: usize = 0;
const ARGUMENT_INDEX: usize = 1; const ARGUMENT_INDEX: usize = 1;
const INVOKED_PROGRAM_INDEX: usize = 2; const INVOKED_PROGRAM_INDEX: usize = 2;
@ -32,128 +36,166 @@ entrypoint!(process_instruction);
fn process_instruction( fn process_instruction(
_program_id: &Pubkey, _program_id: &Pubkey,
accounts: &[AccountInfo], accounts: &[AccountInfo],
_instruction_data: &[u8], instruction_data: &[u8],
) -> ProgramResult { ) -> ProgramResult {
info!("invoke Rust program"); info!("invoke Rust program");
info!("Call system program"); match instruction_data[0] {
{ TEST_SUCCESS => {
assert_eq!(accounts[FROM_INDEX].lamports(), 43); info!("Call system program");
assert_eq!(accounts[ARGUMENT_INDEX].lamports(), 41); {
let instruction = assert_eq!(accounts[FROM_INDEX].lamports(), 43);
system_instruction::transfer(accounts[FROM_INDEX].key, accounts[ARGUMENT_INDEX].key, 1); assert_eq!(accounts[ARGUMENT_INDEX].lamports(), 41);
invoke(&instruction, accounts)?; let instruction = system_instruction::transfer(
assert_eq!(accounts[FROM_INDEX].lamports(), 42); accounts[FROM_INDEX].key,
assert_eq!(accounts[ARGUMENT_INDEX].lamports(), 42); 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"); info!("Test data translation");
{ {
{ {
let mut data = accounts[ARGUMENT_INDEX].try_borrow_mut_data()?; let mut data = accounts[ARGUMENT_INDEX].try_borrow_mut_data()?;
for i in 0..100 { for i in 0..100 {
data[i as usize] = i; 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( invoked_instruction.accounts[0].is_signer = true;
*accounts[INVOKED_PROGRAM_INDEX].key, assert_eq!(
&[ invoke(&invoked_instruction, accounts),
(accounts[ARGUMENT_INDEX].key, true, true), Err(ProgramError::Custom(0x0b9f_0002))
(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);
} }
let data = accounts[INVOKED_ARGUMENT_INDEX].try_borrow_data()?; TEST_PRIVILEGE_ESCALATION_WRITABLE => {
for i in 0..10 { info!("Test privilege escalation writable");
assert_eq!(data[i as usize], i); 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(()) Ok(())

View File

@ -10,7 +10,8 @@ pub const TEST_RETURN_ERROR: u8 = 1;
pub const TEST_DERIVED_SIGNERS: u8 = 2; pub const TEST_DERIVED_SIGNERS: u8 = 2;
pub const TEST_VERIFY_NESTED_SIGNERS: u8 = 3; pub const TEST_VERIFY_NESTED_SIGNERS: u8 = 3;
pub const TEST_VERIFY_WRITER: u8 = 4; 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( pub fn create_instruction(
program_id: Pubkey, program_id: Pubkey,

View File

@ -151,6 +151,9 @@ fn process_instruction(
assert!(!accounts[ARGUMENT_INDEX].is_writable); assert!(!accounts[ARGUMENT_INDEX].is_writable);
} }
TEST_VERIFY_PRIVILEGE_ESCALATION => {
info!("Success");
}
TEST_NESTED_INVOKE => { TEST_NESTED_INVOKE => {
info!("nested invoke"); info!("nested invoke");

View File

@ -309,6 +309,10 @@ mod bpf {
fn test_program_bpf_invoke() { fn test_program_bpf_invoke() {
solana_logger::setup(); 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(); let mut programs = Vec::new();
#[cfg(feature = "bpf_c")] #[cfg(feature = "bpf_c")]
{ {
@ -369,9 +373,11 @@ mod bpf {
AccountMeta::new(from_keypair.pubkey(), true), AccountMeta::new(from_keypair.pubkey(), true),
]; ];
let instruction = Instruction::new(invoke_program_id, &1u8, account_metas); // success cases
let message = Message::new(&[instruction]);
let instruction =
Instruction::new(invoke_program_id, &TEST_SUCCESS, account_metas.clone());
let message = Message::new(&[instruction]);
assert!(bank_client assert!(bank_client
.send_message( .send_message(
&[ &[
@ -383,6 +389,52 @@ mod bpf {
message, message,
) )
.is_ok()); .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))
);
} }
} }
} }

View File

@ -57,6 +57,7 @@ impl UserDefinedError for BPFError {}
pub fn create_vm<'a>( pub fn create_vm<'a>(
prog: &'a [u8], prog: &'a [u8],
parameter_accounts: &'a [KeyedAccount<'a>],
invoke_context: &'a mut dyn InvokeContext, invoke_context: &'a mut dyn InvokeContext,
) -> Result<(EbpfVm<'a, BPFError>, MemoryRegion), EbpfError<BPFError>> { ) -> Result<(EbpfVm<'a, BPFError>, MemoryRegion), EbpfError<BPFError>> {
let mut vm = EbpfVm::new(None)?; let mut vm = EbpfVm::new(None)?;
@ -64,7 +65,7 @@ pub fn create_vm<'a>(
vm.set_max_instruction_count(100_000)?; vm.set_max_instruction_count(100_000)?;
vm.set_elf(&prog)?; 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)) Ok((vm, heap_region))
} }
@ -182,13 +183,14 @@ pub fn process_instruction(
)?; )?;
{ {
let program_account = program.try_account_ref_mut()?; let program_account = program.try_account_ref_mut()?;
let (mut vm, heap_region) = match create_vm(&program_account.data, invoke_context) { let (mut vm, heap_region) =
Ok(info) => info, match create_vm(&program_account.data, &parameter_accounts, invoke_context) {
Err(e) => { Ok(info) => info,
warn!("Failed to create BPF VM: {}", e); Err(e) => {
return Err(BPFLoaderError::VirtualMachineCreationFailed.into()); warn!("Failed to create BPF VM: {}", e);
} return Err(BPFLoaderError::VirtualMachineCreationFailed.into());
}; }
};
info!("Call BPF program {}", program.unsigned_key()); info!("Call BPF program {}", program.unsigned_key());
match vm.execute_program(parameter_bytes.as_slice(), &[], &[heap_region]) { match vm.execute_program(parameter_bytes.as_slice(), &[], &[heap_region]) {
@ -274,7 +276,6 @@ mod tests {
&mut self, &mut self,
_message: &Message, _message: &Message,
_instruction: &CompiledInstruction, _instruction: &CompiledInstruction,
_signers: &[Pubkey],
_accounts: &[Rc<RefCell<Account>>], _accounts: &[Rc<RefCell<Account>>],
) -> Result<(), InstructionError> { ) -> Result<(), InstructionError> {
Ok(()) Ok(())

View File

@ -9,6 +9,7 @@ use solana_rbpf::{
use solana_runtime::{builtin_programs::get_builtin_programs, message_processor::MessageProcessor}; use solana_runtime::{builtin_programs::get_builtin_programs, message_processor::MessageProcessor};
use solana_sdk::{ use solana_sdk::{
account::Account, account::Account,
account::KeyedAccount,
account_info::AccountInfo, account_info::AccountInfo,
bpf_loader, bpf_loader,
entrypoint::SUCCESS, entrypoint::SUCCESS,
@ -48,6 +49,8 @@ pub enum SyscallError {
ProgramNotSupported, ProgramNotSupported,
#[error("{0}")] #[error("{0}")]
InstructionError(InstructionError), InstructionError(InstructionError),
#[error("Cross-program invocation with unauthorized signer or writable account")]
PrivilegeEscalation,
} }
impl From<SyscallError> for EbpfError<BPFError> { impl From<SyscallError> for EbpfError<BPFError> {
fn from(error: SyscallError) -> Self { fn from(error: SyscallError) -> Self {
@ -69,6 +72,7 @@ const DEFAULT_HEAP_SIZE: usize = 32 * 1024;
pub fn register_syscalls<'a>( pub fn register_syscalls<'a>(
vm: &mut EbpfVm<'a, BPFError>, vm: &mut EbpfVm<'a, BPFError>,
callers_keyed_accounts: &'a [KeyedAccount<'a>],
invoke_context: &'a mut dyn InvokeContext, invoke_context: &'a mut dyn InvokeContext,
) -> Result<MemoryRegion, EbpfError<BPFError>> { ) -> Result<MemoryRegion, EbpfError<BPFError>> {
// Syscall function common across languages // Syscall function common across languages
@ -83,12 +87,14 @@ pub fn register_syscalls<'a>(
vm.register_syscall_with_context_ex( vm.register_syscall_with_context_ex(
"sol_invoke_signed_c", "sol_invoke_signed_c",
Box::new(SyscallProcessSolInstructionC { Box::new(SyscallProcessSolInstructionC {
callers_keyed_accounts,
invoke_context: invoke_context.clone(), invoke_context: invoke_context.clone(),
}), }),
)?; )?;
vm.register_syscall_with_context_ex( vm.register_syscall_with_context_ex(
"sol_invoke_signed_rust", "sol_invoke_signed_rust",
Box::new(SyscallProcessInstructionRust { Box::new(SyscallProcessInstructionRust {
callers_keyed_accounts,
invoke_context: invoke_context.clone(), invoke_context: invoke_context.clone(),
}), }),
)?; )?;
@ -301,6 +307,7 @@ pub type TranslatedAccounts<'a> = (Vec<Rc<RefCell<Account>>>, Vec<(&'a mut u64,
/// Implemented by language specific data structure translators /// Implemented by language specific data structure translators
trait SyscallProcessInstruction<'a> { trait SyscallProcessInstruction<'a> {
fn get_context_mut(&self) -> Result<RefMut<&'a mut dyn InvokeContext>, EbpfError<BPFError>>; fn get_context_mut(&self) -> Result<RefMut<&'a mut dyn InvokeContext>, EbpfError<BPFError>>;
fn get_callers_keyed_accounts(&self) -> &'a [KeyedAccount<'a>];
fn translate_instruction( fn translate_instruction(
&self, &self,
addr: u64, addr: u64,
@ -325,6 +332,7 @@ trait SyscallProcessInstruction<'a> {
/// Cross-program invocation called from Rust /// Cross-program invocation called from Rust
pub struct SyscallProcessInstructionRust<'a> { pub struct SyscallProcessInstructionRust<'a> {
callers_keyed_accounts: &'a [KeyedAccount<'a>],
invoke_context: Rc<RefCell<&'a mut dyn InvokeContext>>, invoke_context: Rc<RefCell<&'a mut dyn InvokeContext>>,
} }
impl<'a> SyscallProcessInstruction<'a> for SyscallProcessInstructionRust<'a> { impl<'a> SyscallProcessInstruction<'a> for SyscallProcessInstructionRust<'a> {
@ -333,6 +341,9 @@ impl<'a> SyscallProcessInstruction<'a> for SyscallProcessInstructionRust<'a> {
.try_borrow_mut() .try_borrow_mut()
.map_err(|_| SyscallError::InvokeContextBorrowFailed.into()) .map_err(|_| SyscallError::InvokeContextBorrowFailed.into())
} }
fn get_callers_keyed_accounts(&self) -> &'a [KeyedAccount<'a>] {
self.callers_keyed_accounts
}
fn translate_instruction( fn translate_instruction(
&self, &self,
addr: u64, addr: u64,
@ -519,6 +530,7 @@ struct SolSignerSeedsC {
/// Cross-program invocation called from C /// Cross-program invocation called from C
pub struct SyscallProcessSolInstructionC<'a> { pub struct SyscallProcessSolInstructionC<'a> {
callers_keyed_accounts: &'a [KeyedAccount<'a>],
invoke_context: Rc<RefCell<&'a mut dyn InvokeContext>>, invoke_context: Rc<RefCell<&'a mut dyn InvokeContext>>,
} }
impl<'a> SyscallProcessInstruction<'a> for SyscallProcessSolInstructionC<'a> { impl<'a> SyscallProcessInstruction<'a> for SyscallProcessSolInstructionC<'a> {
@ -527,7 +539,9 @@ impl<'a> SyscallProcessInstruction<'a> for SyscallProcessSolInstructionC<'a> {
.try_borrow_mut() .try_borrow_mut()
.map_err(|_| SyscallError::InvokeContextBorrowFailed.into()) .map_err(|_| SyscallError::InvokeContextBorrowFailed.into())
} }
fn get_callers_keyed_accounts(&self) -> &'a [KeyedAccount<'a>] {
self.callers_keyed_accounts
}
fn translate_instruction( fn translate_instruction(
&self, &self,
addr: u64, addr: u64,
@ -673,6 +687,44 @@ impl<'a> SyscallObject<BPFError> for SyscallProcessSolInstructionC<'a> {
} }
} }
fn verify_instruction<'a>(
syscall: &dyn SyscallProcessInstruction<'a>,
instruction: &Instruction,
signers: &[Pubkey],
) -> Result<(), EbpfError<BPFError>> {
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 /// Call process instruction, common to both Rust and C
fn call<'a>( fn call<'a>(
syscall: &mut dyn SyscallProcessInstruction<'a>, syscall: &mut dyn SyscallProcessInstruction<'a>,
@ -689,12 +741,19 @@ fn call<'a>(
// Translate data passed from the VM // Translate data passed from the VM
let instruction = syscall.translate_instruction(instruction_addr, ro_regions)?; 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 let caller_program_id = invoke_context
.get_caller() .get_caller()
.map_err(SyscallError::InstructionError)?; .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( let (accounts, refs) = syscall.translate_accounts(
&message, &message,
account_infos_addr, account_infos_addr,
@ -702,12 +761,6 @@ fn call<'a>(
ro_regions, ro_regions,
rw_regions, rw_regions,
)?; )?;
let signers = syscall.translate_signers(
caller_program_id,
signers_seeds_addr,
signers_seeds_len as usize,
ro_regions,
)?;
// Process instruction // Process instruction
@ -725,7 +778,6 @@ fn call<'a>(
&message, &message,
&executable_accounts, &executable_accounts,
&accounts, &accounts,
&signers,
*(&mut *invoke_context), *(&mut *invoke_context),
) { ) {
Ok(()) => (), Ok(()) => (),

View File

@ -122,31 +122,6 @@ impl PreAccount {
Ok(()) 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) { pub fn update(&mut self, account: &Account) {
self.lamports = account.lamports; self.lamports = account.lamports;
if self.data.len() != account.data.len() { if self.data.len() != account.data.len() {
@ -213,7 +188,6 @@ impl InvokeContext for ThisInvokeContext {
&mut self, &mut self,
message: &Message, message: &Message,
instruction: &CompiledInstruction, instruction: &CompiledInstruction,
signers: &[Pubkey],
accounts: &[Rc<RefCell<Account>>], accounts: &[Rc<RefCell<Account>>],
) -> Result<(), InstructionError> { ) -> Result<(), InstructionError> {
match self.program_ids.last() { match self.program_ids.last() {
@ -221,10 +195,9 @@ impl InvokeContext for ThisInvokeContext {
message, message,
instruction, instruction,
&mut self.pre_accounts, &mut self.pre_accounts,
accounts,
key, key,
&self.rent, &self.rent,
signers,
accounts,
), ),
None => Err(InstructionError::GenericError), // Should never happen None => Err(InstructionError::GenericError), // Should never happen
} }
@ -353,13 +326,12 @@ impl MessageProcessor {
message: &Message, message: &Message,
executable_accounts: &[(Pubkey, RefCell<Account>)], executable_accounts: &[(Pubkey, RefCell<Account>)],
accounts: &[Rc<RefCell<Account>>], accounts: &[Rc<RefCell<Account>>],
signers: &[Pubkey],
invoke_context: &mut dyn InvokeContext, invoke_context: &mut dyn InvokeContext,
) -> Result<(), InstructionError> { ) -> Result<(), InstructionError> {
let instruction = &message.instructions[0]; let instruction = &message.instructions[0];
// Verify the calling program hasn't misbehaved // 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 // Construct keyed accounts
let keyed_accounts = let keyed_accounts =
@ -371,7 +343,7 @@ impl MessageProcessor {
self.process_instruction(&keyed_accounts, &instruction.data, invoke_context); self.process_instruction(&keyed_accounts, &instruction.data, invoke_context);
if result.is_ok() { if result.is_ok() {
// Verify the called program has not misbehaved // 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(); invoke_context.pop();
@ -419,7 +391,7 @@ impl MessageProcessor {
pre_accounts: &[PreAccount], pre_accounts: &[PreAccount],
executable_accounts: &[(Pubkey, RefCell<Account>)], executable_accounts: &[(Pubkey, RefCell<Account>)],
accounts: &[Rc<RefCell<Account>>], accounts: &[Rc<RefCell<Account>>],
rent_collector: &RentCollector, rent: &Rent,
) -> Result<(), InstructionError> { ) -> Result<(), InstructionError> {
// Verify all executable accounts have zero outstanding refs // Verify all executable accounts have zero outstanding refs
Self::verify_account_references(executable_accounts)?; Self::verify_account_references(executable_accounts)?;
@ -433,7 +405,7 @@ impl MessageProcessor {
let account = accounts[account_index] let account = accounts[account_index]
.try_borrow_mut() .try_borrow_mut()
.map_err(|_| InstructionError::AccountBorrowOutstanding)?; .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()); pre_sum += u128::from(pre_accounts[unique_index].lamports());
post_sum += u128::from(account.lamports); post_sum += u128::from(account.lamports);
Ok(()) Ok(())
@ -453,10 +425,9 @@ impl MessageProcessor {
message: &Message, message: &Message,
instruction: &CompiledInstruction, instruction: &CompiledInstruction,
pre_accounts: &mut [PreAccount], pre_accounts: &mut [PreAccount],
accounts: &[Rc<RefCell<Account>>],
program_id: &Pubkey, program_id: &Pubkey,
rent: &Rent, rent: &Rent,
signers: &[Pubkey],
accounts: &[Rc<RefCell<Account>>],
) -> Result<(), InstructionError> { ) -> Result<(), InstructionError> {
// Verify the per-account instruction results // Verify the per-account instruction results
let (mut pre_sum, mut post_sum) = (0_u128, 0_u128); let (mut pre_sum, mut post_sum) = (0_u128, 0_u128);
@ -471,14 +442,7 @@ impl MessageProcessor {
.try_borrow_mut() .try_borrow_mut()
.map_err(|_| InstructionError::AccountBorrowOutstanding)?; .map_err(|_| InstructionError::AccountBorrowOutstanding)?;
pre_account.verify_cross_program( pre_account.verify(&program_id, &rent, &account)?;
message.is_writable(account_index),
message.is_signer(account_index),
signers,
&program_id,
&rent,
&account,
)?;
pre_sum += u128::from(pre_account.lamports()); pre_sum += u128::from(pre_account.lamports());
post_sum += u128::from(account.lamports); post_sum += u128::from(account.lamports);
@ -525,7 +489,7 @@ impl MessageProcessor {
&invoke_context.pre_accounts, &invoke_context.pre_accounts,
executable_accounts, executable_accounts,
accounts, accounts,
rent_collector, &rent_collector.rent,
)?; )?;
Ok(()) Ok(())
} }
@ -622,7 +586,6 @@ mod tests {
.verify_and_update( .verify_and_update(
&message, &message,
&message.instructions[0], &message.instructions[0],
&[],
&accounts[not_owned_index..owned_index + 1], &accounts[not_owned_index..owned_index + 1],
) )
.unwrap(); .unwrap();
@ -638,7 +601,6 @@ mod tests {
invoke_context.verify_and_update( invoke_context.verify_and_update(
&message, &message,
&message.instructions[0], &message.instructions[0],
&[],
&accounts[not_owned_index..owned_index + 1], &accounts[not_owned_index..owned_index + 1],
), ),
Err(InstructionError::ExternalAccountDataModified) Err(InstructionError::ExternalAccountDataModified)
@ -685,23 +647,16 @@ mod tests {
); );
} }
struct Change<'a> { struct Change {
program_id: Pubkey, program_id: Pubkey,
message_is_writable: bool,
message_is_signer: bool,
signers: &'a [Pubkey],
rent: Rent, rent: Rent,
pre: PreAccount, pre: PreAccount,
post: Account, post: Account,
} }
impl<'a> Change<'a> { impl Change {
pub fn new(owner: &Pubkey, program_id: &Pubkey) -> Self { pub fn new(owner: &Pubkey, program_id: &Pubkey) -> Self {
Self { Self {
// key: Pubkey::new_rand(),
program_id: *program_id, program_id: *program_id,
message_is_writable: false,
message_is_signer: false,
signers: &[],
rent: Rent::default(), rent: Rent::default(),
pre: PreAccount::new( pre: PreAccount::new(
&Pubkey::new_rand(), &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 { pub fn read_only(mut self) -> Self {
self.pre.is_writable = false; self.pre.is_writable = false;
self 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 { pub fn executable(mut self, pre: bool, post: bool) -> Self {
self.pre.is_executable = pre; self.pre.is_executable = pre;
self.post.executable = post; self.post.executable = post;
@ -768,16 +707,6 @@ mod tests {
pub fn verify(&self) -> Result<(), InstructionError> { pub fn verify(&self) -> Result<(), InstructionError> {
self.pre.verify(&self.program_id, &self.rent, &self.post) 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] #[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] #[test]
fn test_verify_account_changes_data_len() { fn test_verify_account_changes_data_len() {
let alice_program_id = Pubkey::new_rand(); let alice_program_id = Pubkey::new_rand();
@ -1446,7 +1322,6 @@ mod tests {
&message, &message,
&executable_accounts, &executable_accounts,
&accounts, &accounts,
&[],
&mut invoke_context, &mut invoke_context,
), ),
Err(InstructionError::ExternalAccountDataModified) Err(InstructionError::ExternalAccountDataModified)
@ -1474,7 +1349,6 @@ mod tests {
&message, &message,
&executable_accounts, &executable_accounts,
&accounts, &accounts,
&[],
&mut invoke_context, &mut invoke_context,
), ),
case.1 case.1

View File

@ -154,7 +154,6 @@ pub trait InvokeContext {
&mut self, &mut self,
message: &Message, message: &Message,
instruction: &CompiledInstruction, instruction: &CompiledInstruction,
signers: &[Pubkey],
accounts: &[Rc<RefCell<Account>>], accounts: &[Rc<RefCell<Account>>],
) -> Result<(), InstructionError>; ) -> Result<(), InstructionError>;
fn get_caller(&self) -> Result<&Pubkey, InstructionError>; fn get_caller(&self) -> Result<&Pubkey, InstructionError>;

View File

@ -140,14 +140,6 @@ pub enum InstructionError {
#[error("Unsupported program id")] #[error("Unsupported program id")]
UnsupportedProgramId, 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 /// Cross-program invocation call depth too deep
#[error("Cross-program invocation call depth too deep")] #[error("Cross-program invocation call depth too deep")]
CallDepth, CallDepth,