Skip to content

Commit

Permalink
Allow programs to realloc their accounts within limits
Browse files Browse the repository at this point in the history
  • Loading branch information
jackcmay committed Sep 3, 2021
1 parent 5e25ee5 commit c1d60f4
Show file tree
Hide file tree
Showing 15 changed files with 1,030 additions and 35 deletions.
30 changes: 19 additions & 11 deletions program-runtime/src/instruction_processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use solana_sdk::{
process_instruction::{Executor, InvokeContext, Logger, ProcessInstructionWithContext},
pubkey::Pubkey,
rent::Rent,
system_program,
system_instruction::MAX_PERMITTED_DATA_LENGTH,
};
use std::{
cell::{Ref, RefCell},
Expand Down Expand Up @@ -151,13 +151,14 @@ impl PreAccount {
}
}

// Only the system program can change the size of the data
// and only if the system program owns the account
// Account data size cannot exceed a maxumum length
if post.data().len() > MAX_PERMITTED_DATA_LENGTH as usize {
return Err(InstructionError::InvalidRealloc);
}

// The owner of the account can change the size of the data
let data_len_changed = pre.data().len() != post.data().len();
if data_len_changed
&& (!system_program::check_id(program_id) // line coverage used to get branch coverage
|| !system_program::check_id(pre.owner()))
{
if data_len_changed && program_id != pre.owner() {
return Err(InstructionError::AccountDataSizeChanged);
}

Expand Down Expand Up @@ -774,7 +775,7 @@ impl InstructionProcessor {
#[cfg(test)]
mod tests {
use super::*;
use solana_sdk::{account::Account, instruction::InstructionError};
use solana_sdk::{account::Account, instruction::InstructionError, system_program};

#[test]
fn test_is_zeroed() {
Expand Down Expand Up @@ -1171,11 +1172,18 @@ mod tests {
"system program should not be able to change another program's account data size"
);
assert_eq!(
Change::new(&alice_program_id, &alice_program_id)
Change::new(&alice_program_id, &solana_sdk::pubkey::new_rand())
.data(vec![0], vec![0, 0])
.verify(),
Err(InstructionError::AccountDataSizeChanged),
"non-system programs cannot change their data size"
"one program should not be able to change another program's account data size"
);
assert_eq!(
Change::new(&alice_program_id, &alice_program_id)
.data(vec![0], vec![0, 0])
.verify(),
Ok(()),
"programs can change their own data size"
);
assert_eq!(
Change::new(&system_program::id(), &system_program::id())
Expand All @@ -1197,7 +1205,7 @@ mod tests {
.executable(false, true)
.verify(),
Err(InstructionError::ExecutableModified),
"Program should not be able to change owner and executable at the same time"
"program should not be able to change owner and executable at the same time"
);
}

Expand Down
17 changes: 17 additions & 0 deletions programs/bpf/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 5 additions & 2 deletions programs/bpf/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ itertools = "0.10.1"
log = "0.4.11"
miow = "0.3.6"
net2 = "0.2.37"
solana-bpf-loader-program = { path = "../bpf_loader", version = "=1.8.0" }
solana-bpf-loader-program = { path = "../bpf_loader", version = "=1.8.0"}
solana-bpf-rust-realloc = { path = "rust/realloc", version = "=1.8.0", features = ["custom-heap"]}
solana-bpf-rust-realloc-invoke = { path = "rust/realloc_invoke", version = "=1.8.0", features = ["custom-heap"]}
solana-cli-output = { path = "../../cli-output", version = "=1.8.0" }
solana-logger = { path = "../../logger", version = "=1.8.0" }
solana-measure = { path = "../../measure", version = "=1.8.0" }
Expand All @@ -36,7 +38,6 @@ solana-sdk = { path = "../../sdk", version = "=1.8.0" }
solana-transaction-status = { path = "../../transaction-status", version = "=1.8.0" }
solana-account-decoder = { path = "../../account-decoder", version = "=1.8.0" }


[[bench]]
name = "bpf_loader"

Expand Down Expand Up @@ -70,6 +71,8 @@ members = [
"rust/param_passing",
"rust/param_passing_dep",
"rust/rand",
"rust/realloc",
"rust/realloc_invoke",
"rust/ro_modify",
"rust/ro_account_modify",
"rust/sanity",
Expand Down
2 changes: 2 additions & 0 deletions programs/bpf/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ fn main() {
"panic",
"param_passing",
"rand",
"realloc",
"realloc_invoke",
"ro_modify",
"ro_account_modify",
"sanity",
Expand Down
22 changes: 22 additions & 0 deletions programs/bpf/rust/realloc/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[package]
name = "solana-bpf-rust-realloc"
version = "1.8.0"
description = "Solana BPF test program written in Rust"
authors = ["Solana Maintainers <[email protected]>"]
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
documentation = "https://docs.rs/solana-bpf-rust-realloc"
edition = "2018"

[features]
custom-heap = []

[dependencies]
solana-program = { path = "../../../../sdk/program", version = "=1.8.0" }

[lib]
crate-type = ["lib", "cdylib"]

[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
10 changes: 10 additions & 0 deletions programs/bpf/rust/realloc/src/instructions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
//! @brief Example Rust-based BPF realloc test program
pub const REALLOC_ZERO: u8 = 0;
pub const REALLOC_TO: u8 = 1;
pub const REALLOC_MAX_PLUS_ONE: u8 = 2;
pub const REALLOC_MAX: u8 = 3;
pub const REALLOC_AND_ASSIGN: u8 = 4;
pub const REALLOC_AND_ASSIGN_TO_SELF: u8 = 5;
pub const ASSIGN_TO_SELF_AND_REALLOC: u8 = 6;
pub const CHECK: u8 = 7;
91 changes: 91 additions & 0 deletions programs/bpf/rust/realloc/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
//! @brief Example Rust-based BPF realloc test program
pub mod instructions;

extern crate solana_program;
use crate::instructions::*;
use solana_program::{
account_info::AccountInfo, entrypoint, entrypoint::ProgramResult,
entrypoint::MAX_PERMITTED_DATA_INCREASE, msg, program::invoke, pubkey::Pubkey,
system_instruction, system_program,
};
use std::convert::TryInto;

entrypoint!(process_instruction);
#[allow(clippy::unnecessary_wraps)]
fn process_instruction(
program_id: &Pubkey,
accounts: &[AccountInfo],
instruction_data: &[u8],
) -> ProgramResult {
let account = &accounts[0];
let pre_len = account.data_len();

match instruction_data[0] {
REALLOC_ZERO => {
msg!("realloc to zero");
account.realloc(0)?;
assert_eq!(0, account.data_len());
}
REALLOC_TO => {
let (bytes, _) = instruction_data[2..].split_at(std::mem::size_of::<usize>());
let new_len = usize::from_le_bytes(bytes.try_into().unwrap());
msg!("realloc to {}", new_len);
account.realloc(new_len)?;
assert_eq!(new_len, account.data_len());
}
REALLOC_MAX_PLUS_ONE => {
msg!("realloc to max + 1");
account.realloc(pre_len + MAX_PERMITTED_DATA_INCREASE + 1)?;
}
REALLOC_MAX => {
msg!("realloc to max");
account.realloc(pre_len + MAX_PERMITTED_DATA_INCREASE)?;
assert_eq!(pre_len + MAX_PERMITTED_DATA_INCREASE, account.data_len());
account.try_borrow_mut_data()?[pre_len..].fill(instruction_data[1]);
}
REALLOC_AND_ASSIGN => {
msg!("realloc and assign");
account.realloc(pre_len + MAX_PERMITTED_DATA_INCREASE)?;
assert_eq!(pre_len + MAX_PERMITTED_DATA_INCREASE, account.data_len());
account.assign(&system_program::id());
assert_eq!(*account.owner, system_program::id());
}
REALLOC_AND_ASSIGN_TO_SELF => {
msg!("realloc and assign to self via cpi");
account.realloc(pre_len + MAX_PERMITTED_DATA_INCREASE)?;
assert_eq!(pre_len + MAX_PERMITTED_DATA_INCREASE, account.data_len());
invoke(
&system_instruction::assign(account.key, program_id),
accounts,
)?;
assert_eq!(pre_len + MAX_PERMITTED_DATA_INCREASE, account.data_len());
}
ASSIGN_TO_SELF_AND_REALLOC => {
msg!("assign to self via cpi and realloc");
invoke(
&system_instruction::assign(account.key, program_id),
accounts,
)?;
account.realloc(pre_len + MAX_PERMITTED_DATA_INCREASE)?;
assert_eq!(pre_len + MAX_PERMITTED_DATA_INCREASE, account.data_len());
assert_eq!(account.owner, program_id);
}
CHECK => {
msg!("check");
let (bytes, _) = instruction_data[2..].split_at(std::mem::size_of::<usize>());
let new_len = usize::from_le_bytes(bytes.try_into().unwrap());
assert_eq!(new_len, account.data_len());
let data = account.try_borrow_mut_data()?;
for x in data[0..5].iter() {
assert_eq!(0, *x);
}
for x in data[5..].iter() {
assert_eq!(instruction_data[1], *x);
}
}
_ => panic!(),
}

Ok(())
}
23 changes: 23 additions & 0 deletions programs/bpf/rust/realloc_invoke/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[package]
name = "solana-bpf-rust-realloc-invoke"
version = "1.8.0"
description = "Solana BPF test program written in Rust"
authors = ["Solana Maintainers <[email protected]>"]
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
documentation = "https://docs.rs/solana-bpf-rust-realloc-invoke"
edition = "2018"

[features]
custom-heap = []

[dependencies]
solana-program = { path = "../../../../sdk/program", version = "=1.8.0" }
solana-bpf-rust-realloc = { path = "../realloc", version = "=1.8.0", features = ["custom-heap"]}

[lib]
crate-type = ["lib", "cdylib"]

[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
14 changes: 14 additions & 0 deletions programs/bpf/rust/realloc_invoke/src/instructions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//! @brief Example Rust-based BPF realloc test program
pub const INVOKE_REALLOC_ZERO_RO: u8 = 0;
pub const INVOKE_REALLOC_ZERO: u8 = 1;
pub const INVOKE_REALLOC_MAX_PLUS_ONE: u8 = 2;
pub const INVOKE_REALLOC_MAX: u8 = 3;
pub const INVOKE_REALLOC_AND_ASSIGN: u8 = 4;
pub const INVOKE_REALLOC_AND_ASSIGN_TO_SELF: u8 = 5;
pub const INVOKE_ASSIGN_TO_SELF_AND_REALLOC: u8 = 6;
pub const INVOKE_REALLOC_INVOKE_CHECK: u8 = 7;
pub const INVOKE_OVERFLOW: u8 = 8;
pub const INVOKE_REALLOC_TO: u8 = 9;
pub const INVOKE_REALLOC_RECURSIVE: u8 = 10;
pub const INVOKE_CREATE_ACCOUNT_CHECK: u8 = 11;
Loading

0 comments on commit c1d60f4

Please sign in to comment.