diff --git a/sdk/macro/src/lib.rs b/sdk/macro/src/lib.rs index c9cff97aa..29716b9ce 100644 --- a/sdk/macro/src/lib.rs +++ b/sdk/macro/src/lib.rs @@ -405,3 +405,38 @@ pub fn wasm_bindgen_stub(_attr: TokenStream, item: TokenStream) -> TokenStream { } .into() } + +// Sets padding in structures to zero explicitly. +// Otherwise padding could be inconsistent across the network and lead to divergence / consensus failures. +#[proc_macro_derive(CloneZeroed)] +pub fn derive_clone_zeroed(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + match parse_macro_input!(input as syn::Item) { + syn::Item::Struct(item_struct) => { + let clone_statements = match item_struct.fields { + syn::Fields::Named(ref fields) => fields.named.iter().map(|f| { + let name = &f.ident; + quote! { + std::ptr::addr_of_mut!((*ptr).#name).write(self.#name); + } + }), + _ => unimplemented!(), + }; + let name = &item_struct.ident; + quote! { + impl Clone for #name { + fn clone(&self) -> Self { + let mut value = std::mem::MaybeUninit::::uninit(); + unsafe { + std::ptr::write_bytes(&mut value, 0, 1); + let ptr = value.as_mut_ptr(); + #(#clone_statements)* + value.assume_init() + } + } + } + } + } + _ => unimplemented!(), + } + .into() +} diff --git a/sdk/program/src/clock.rs b/sdk/program/src/clock.rs index a28a73510..52431796f 100644 --- a/sdk/program/src/clock.rs +++ b/sdk/program/src/clock.rs @@ -20,10 +20,7 @@ //! //! [oracle]: https://docs.solana.com/implemented-proposals/validator-timestamp-oracle -use { - crate::{clone_zeroed, copy_field}, - std::mem::MaybeUninit, -}; +use solana_sdk_macro::CloneZeroed; /// The default tick rate that the cluster attempts to achieve (160 per second). /// @@ -147,7 +144,7 @@ pub type UnixTimestamp = i64; /// /// All members of `Clock` start from 0 upon network boot. #[repr(C)] -#[derive(Serialize, Deserialize, Debug, Default, PartialEq, Eq)] +#[derive(Serialize, Deserialize, Debug, CloneZeroed, Default, PartialEq, Eq)] pub struct Clock { /// The current `Slot`. pub slot: Slot, @@ -170,21 +167,6 @@ pub struct Clock { pub unix_timestamp: UnixTimestamp, } -impl Clone for Clock { - fn clone(&self) -> Self { - clone_zeroed(|cloned: &mut MaybeUninit| { - let ptr = cloned.as_mut_ptr(); - unsafe { - copy_field!(ptr, self, slot); - copy_field!(ptr, self, epoch_start_timestamp); - copy_field!(ptr, self, epoch); - copy_field!(ptr, self, leader_schedule_epoch); - copy_field!(ptr, self, unix_timestamp); - } - }) - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/sdk/program/src/epoch_schedule.rs b/sdk/program/src/epoch_schedule.rs index 984efb1e0..b122f00ed 100644 --- a/sdk/program/src/epoch_schedule.rs +++ b/sdk/program/src/epoch_schedule.rs @@ -12,10 +12,7 @@ //! epochs increasing in slots until they last for [`DEFAULT_SLOTS_PER_EPOCH`]. pub use crate::clock::{Epoch, Slot, DEFAULT_SLOTS_PER_EPOCH}; -use { - crate::{clone_zeroed, copy_field}, - std::mem::MaybeUninit, -}; +use solana_sdk_macro::CloneZeroed; /// The default number of slots before an epoch starts to calculate the leader schedule. pub const DEFAULT_LEADER_SCHEDULE_SLOT_OFFSET: u64 = DEFAULT_SLOTS_PER_EPOCH; @@ -32,7 +29,7 @@ pub const MAX_LEADER_SCHEDULE_EPOCH_OFFSET: u64 = 3; pub const MINIMUM_SLOTS_PER_EPOCH: u64 = 32; #[repr(C)] -#[derive(Debug, Copy, PartialEq, Eq, Deserialize, Serialize, AbiExample)] +#[derive(Debug, CloneZeroed, Copy, PartialEq, Eq, Deserialize, Serialize, AbiExample)] #[serde(rename_all = "camelCase")] pub struct EpochSchedule { /// The maximum number of slots in each epoch. @@ -66,21 +63,6 @@ impl Default for EpochSchedule { } } -impl Clone for EpochSchedule { - fn clone(&self) -> Self { - clone_zeroed(|cloned: &mut MaybeUninit| { - let ptr = cloned.as_mut_ptr(); - unsafe { - copy_field!(ptr, self, slots_per_epoch); - copy_field!(ptr, self, leader_schedule_slot_offset); - copy_field!(ptr, self, warmup); - copy_field!(ptr, self, first_normal_epoch); - copy_field!(ptr, self, first_normal_slot); - } - }) - } -} - impl EpochSchedule { pub fn new(slots_per_epoch: u64) -> Self { Self::custom(slots_per_epoch, slots_per_epoch, true) diff --git a/sdk/program/src/lib.rs b/sdk/program/src/lib.rs index 6adbdcea8..b1f741434 100644 --- a/sdk/program/src/lib.rs +++ b/sdk/program/src/lib.rs @@ -742,25 +742,6 @@ macro_rules! unchecked_div_by_const { }}; } -use std::{mem::MaybeUninit, ptr::write_bytes}; - -#[macro_export] -macro_rules! copy_field { - ($ptr:expr, $self:ident, $field:ident) => { - std::ptr::addr_of_mut!((*$ptr).$field).write($self.$field) - }; -} - -pub fn clone_zeroed(clone: F) -> T -where - F: Fn(&mut MaybeUninit), -{ - let mut value = MaybeUninit::::uninit(); - unsafe { write_bytes(&mut value, 0, 1) } - clone(&mut value); - unsafe { value.assume_init() } -} - // This module is purposefully listed after all other exports: because of an // interaction within rustdoc between the reexports inside this module of // `solana_program`'s top-level modules, and `solana_sdk`'s glob re-export of diff --git a/sdk/program/src/rent.rs b/sdk/program/src/rent.rs index f5d95ec8d..6e1aec3b8 100644 --- a/sdk/program/src/rent.rs +++ b/sdk/program/src/rent.rs @@ -4,14 +4,11 @@ #![allow(clippy::integer_arithmetic)] -use { - crate::{clock::DEFAULT_SLOTS_PER_EPOCH, clone_zeroed, copy_field}, - std::mem::MaybeUninit, -}; +use {crate::clock::DEFAULT_SLOTS_PER_EPOCH, solana_sdk_macro::CloneZeroed}; /// Configuration of network rent. #[repr(C)] -#[derive(Serialize, Deserialize, PartialEq, Copy, Debug, AbiExample)] +#[derive(Serialize, Deserialize, PartialEq, CloneZeroed, Copy, Debug, AbiExample)] pub struct Rent { /// Rental rate in lamports/byte-year. pub lamports_per_byte_year: u64, @@ -62,19 +59,6 @@ impl Default for Rent { } } -impl Clone for Rent { - fn clone(&self) -> Self { - clone_zeroed(|cloned: &mut MaybeUninit| { - let ptr = cloned.as_mut_ptr(); - unsafe { - copy_field!(ptr, self, lamports_per_byte_year); - copy_field!(ptr, self, exemption_threshold); - copy_field!(ptr, self, burn_percent); - } - }) - } -} - impl Rent { /// Calculate how much rent to burn from the collected rent. /// diff --git a/sdk/program/src/sysvar/fees.rs b/sdk/program/src/sysvar/fees.rs index f80d8c691..82bc145ef 100644 --- a/sdk/program/src/sysvar/fees.rs +++ b/sdk/program/src/sysvar/fees.rs @@ -22,10 +22,9 @@ use { crate::{ - clone_zeroed, copy_field, fee_calculator::FeeCalculator, impl_sysvar_get, - program_error::ProgramError, sysvar::Sysvar, + fee_calculator::FeeCalculator, impl_sysvar_get, program_error::ProgramError, sysvar::Sysvar, }, - std::mem::MaybeUninit, + solana_sdk_macro::CloneZeroed, }; crate::declare_deprecated_sysvar_id!("SysvarFees111111111111111111111111111111111", Fees); @@ -36,22 +35,11 @@ crate::declare_deprecated_sysvar_id!("SysvarFees11111111111111111111111111111111 note = "Please do not use, will no longer be available in the future" )] #[repr(C)] -#[derive(Serialize, Deserialize, Debug, Default, PartialEq, Eq)] +#[derive(Serialize, Deserialize, Debug, CloneZeroed, Default, PartialEq, Eq)] pub struct Fees { pub fee_calculator: FeeCalculator, } -impl Clone for Fees { - fn clone(&self) -> Self { - clone_zeroed(|cloned: &mut MaybeUninit| { - let ptr = cloned.as_mut_ptr(); - unsafe { - copy_field!(ptr, self, fee_calculator); - } - }) - } -} - impl Fees { pub fn new(fee_calculator: &FeeCalculator) -> Self { #[allow(deprecated)] diff --git a/sdk/src/lib.rs b/sdk/src/lib.rs index 624d5c999..0a52d27f9 100644 --- a/sdk/src/lib.rs +++ b/sdk/src/lib.rs @@ -13,11 +13,11 @@ pub use signer::signers; pub use solana_program::program_stubs; pub use solana_program::{ account_info, address_lookup_table_account, blake3, borsh, bpf_loader, bpf_loader_deprecated, - bpf_loader_upgradeable, clock, clone_zeroed, config, copy_field, custom_heap_default, - custom_panic_default, debug_account_data, declare_deprecated_sysvar_id, declare_sysvar_id, - decode_error, ed25519_program, epoch_schedule, fee_calculator, impl_sysvar_get, incinerator, - instruction, keccak, lamports, loader_instruction, loader_upgradeable_instruction, message, - msg, native_token, nonce, program, program_error, program_memory, program_option, program_pack, + bpf_loader_upgradeable, clock, config, custom_heap_default, custom_panic_default, + debug_account_data, declare_deprecated_sysvar_id, declare_sysvar_id, decode_error, + ed25519_program, epoch_schedule, fee_calculator, impl_sysvar_get, incinerator, instruction, + keccak, lamports, loader_instruction, loader_upgradeable_instruction, message, msg, + native_token, nonce, program, program_error, program_memory, program_option, program_pack, rent, sanitize, sdk_ids, secp256k1_program, secp256k1_recover, serialize_utils, short_vec, slot_hashes, slot_history, stake, stake_history, syscalls, system_instruction, system_program, sysvar, unchecked_div_by_const, vote, wasm_bindgen,