Enforce only system program can allocate accounts (#6386)

This commit is contained in:
Jack May 2019-10-16 10:47:45 -07:00 committed by GitHub
parent 8dd24bc7d9
commit 1fd84cb52b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 30 additions and 8 deletions

View File

@ -67,7 +67,7 @@ fn verify_instruction(
// Verify the transaction // Verify the transaction
// Make sure that program_id is still the same or this was just assigned by the system program, // Make sure that program_id is still the same or this was just assigned by the system program,
// but even the system program can't touch a credit-only account // but even the system program can't touch a credit-only account.
if pre.owner != post.owner && (!is_debitable || !system_program::check_id(&program_id)) { if pre.owner != post.owner && (!is_debitable || !system_program::check_id(&program_id)) {
return Err(InstructionError::ModifiedProgramId); return Err(InstructionError::ModifiedProgramId);
} }
@ -75,10 +75,14 @@ fn verify_instruction(
if *program_id != post.owner && pre.lamports > post.lamports { if *program_id != post.owner && pre.lamports > post.lamports {
return Err(InstructionError::ExternalAccountLamportSpend); return Err(InstructionError::ExternalAccountLamportSpend);
} }
// The balance of credit-only accounts may only increase // The balance of credit-only accounts may only increase.
if !is_debitable && pre.lamports > post.lamports { if !is_debitable && pre.lamports > post.lamports {
return Err(InstructionError::CreditOnlyLamportSpend); return Err(InstructionError::CreditOnlyLamportSpend);
} }
// Only system accounts can change the size of the data.
if !system_program::check_id(&program_id) && pre.data.len() != post.data.len() {
return Err(InstructionError::AccountDataSizeChanged);
}
// For accounts unassigned to the program, the data may not change. // For accounts unassigned to the program, the data may not change.
if *program_id != post.owner && !system_program::check_id(&program_id) && pre.data != post.data if *program_id != post.owner && !system_program::check_id(&program_id) && pre.data != post.data
{ {
@ -88,9 +92,8 @@ fn verify_instruction(
if !is_debitable && pre.data != post.data { if !is_debitable && pre.data != post.data {
return Err(InstructionError::CreditOnlyDataModified); return Err(InstructionError::CreditOnlyDataModified);
} }
// executable is one-way (false->true) and // executable is one-way (false->true) and
// only system or the account owner may modify // only system or the account owner may modify.
if pre.executable != post.executable if pre.executable != post.executable
&& (!is_debitable && (!is_debitable
|| pre.executable || pre.executable
@ -98,8 +101,7 @@ fn verify_instruction(
{ {
return Err(InstructionError::ExecutableModified); return Err(InstructionError::ExecutableModified);
} }
// No one modifies rent_epoch (yet).
// no one modifies rent_epoch (yet)
if pre.rent_epoch != post.rent_epoch { if pre.rent_epoch != post.rent_epoch {
return Err(InstructionError::RentEpochModified); return Err(InstructionError::RentEpochModified);
} }
@ -244,7 +246,7 @@ impl MessageProcessor {
let program_id = instruction.program_id(&message.account_keys); let program_id = instruction.program_id(&message.account_keys);
assert_eq!(instruction.accounts.len(), program_accounts.len()); assert_eq!(instruction.accounts.len(), program_accounts.len());
// TODO: the runtime should be checking read/write access to memory // TODO: the runtime should be checking read/write access to memory
// we are trusting the hard-coded programs not to clobber or allocate // we are trusting the hard-coded programs not to clobber
let pre_total: u128 = program_accounts let pre_total: u128 = program_accounts
.iter() .iter()
.map(|a| u128::from(a.lamports)) .map(|a| u128::from(a.lamports))
@ -467,7 +469,7 @@ mod tests {
let change_data = let change_data =
|program_id: &Pubkey, is_debitable: bool| -> Result<(), InstructionError> { |program_id: &Pubkey, is_debitable: bool| -> Result<(), InstructionError> {
let pre = Account::new(0, 0, &alice_program_id); let pre = Account::new_data(0, &[0], &alice_program_id).unwrap();
let post = Account::new_data(0, &[42], &alice_program_id).unwrap(); let post = Account::new_data(0, &[42], &alice_program_id).unwrap();
verify_instruction(is_debitable, &program_id, &pre, &post) verify_instruction(is_debitable, &program_id, &pre, &post)
}; };
@ -535,6 +537,23 @@ mod tests {
); );
} }
#[test]
fn test_verify_instruction_data_size_changed() {
let alice_program_id = Pubkey::new_rand();
let pre = Account::new_data(42, &[42], &alice_program_id).unwrap();
let post = Account::new_data(42, &[42, 42], &alice_program_id).unwrap();
assert_eq!(
verify_instruction(true, &system_program::id(), &pre, &post),
Ok(()),
"system program should be able to change account data size"
);
assert_eq!(
verify_instruction(true, &alice_program_id, &pre, &post),
Err(InstructionError::AccountDataSizeChanged),
"non-system programs cannot change their data size"
);
}
#[test] #[test]
fn test_process_message_credit_only_handling() { fn test_process_message_credit_only_handling() {
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]

View File

@ -70,6 +70,9 @@ pub enum InstructionError {
/// The instruction expected additional account keys /// The instruction expected additional account keys
NotEnoughAccountKeys, NotEnoughAccountKeys,
/// A non-system program changed the size of the account data
AccountDataSizeChanged,
/// CustomError allows on-chain programs to implement program-specific error types and see /// CustomError allows on-chain programs to implement program-specific error types and see
/// them returned by the Solana runtime. A CustomError may be any type that is represented /// them returned by the Solana runtime. A CustomError may be any type that is represented
/// as or serialized to a u32 integer. /// as or serialized to a u32 integer.