Skip to content
This repository has been archived by the owner on Jan 13, 2025. It is now read-only.

Commit

Permalink
Add more CPI call depth tests
Browse files Browse the repository at this point in the history
  • Loading branch information
jackcmay committed Jun 2, 2021
1 parent 3a3454d commit cd85e68
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 60 deletions.
62 changes: 37 additions & 25 deletions programs/bpf/c/src/invoke/invoke.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ static const uint8_t TEST_RETURN_ERROR = 11;
static const uint8_t TEST_PRIVILEGE_DEESCALATION_ESCALATION_SIGNER = 12;
static const uint8_t TEST_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE = 13;
static const uint8_t TEST_WRITABLE_DEESCALATION_WRITABLE = 14;
static const uint8_t TEST_NESTED_INVOKE_TOO_DEEP = 15;

static const int MINT_INDEX = 0;
static const int ARGUMENT_INDEX = 1;
Expand All @@ -31,6 +32,35 @@ static const int DERIVED_KEY3_INDEX = 8;
static const int SYSTEM_PROGRAM_INDEX = 9;
static const int FROM_INDEX = 10;

uint64_t do_nested_invokes(uint64_t num_nested_invokes,
SolAccountInfo *accounts, uint64_t num_accounts) {
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},
{accounts[INVOKED_PROGRAM_INDEX].key, false, false}};
uint8_t data[] = {NESTED_INVOKE, num_nested_invokes};
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, num_accounts));
sol_log("2nd invoke from first program");
sol_assert(SUCCESS == sol_invoke(&instruction, accounts, num_accounts));

sol_assert(*accounts[ARGUMENT_INDEX].lamports ==
42 - 5 + (2 * num_nested_invokes));
sol_assert(*accounts[INVOKED_ARGUMENT_INDEX].lamports ==
10 + 5 - (2 * num_nested_invokes));

return SUCCESS;
}

extern uint64_t entrypoint(const uint8_t *input) {
sol_log("Invoke C program");

Expand Down Expand Up @@ -203,32 +233,9 @@ extern uint64_t entrypoint(const uint8_t *input) {
sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts)));
}

sol_log("Test invoke");
sol_log("Test nested 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},
{accounts[INVOKED_PROGRAM_DUP_INDEX].key, false, false}};
uint8_t data[] = {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 + 1 + 1);
sol_assert(*accounts[INVOKED_ARGUMENT_INDEX].lamports ==
10 + 5 - 1 - 1 - 1 - 1);
sol_assert(SUCCESS == do_nested_invokes(4, accounts, params.ka_num));
}

sol_log("Test privilege deescalation");
Expand Down Expand Up @@ -523,6 +530,11 @@ extern uint64_t entrypoint(const uint8_t *input) {
}
break;
}
case TEST_NESTED_INVOKE_TOO_DEEP: {
do_nested_invokes(5, accounts, params.ka_num);
break;
}

