From 37497657c6636bcc6c85bde9f798bebad2332a78 Mon Sep 17 00:00:00 2001 From: Jack May Date: Wed, 30 Mar 2022 08:28:49 -0700 Subject: [PATCH] assert-type-assumptions (#23996) --- Cargo.lock | 1 + programs/bpf/Cargo.lock | 1 + programs/bpf/rust/sanity/src/lib.rs | 5 +- programs/bpf/tests/programs.rs | 6 +- programs/bpf_loader/src/syscalls.rs | 6 +- sdk/program/Cargo.toml | 1 + sdk/program/src/program.rs | 162 ++++++++++++++++++++++++++++ 7 files changed, 174 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 76fc3ee1c..b31bc24a9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5434,6 +5434,7 @@ dependencies = [ "lazy_static", "libsecp256k1 0.6.0", "log", + "memoffset", "num-derive", "num-traits", "parking_lot 0.12.0", diff --git a/programs/bpf/Cargo.lock b/programs/bpf/Cargo.lock index 10142ca9d..2fce41d86 100644 --- a/programs/bpf/Cargo.lock +++ b/programs/bpf/Cargo.lock @@ -3642,6 +3642,7 @@ dependencies = [ "lazy_static", "libsecp256k1 0.6.0", "log", + "memoffset", "num-derive", "num-traits", "parking_lot", diff --git a/programs/bpf/rust/sanity/src/lib.rs b/programs/bpf/rust/sanity/src/lib.rs index 57df1ff5b..fc0b57140 100644 --- a/programs/bpf/rust/sanity/src/lib.rs +++ b/programs/bpf/rust/sanity/src/lib.rs @@ -4,7 +4,8 @@ extern crate solana_program; use solana_program::{ - account_info::AccountInfo, bpf_loader, entrypoint::ProgramResult, log::*, msg, pubkey::Pubkey, + account_info::AccountInfo, bpf_loader, entrypoint::ProgramResult, log::*, msg, + program::check_type_assumptions, pubkey::Pubkey, }; #[derive(Debug, PartialEq)] @@ -70,6 +71,8 @@ pub fn process_instruction( assert!(1.9986f64 < num && num < 2.0f64); } + check_type_assumptions(); + sol_log_compute_units(); Ok(()) } diff --git a/programs/bpf/tests/programs.rs b/programs/bpf/tests/programs.rs index fc9a8e842..d0fc4ec2a 100644 --- a/programs/bpf/tests/programs.rs +++ b/programs/bpf/tests/programs.rs @@ -1428,7 +1428,7 @@ fn assert_instruction_count() { ("sanity", 2378), ("sanity++", 2278), ("secp256k1_recover", 25383), - ("sha", 1895), + ("sha", 1355), ("struct_pass", 108), ("struct_ret", 122), ]); @@ -1448,9 +1448,9 @@ fn assert_instruction_count() { ("solana_bpf_rust_noop", 315), ("solana_bpf_rust_param_passing", 146), ("solana_bpf_rust_rand", 418), - ("solana_bpf_rust_sanity", 9128), + ("solana_bpf_rust_sanity", 52170), ("solana_bpf_rust_secp256k1_recover", 25707), - ("solana_bpf_rust_sha", 27033), + ("solana_bpf_rust_sha", 25338), ]); } diff --git a/programs/bpf_loader/src/syscalls.rs b/programs/bpf_loader/src/syscalls.rs index f45ea9eb1..deb6cc426 100644 --- a/programs/bpf_loader/src/syscalls.rs +++ b/programs/bpf_loader/src/syscalls.rs @@ -3541,6 +3541,7 @@ mod tests { bpf_loader, fee_calculator::FeeCalculator, hash::hashv, + program::check_type_assumptions, sysvar::{clock::Clock, epoch_schedule::EpochSchedule, rent::Rent}, transaction_context::TransactionContext, }, @@ -4906,9 +4907,6 @@ mod tests { #[test] fn test_check_type_assumptions() { - // Code in this file assumes that u64 and usize are the same - assert_eq!(size_of::(), size_of::()); - // Code in this file assumes that u8 is byte aligned - assert_eq!(1, align_of::()); + check_type_assumptions(); } } diff --git a/sdk/program/Cargo.toml b/sdk/program/Cargo.toml index 6611e5d76..5e467f875 100644 --- a/sdk/program/Cargo.toml +++ b/sdk/program/Cargo.toml @@ -21,6 +21,7 @@ bv = { version = "0.11.1", features = ["serde"] } itertools = "0.10.1" lazy_static = "1.4.0" log = "0.4.14" +memoffset = "0.6" num-derive = "0.3" num-traits = "0.2" rustversion = "1.0.3" diff --git a/sdk/program/src/program.rs b/sdk/program/src/program.rs index f07099a0f..7b744b523 100644 --- a/sdk/program/src/program.rs +++ b/sdk/program/src/program.rs @@ -138,3 +138,165 @@ pub fn get_return_data() -> Option<(Pubkey, Vec)> { #[cfg(not(target_arch = "bpf"))] crate::program_stubs::sol_get_return_data() } + +#[allow(clippy::integer_arithmetic)] +pub fn check_type_assumptions() { + extern crate memoffset; + use { + crate::{clock::Epoch, instruction::AccountMeta}, + memoffset::offset_of, + std::{ + cell::RefCell, + mem::{align_of, size_of}, + rc::Rc, + str::FromStr, + }, + }; + + // Code in this file assumes that u64 and usize are the same + assert_eq!(size_of::(), size_of::()); + // Code in this file assumes that u8 is byte aligned + assert_eq!(1, align_of::()); + + // Enforce Instruction layout + { + assert_eq!(size_of::(), 32 + 1 + 1); + + let pubkey1 = Pubkey::from_str("J9PYCcoKusHyKRMXnBL17VTXC3MVETyqBG2KyLXVv6Ai").unwrap(); + let pubkey2 = Pubkey::from_str("Hvy4GHgPToZNoENTKjC4mJqpzWWjgTwXrFufKfxYiKkV").unwrap(); + let pubkey3 = Pubkey::from_str("JDMyRL8rCkae7maCSv47upNuBMFd3Mgos1fz2AvYzVzY").unwrap(); + let account_meta1 = AccountMeta { + pubkey: pubkey2, + is_signer: true, + is_writable: false, + }; + let account_meta2 = AccountMeta { + pubkey: pubkey3, + is_signer: false, + is_writable: true, + }; + let data = vec![1, 2, 3, 4, 5]; + let instruction = Instruction { + program_id: pubkey1, + accounts: vec![account_meta1.clone(), account_meta2.clone()], + data: data.clone(), + }; + let instruction_addr = &instruction as *const _ as u64; + + // program id + assert_eq!(offset_of!(Instruction, program_id), 48); + let pubkey_ptr = (instruction_addr + 48) as *const Pubkey; + unsafe { + assert_eq!(*pubkey_ptr, pubkey1); + } + + // accounts + assert_eq!(offset_of!(Instruction, accounts), 0); + let accounts_ptr = (instruction_addr) as *const *const AccountMeta; + let accounts_cap = (instruction_addr + 8) as *const usize; + let accounts_len = (instruction_addr + 16) as *const usize; + unsafe { + assert_eq!(*accounts_cap, 2); + assert_eq!(*accounts_len, 2); + let account_meta_ptr = *accounts_ptr; + assert_eq!(*account_meta_ptr, account_meta1); + assert_eq!(*(account_meta_ptr.offset(1)), account_meta2); + } + + // data + assert_eq!(offset_of!(Instruction, data), 24); + let data_ptr = (instruction_addr + 24) as *const *const [u8; 5]; + let data_cap = (instruction_addr + 24 + 8) as *const usize; + let data_len = (instruction_addr + 24 + 16) as *const usize; + unsafe { + assert_eq!(*data_cap, 5); + + assert_eq!(*data_len, 5); + let u8_ptr = *data_ptr; + assert_eq!(*u8_ptr, data[..]); + } + } + + // Enforce AccountInfo layout + { + let key = Pubkey::from_str("6o8R9NsUxNskF1MfWM1f265y4w86JYbEwqCmTacdLkHp").unwrap(); + let mut lamports = 31; + let mut data = vec![1, 2, 3, 4, 5]; + let owner = Pubkey::from_str("2tjK4XyNU54XdN9jokx46QzLybbLVGwQQvTfhcuBXAjR").unwrap(); + let account_info = AccountInfo { + key: &key, + is_signer: true, + is_writable: false, + lamports: Rc::new(RefCell::new(&mut lamports)), + data: Rc::new(RefCell::new(&mut data)), + owner: &owner, + executable: true, + rent_epoch: 42, + }; + let account_info_addr = &account_info as *const _ as u64; + + // key + assert_eq!(offset_of!(AccountInfo, key), 0); + let key_ptr = (account_info_addr) as *const &Pubkey; + unsafe { + assert_eq!(**key_ptr, key); + } + + // is_signer + assert_eq!(offset_of!(AccountInfo, is_signer), 40); + let is_signer_ptr = (account_info_addr + 40) as *const bool; + unsafe { + assert!(*is_signer_ptr); + } + + // is_writable + assert_eq!(offset_of!(AccountInfo, is_writable), 41); + let is_writable_ptr = (account_info_addr + 41) as *const bool; + unsafe { + assert!(!*is_writable_ptr); + } + + // lamports + assert_eq!(offset_of!(AccountInfo, lamports), 8); + let lamports_ptr = (account_info_addr + 8) as *const Rc>; + unsafe { + assert_eq!(**(*lamports_ptr).as_ptr(), 31); + } + + // data + assert_eq!(offset_of!(AccountInfo, data), 16); + let data_ptr = (account_info_addr + 16) as *const Rc>; + unsafe { + assert_eq!((*(*data_ptr).as_ptr())[..], data[..]); + } + + // owner + assert_eq!(offset_of!(AccountInfo, owner), 24); + let owner_ptr = (account_info_addr + 24) as *const &Pubkey; + unsafe { + assert_eq!(**owner_ptr, owner); + } + + // executable + assert_eq!(offset_of!(AccountInfo, executable), 42); + let executable_ptr = (account_info_addr + 42) as *const bool; + unsafe { + assert!(*executable_ptr); + } + + // rent_epoch + assert_eq!(offset_of!(AccountInfo, rent_epoch), 32); + let renbt_epoch_ptr = (account_info_addr + 32) as *const Epoch; + unsafe { + assert_eq!(*renbt_epoch_ptr, 42); + } + } +} + +#[cfg(test)] +mod tests { + #[test] + fn test_check_type_assumptions() { + super::check_type_assumptions() + } +}