Skip to content

Commit 53f4bde

Browse files
add checked instructions sysvar api (backport solana-labs#20790) (solana-labs#20816)
* add checked instructions sysvar api (solana-labs#20790) (cherry picked from commit a8098f3) # Conflicts: # programs/bpf/rust/sysvar/src/lib.rs # runtime/src/accounts.rs * resolve conflicts Co-authored-by: Jack May <jack@solana.com>
1 parent 232731e commit 53f4bde

File tree

4 files changed

+102
-4
lines changed

4 files changed

+102
-4
lines changed

programs/bpf/rust/instruction_introspection/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ fn process_instruction(
2626
return Err(ProgramError::InvalidAccountData);
2727
}
2828

29-
let instruction = instructions::load_instruction_at(
29+
let instruction = instructions::load_instruction_at_checked(
3030
secp_instruction_index as usize,
31-
&instruction_accounts.try_borrow_data()?,
31+
instruction_accounts,
3232
)
3333
.map_err(|_| ProgramError::InvalidAccountData)?;
3434

programs/bpf/rust/sysvar/src/lib.rs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use solana_program::{
66
entrypoint,
77
entrypoint::ProgramResult,
88
fee_calculator::FeeCalculator,
9+
instruction::{AccountMeta, Instruction},
910
msg,
1011
program_error::ProgramError,
1112
pubkey::Pubkey,
@@ -19,7 +20,7 @@ use solana_program::{
1920
entrypoint!(process_instruction);
2021
#[allow(clippy::unnecessary_wraps)]
2122
pub fn process_instruction(
22-
_program_id: &Pubkey,
23+
program_id: &Pubkey,
2324
accounts: &[AccountInfo],
2425
_instruction_data: &[u8],
2526
) -> ProgramResult {
@@ -57,8 +58,30 @@ pub fn process_instruction(
5758
// Instructions
5859
msg!("Instructions identifier:");
5960
sysvar::instructions::id().log();
61+
assert_eq!(*accounts[4].owner, sysvar::id());
6062
let index = instructions::load_current_index(&accounts[5].try_borrow_data()?);
63+
let instruction = instructions::load_instruction_at_checked(index as usize, &accounts[5])?;
6164
assert_eq!(0, index);
65+
assert_eq!(
66+
instruction,
67+
Instruction::new_with_bytes(
68+
*program_id,
69+
&[] as &[u8],
70+
vec![
71+
AccountMeta::new(*accounts[0].key, true),
72+
AccountMeta::new(*accounts[1].key, false),
73+
AccountMeta::new_readonly(*accounts[2].key, false),
74+
AccountMeta::new_readonly(*accounts[3].key, false),
75+
AccountMeta::new_readonly(*accounts[4].key, false),
76+
AccountMeta::new_readonly(*accounts[5].key, false),
77+
AccountMeta::new_readonly(*accounts[6].key, false),
78+
AccountMeta::new_readonly(*accounts[7].key, false),
79+
AccountMeta::new_readonly(*accounts[8].key, false),
80+
AccountMeta::new_readonly(*accounts[9].key, false),
81+
AccountMeta::new_readonly(*accounts[10].key, false),
82+
],
83+
)
84+
);
6285

6386
// Recent Blockhashes
6487
{

sdk/benches/serialize_instructions.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ fn bench_manual_instruction_deserialize(b: &mut Bencher) {
4949
let serialized = message.serialize_instructions(DEMOTE_PROGRAM_WRITE_LOCKS);
5050
b.iter(|| {
5151
for i in 0..instructions.len() {
52+
#[allow(deprecated)]
5253
test::black_box(instructions::load_instruction_at(i, &serialized).unwrap());
5354
}
5455
});
@@ -60,6 +61,7 @@ fn bench_manual_instruction_deserialize_single(b: &mut Bencher) {
6061
let message = Message::new(&instructions, None);
6162
let serialized = message.serialize_instructions(DEMOTE_PROGRAM_WRITE_LOCKS);
6263
b.iter(|| {
64+
#[allow(deprecated)]
6365
test::black_box(instructions::load_instruction_at(3, &serialized).unwrap());
6466
});
6567
}

sdk/program/src/sysvar/instructions.rs

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
#![allow(clippy::integer_arithmetic)]
22
//! This account contains the serialized transaction instructions
33
4-
use crate::{instruction::Instruction, sanitize::SanitizeError};
4+
use crate::{
5+
account_info::AccountInfo, instruction::Instruction, program_error::ProgramError,
6+
sanitize::SanitizeError,
7+
};
58

69
// Instructions Sysvar, dummy type, use the associated helpers instead of the Sysvar trait
710
pub struct Instructions();
@@ -23,13 +26,36 @@ pub fn store_current_index(data: &mut [u8], instruction_index: u16) {
2326
}
2427

2528
/// Load an instruction at the specified index
29+
#[deprecated(
30+
since = "1.8.0",
31+
note = "Unsafe because the sysvar accounts address is not checked, please use `load_instruction_at_checked` instead"
32+
)]
2633
pub fn load_instruction_at(index: usize, data: &[u8]) -> Result<Instruction, SanitizeError> {
2734
crate::message::Message::deserialize_instruction(index, data)
2835
}
2936

37+
/// Load an instruction at the specified index
38+
pub fn load_instruction_at_checked(
39+
index: usize,
40+
instruction_sysvar_account_info: &AccountInfo,
41+
) -> Result<Instruction, ProgramError> {
42+
if !check_id(instruction_sysvar_account_info.key) {
43+
return Err(ProgramError::UnsupportedSysvar);
44+
}
45+
46+
let instruction_sysvar = instruction_sysvar_account_info.try_borrow_data()?;
47+
crate::message::Message::deserialize_instruction(index, &instruction_sysvar).map_err(|err| {
48+
match err {
49+
SanitizeError::IndexOutOfBounds => ProgramError::InvalidArgument,
50+
_ => ProgramError::InvalidInstructionData,
51+
}
52+
})
53+
}
54+
3055
#[cfg(test)]
3156
mod tests {
3257
use super::*;
58+
use crate::{instruction::AccountMeta, message::Message, pubkey::Pubkey};
3359

3460
#[test]
3561
fn test_load_store_instruction() {
@@ -38,4 +64,51 @@ mod tests {
3864
assert_eq!(load_current_index(&data), 3);
3965
assert_eq!([4u8; 8], data[0..8]);
4066
}
67+
68+
#[test]
69+
fn test_load_instruction_at_checked() {
70+
let instruction1 = Instruction::new_with_bincode(
71+
Pubkey::new_unique(),
72+
&0,
73+
vec![AccountMeta::new(Pubkey::new_unique(), false)],
74+
);
75+
let instruction2 = Instruction::new_with_bincode(
76+
Pubkey::new_unique(),
77+
&0,
78+
vec![AccountMeta::new(Pubkey::new_unique(), false)],
79+
);
80+
let message = Message::new(
81+
&[instruction1.clone(), instruction2.clone()],
82+
Some(&Pubkey::new_unique()),
83+
);
84+
85+
let key = id();
86+
let mut lamports = 0;
87+
let mut data = message.serialize_instructions(true);
88+
data.resize(data.len() + 2, 0);
89+
let owner = crate::sysvar::id();
90+
let account_info = AccountInfo::new(
91+
&key,
92+
false,
93+
false,
94+
&mut lamports,
95+
&mut data,
96+
&owner,
97+
false,
98+
0,
99+
);
100+
101+
assert_eq!(
102+
instruction1,
103+
load_instruction_at_checked(0, &account_info).unwrap()
104+
);
105+
assert_eq!(
106+
instruction2,
107+
load_instruction_at_checked(1, &account_info).unwrap()
108+
);
109+
assert_eq!(
110+
Err(ProgramError::InvalidArgument),
111+
load_instruction_at_checked(2, &account_info)
112+
);
113+
}
41114
}

0 commit comments

Comments
 (0)