default:
sol_panic();
}
Expand Down
11 changes: 6 additions & 5 deletions programs/bpf/c/src/invoked/invoked.c
Original file line number Diff line number Diff line change
Expand Up @@ -228,16 +228,17 @@ extern uint64_t entrypoint(const uint8_t *input) {
*accounts[INVOKED_ARGUMENT_INDEX].lamports -= 1;
*accounts[ARGUMENT_INDEX].lamports += 1;

if (params.ka_num == 3) {
uint8_t remaining_invokes = params.data[1];
if (remaining_invokes > 1) {
sol_log("Invoke again");
SolAccountMeta arguments[] = {
{accounts[INVOKED_ARGUMENT_INDEX].key, true, true},
{accounts[ARGUMENT_INDEX].key, true, true}};
uint8_t data[] = {NESTED_INVOKE};
{accounts[ARGUMENT_INDEX].key, true, true},
{accounts[INVOKED_PROGRAM_INDEX].key, false, false}};
uint8_t data[] = {NESTED_INVOKE, remaining_invokes - 1};
const SolInstruction instruction = {accounts[INVOKED_PROGRAM_INDEX].key,
arguments, SOL_ARRAY_SIZE(arguments),
data, SOL_ARRAY_SIZE(data)};

sol_log("Invoke again");
sol_assert(SUCCESS == sol_invoke(&instruction, accounts, params.ka_num));
} else {
sol_log("Last invoked");
Expand Down
64 changes: 37 additions & 27 deletions programs/bpf/rust/invoke/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,19 +30,50 @@ const TEST_RETURN_ERROR: u8 = 11;
const TEST_PRIVILEGE_DEESCALATION_ESCALATION_SIGNER: u8 = 12;
const TEST_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE: u8 = 13;
const TEST_WRITABLE_DEESCALATION_WRITABLE: u8 = 14;
const TEST_NESTED_INVOKE_TOO_DEEP: u8 = 15;

// const MINT_INDEX: usize = 0;
// const MINT_INDEX: usize = 0; // unused placeholder
const ARGUMENT_INDEX: usize = 1;
const INVOKED_PROGRAM_INDEX: usize = 2;
const INVOKED_ARGUMENT_INDEX: usize = 3;
const INVOKED_PROGRAM_DUP_INDEX: usize = 4;
// const ARGUMENT_DUP_INDEX: usize = 5;
// const ARGUMENT_DUP_INDEX: usize = 5; unused placeholder
const DERIVED_KEY1_INDEX: usize = 6;
const DERIVED_KEY2_INDEX: usize = 7;
const DERIVED_KEY3_INDEX: usize = 8;
const SYSTEM_PROGRAM_INDEX: usize = 9;
const FROM_INDEX: usize = 10;

fn do_nested_invokes(num_nested_invokes: u64,accounts: &[AccountInfo]) -> ProgramResult {
assert!(accounts[ARGUMENT_INDEX].is_signer);

let pre_argument_lamports = accounts[ARGUMENT_INDEX].lamports();
let pre_invoke_argument_lamports = accounts[INVOKED_ARGUMENT_INDEX].lamports();
**accounts[ARGUMENT_INDEX].lamports.borrow_mut() -= 5;
**accounts[INVOKED_ARGUMENT_INDEX].lamports.borrow_mut() += 5;

msg!("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_INDEX].key, false, false),
],
vec![NESTED_INVOKE, num_nested_invokes as u8],
);
invoke(&instruction, accounts)?;
msg!("2nd invoke from first program");
invoke(&instruction, accounts)?;

assert_eq!(accounts[ARGUMENT_INDEX].lamports(), pre_argument_lamports - 5 + (2 * num_nested_invokes));
assert_eq!(
accounts[INVOKED_ARGUMENT_INDEX].lamports(),
pre_invoke_argument_lamports + 5 - (2 * num_nested_invokes)
);
Ok(())
}

entrypoint!(process_instruction);
fn process_instruction(
program_id: &Pubkey,
Expand Down Expand Up @@ -282,31 +313,7 @@ fn process_instruction(

msg!("Test nested invoke");
{
assert!(accounts[ARGUMENT_INDEX].is_signer);

**accounts[ARGUMENT_INDEX].lamports.borrow_mut() -= 5;
**accounts[INVOKED_ARGUMENT_INDEX].lamports.borrow_mut() += 5;

msg!("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![NESTED_INVOKE],
);
invoke(&instruction, accounts)?;
msg!("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
);
do_nested_invokes(4, accounts)?;
}

msg!("Test privilege deescalation");
Expand Down Expand Up @@ -602,6 +609,9 @@ fn process_instruction(
accounts[INVOKED_ARGUMENT_INDEX].data.borrow_mut()[..NUM_BYTES]
);
}
TEST_NESTED_INVOKE_TOO_DEEP => {
let _ = do_nested_invokes(5, accounts);
}
_ => panic!(),
}

Expand Down
10 changes: 7 additions & 3 deletions programs/bpf/rust/invoked/src/processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,21 +202,25 @@ fn process_instruction(
msg!("nested invoke");
const ARGUMENT_INDEX: usize = 0;
const INVOKED_ARGUMENT_INDEX: usize = 1;
const INVOKED_PROGRAM_INDEX: usize = 3;
const INVOKED_PROGRAM_INDEX: usize = 2;

assert!(accounts[INVOKED_ARGUMENT_INDEX].is_signer);
assert!(instruction_data.len() > 1);

**accounts[INVOKED_ARGUMENT_INDEX].lamports.borrow_mut() -= 1;
**accounts[ARGUMENT_INDEX].lamports.borrow_mut() += 1;
if accounts.len() > 2 {
let remaining_invokes = instruction_data[1];
if remaining_invokes > 1 {

msg!("Invoke again");
let invoked_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),
],
vec![NESTED_INVOKE],
vec![NESTED_INVOKE, remaining_invokes - 1],
);
invoke(&invoked_instruction, accounts)?;
} else {
Expand Down
15 changes: 15 additions & 0 deletions programs/bpf/tests/programs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -754,6 +754,7 @@ fn test_program_bpf_invoke_sanity() {
const TEST_PRIVILEGE_DEESCALATION_ESCALATION_SIGNER: u8 = 12;
const TEST_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE: u8 = 13;
const TEST_WRITABLE_DEESCALATION_WRITABLE: u8 = 14;
const TEST_NESTED_INVOKE_TOO_DEEP: u8 = 15;

#[allow(dead_code)]
#[derive(Debug)]
Expand Down Expand Up @@ -871,6 +872,10 @@ fn test_program_bpf_invoke_sanity() {
invoked_program_id.clone(),
invoked_program_id.clone(),
invoked_program_id.clone(),
invoked_program_id.clone(),
invoked_program_id.clone(),
invoked_program_id.clone(),
invoked_program_id.clone(),
],
Languages::Rust => vec![
solana_sdk::system_program::id(),
Expand All @@ -890,6 +895,10 @@ fn test_program_bpf_invoke_sanity() {
invoked_program_id.clone(),
invoked_program_id.clone(),
invoked_program_id.clone(),
invoked_program_id.clone(),
invoked_program_id.clone(),
invoked_program_id.clone(),
invoked_program_id.clone(),
solana_sdk::system_program::id(),
],
};
Expand Down Expand Up @@ -1001,6 +1010,12 @@ fn test_program_bpf_invoke_sanity() {
&[invoked_program_id.clone()],
);

do_invoke_failure_test_local(
TEST_NESTED_INVOKE_TOO_DEEP,
TransactionError::InstructionError(0, InstructionError::CallDepth),
&[invoked_program_id.clone(), invoked_program_id.clone(), invoked_program_id.clone(), invoked_program_id.clone(), invoked_program_id.clone()],
);

// Check resulting state

assert_eq!(43, bank.get_balance(&derived_key1));
Expand Down

0 comments on commit cd85e68

Please sign in to comment.