solana_runtime/
append_vec.rs

1//! Persistent storage for accounts.
2//!
3//! For more information, see:
4//!
5//! <https://docs.solana.com/implemented-proposals/persistent-account-storage>
6
7use {
8    crate::storable_accounts::StorableAccounts,
9    log::*,
10    memmap2::MmapMut,
11    serde::{Deserialize, Serialize},
12    solana_sdk::{
13        account::{Account, AccountSharedData, ReadableAccount},
14        clock::{Epoch, Slot},
15        hash::Hash,
16        pubkey::Pubkey,
17    },
18    std::{
19        borrow::Borrow,
20        convert::TryFrom,
21        fs::{remove_file, OpenOptions},
22        io::{self, Seek, SeekFrom, Write},
23        marker::PhantomData,
24        mem,
25        path::{Path, PathBuf},
26        sync::{
27            atomic::{AtomicU64, AtomicUsize, Ordering},
28            Mutex,
29        },
30    },
31    thiserror::Error,
32};
33
34pub mod test_utils;
35
36// Data placement should be aligned at the next boundary. Without alignment accessing the memory may
37// crash on some architectures.
38pub const ALIGN_BOUNDARY_OFFSET: usize = mem::size_of::<u64>();
39macro_rules! u64_align {
40    ($addr: expr) => {
41        ($addr + (ALIGN_BOUNDARY_OFFSET - 1)) & !(ALIGN_BOUNDARY_OFFSET - 1)
42    };
43}
44
45/// size of the fixed sized fields in an append vec
46/// we need to add data len and align it to get the actual stored size
47pub const STORE_META_OVERHEAD: usize = 136;
48
49/// Returns the size this item will take to store plus possible alignment padding bytes before the next entry.
50/// fixed-size portion of per-account data written
51/// plus 'data_len', aligned to next boundary
52pub fn aligned_stored_size(data_len: usize) -> usize {
53    u64_align!(STORE_META_OVERHEAD + data_len)
54}
55
56pub const MAXIMUM_APPEND_VEC_FILE_SIZE: u64 = 16 * 1024 * 1024 * 1024; // 16 GiB
57
58pub type StoredMetaWriteVersion = u64;
59
60/// Goal is to eliminate copies and data reshaping given various code paths that store accounts.
61/// This struct contains what is needed to store accounts to a storage
62/// 1. account & pubkey (StorableAccounts)
63/// 2. hash per account (Maybe in StorableAccounts, otherwise has to be passed in separately)
64/// 3. write version per account (Maybe in StorableAccounts, otherwise has to be passed in separately)
65pub struct StorableAccountsWithHashesAndWriteVersions<
66    'a: 'b,
67    'b,
68    T: ReadableAccount + Sync + 'b,
69    U: StorableAccounts<'a, T>,
70    V: Borrow<Hash>,
71> {
72    /// accounts to store
73    /// always has pubkey and account
74    /// may also have hash and write_version per account
75    accounts: &'b U,
76    /// if accounts does not have hash and write version, this has a hash and write version per account
77    hashes_and_write_versions: Option<(Vec<V>, Vec<StoredMetaWriteVersion>)>,
78    _phantom: PhantomData<&'a T>,
79}
80
81impl<'a: 'b, 'b, T: ReadableAccount + Sync + 'b, U: StorableAccounts<'a, T>, V: Borrow<Hash>>
82    StorableAccountsWithHashesAndWriteVersions<'a, 'b, T, U, V>
83{
84    /// used when accounts contains hash and write version already
85    pub fn new(accounts: &'b U) -> Self {
86        assert!(accounts.has_hash_and_write_version());
87        Self {
88            accounts,
89            hashes_and_write_versions: None,
90            _phantom: PhantomData,
91        }
92    }
93    /// used when accounts does NOT contains hash or write version
94    /// In this case, hashes and write_versions have to be passed in separately and zipped together.
95    pub fn new_with_hashes_and_write_versions(
96        accounts: &'b U,
97        hashes: Vec<V>,
98        write_versions: Vec<StoredMetaWriteVersion>,
99    ) -> Self {
100        assert!(!accounts.has_hash_and_write_version());
101        assert_eq!(accounts.len(), hashes.len());
102        assert_eq!(write_versions.len(), hashes.len());
103        Self {
104            accounts,
105            hashes_and_write_versions: Some((hashes, write_versions)),
106            _phantom: PhantomData,
107        }
108    }
109
110    /// get all account fields at 'index'
111    pub fn get(&self, index: usize) -> (Option<&T>, &Pubkey, &Hash, StoredMetaWriteVersion) {
112        let account = self.accounts.account_default_if_zero_lamport(index);
113        let pubkey = self.accounts.pubkey(index);
114        let (hash, write_version) = if self.accounts.has_hash_and_write_version() {
115            (
116                self.accounts.hash(index),
117                self.accounts.write_version(index),
118            )
119        } else {
120            let item = self.hashes_and_write_versions.as_ref().unwrap();
121            (item.0[index].borrow(), item.1[index])
122        };
123        (account, pubkey, hash, write_version)
124    }
125
126    /// None if account at index has lamports == 0
127    /// Otherwise, Some(account)
128    /// This is the only way to access the account.
129    pub fn account(&self, index: usize) -> Option<&T> {
130        self.accounts.account_default_if_zero_lamport(index)
131    }
132
133    /// # accounts to write
134    pub fn len(&self) -> usize {
135        self.accounts.len()
136    }
137
138    pub fn is_empty(&self) -> bool {
139        self.len() == 0
140    }
141}
142
143/// Meta contains enough context to recover the index from storage itself
144/// This struct will be backed by mmaped and snapshotted data files.
145/// So the data layout must be stable and consistent across the entire cluster!
146#[derive(Clone, PartialEq, Eq, Debug)]
147#[repr(C)]
148pub struct StoredMeta {
149    /// global write version
150    /// This will be made completely obsolete such that we stop storing it.
151    /// We will not support multiple append vecs per slot anymore, so this concept is no longer necessary.
152    /// Order of stores of an account to an append vec will determine 'latest' account data per pubkey.
153    pub write_version_obsolete: StoredMetaWriteVersion,
154    pub data_len: u64,
155    /// key for the account
156    pub pubkey: Pubkey,
157}
158
159/// This struct will be backed by mmaped and snapshotted data files.
160/// So the data layout must be stable and consistent across the entire cluster!
161#[derive(Clone, Debug, Default, Eq, PartialEq)]
162#[repr(C)]
163pub struct AccountMeta {
164    /// lamports in the account
165    pub lamports: u64,
166    /// the epoch at which this account will next owe rent
167    pub rent_epoch: Epoch,
168    /// the program that owns this account. If executable, the program that loads this account.
169    pub owner: Pubkey,
170    /// this account's data contains a loaded program (and is now read-only)
171    pub executable: bool,
172}
173
174impl<'a, T: ReadableAccount> From<&'a T> for AccountMeta {
175    fn from(account: &'a T) -> Self {
176        Self {
177            lamports: account.lamports(),
178            owner: *account.owner(),
179            executable: account.executable(),
180            rent_epoch: account.rent_epoch(),
181        }
182    }
183}
184
185impl<'a, T: ReadableAccount> From<Option<&'a T>> for AccountMeta {
186    fn from(account: Option<&'a T>) -> Self {
187        match account {
188            Some(account) => AccountMeta::from(account),
189            None => AccountMeta::default(),
190        }
191    }
192}
193pub struct StoredAccountMetaClone<'a> {
194    pub meta: StoredMeta,
195    /// account data
196    pub account_meta: AccountMeta,
197    pub data: &'a [u8],
198    pub offset: usize,
199    pub stored_size: usize,
200    pub hash: Hash,
201}
202/// References to account data stored elsewhere. Getting an `Account` requires cloning
203/// (see `StoredAccountMeta::clone_account()`).
204#[derive(PartialEq, Eq, Debug)]
205pub struct StoredAccountMeta<'a> {
206    pub meta: &'a StoredMeta,
207    /// account data
208    pub account_meta: &'a AccountMeta,
209    pub data: &'a [u8],
210    pub offset: usize,
211    pub stored_size: usize,
212    pub hash: &'a Hash,
213}
214
215impl<'a> StoredAccountMeta<'a> {
216    /// Return a new Account by copying all the data referenced by the `StoredAccountMeta`.
217    pub fn clone_account(&self) -> AccountSharedData {
218        AccountSharedData::from(Account {
219            lamports: self.account_meta.lamports,
220            owner: self.account_meta.owner,
221            executable: self.account_meta.executable,
222            rent_epoch: self.account_meta.rent_epoch,
223            data: self.data.to_vec(),
224        })
225    }
226
227    pub fn pubkey(&self) -> &Pubkey {
228        &self.meta.pubkey
229    }
230
231    fn sanitize(&self) -> bool {
232        self.sanitize_executable() && self.sanitize_lamports()
233    }
234
235    fn sanitize_executable(&self) -> bool {
236        // Sanitize executable to ensure higher 7-bits are cleared correctly.
237        self.ref_executable_byte() & !1 == 0
238    }
239
240    fn sanitize_lamports(&self) -> bool {
241        // Sanitize 0 lamports to ensure to be same as AccountSharedData::default()
242        self.account_meta.lamports != 0 || self.clone_account() == AccountSharedData::default()
243    }
244
245    fn ref_executable_byte(&self) -> &u8 {
246        // Use extra references to avoid value silently clamped to 1 (=true) and 0 (=false)
247        // Yes, this really happens; see test_new_from_file_crafted_executable
248        let executable_bool: &bool = &self.account_meta.executable;
249        // UNSAFE: Force to interpret mmap-backed bool as u8 to really read the actual memory content
250        let executable_byte: &u8 = unsafe { &*(executable_bool as *const bool as *const u8) };
251        executable_byte
252    }
253}
254
255pub struct AppendVecAccountsIter<'a> {
256    append_vec: &'a AppendVec,
257    offset: usize,
258}
259
260impl<'a> AppendVecAccountsIter<'a> {
261    pub fn new(append_vec: &'a AppendVec) -> Self {
262        Self {
263            append_vec,
264            offset: 0,
265        }
266    }
267}
268
269impl<'a> Iterator for AppendVecAccountsIter<'a> {
270    type Item = StoredAccountMeta<'a>;
271
272    fn next(&mut self) -> Option<Self::Item> {
273        if let Some((account, next_offset)) = self.append_vec.get_account(self.offset) {
274            self.offset = next_offset;
275            Some(account)
276        } else {
277            None
278        }
279    }
280}
281
282#[derive(Error, Debug, PartialEq, Eq)]
283pub enum MatchAccountOwnerError {
284    #[error("The account owner does not match with the provided list")]
285    NoMatch,
286    #[error("Unable to load the account")]
287    UnableToLoad,
288}
289
290/// A thread-safe, file-backed block of memory used to store `Account` instances. Append operations
291/// are serialized such that only one thread updates the internal `append_lock` at a time. No
292/// restrictions are placed on reading. That is, one may read items from one thread while another
293/// is appending new items.
294#[derive(Debug, AbiExample)]
295pub struct AppendVec {
296    /// The file path where the data is stored.
297    path: PathBuf,
298
299    /// A file-backed block of memory that is used to store the data for each appended item.
300    map: MmapMut,
301
302    /// A lock used to serialize append operations.
303    append_lock: Mutex<()>,
304
305    /// The number of bytes used to store items, not the number of items.
306    current_len: AtomicUsize,
307
308    /// The number of bytes available for storing items.
309    file_size: u64,
310
311    /// True if the file should automatically be deleted when this AppendVec is dropped.
312    remove_on_drop: bool,
313}
314
315lazy_static! {
316    pub static ref APPEND_VEC_MMAPPED_FILES_OPEN: AtomicU64 = AtomicU64::default();
317}
318
319impl Drop for AppendVec {
320    fn drop(&mut self) {
321        if self.remove_on_drop {
322            APPEND_VEC_MMAPPED_FILES_OPEN.fetch_sub(1, Ordering::Relaxed);
323            if let Err(_e) = remove_file(&self.path) {
324                // promote this to panic soon.
325                // disabled due to many false positive warnings while running tests.
326                // blocked by rpc's upgrade to jsonrpc v17
327                //error!("AppendVec failed to remove {:?}: {:?}", &self.path, e);
328                inc_new_counter_info!("append_vec_drop_fail", 1);
329            }
330        }
331    }
332}
333
334impl AppendVec {
335    pub fn new(file: &Path, create: bool, size: usize) -> Self {
336        let initial_len = 0;
337        AppendVec::sanitize_len_and_size(initial_len, size).unwrap();
338
339        if create {
340            let _ignored = remove_file(file);
341        }
342
343        let mut data = OpenOptions::new()
344            .read(true)
345            .write(true)
346            .create(create)
347            .open(file)
348            .map_err(|e| {
349                panic!(
350                    "Unable to {} data file {} in current dir({:?}): {:?}",
351                    if create { "create" } else { "open" },
352                    file.display(),
353                    std::env::current_dir(),
354                    e
355                );
356            })
357            .unwrap();
358
359        // Theoretical performance optimization: write a zero to the end of
360        // the file so that we won't have to resize it later, which may be
361        // expensive.
362        data.seek(SeekFrom::Start((size - 1) as u64)).unwrap();
363        data.write_all(&[0]).unwrap();
364        data.rewind().unwrap();
365        data.flush().unwrap();
366
367        //UNSAFE: Required to create a Mmap
368        let map = unsafe { MmapMut::map_mut(&data) };
369        let map = map.unwrap_or_else(|e| {
370            error!(
371                "Failed to map the data file (size: {}): {}.\n
372                    Please increase sysctl vm.max_map_count or equivalent for your platform.",
373                size, e
374            );
375            std::process::exit(1);
376        });
377        APPEND_VEC_MMAPPED_FILES_OPEN.fetch_add(1, Ordering::Relaxed);
378
379        AppendVec {
380            path: file.to_path_buf(),
381            map,
382            // This mutex forces append to be single threaded, but concurrent with reads
383            // See UNSAFE usage in `append_ptr`
384            append_lock: Mutex::new(()),
385            current_len: AtomicUsize::new(initial_len),
386            file_size: size as u64,
387            remove_on_drop: true,
388        }
389    }
390
391    pub fn set_no_remove_on_drop(&mut self) {
392        self.remove_on_drop = false;
393    }
394
395    fn sanitize_len_and_size(current_len: usize, file_size: usize) -> io::Result<()> {
396        if file_size == 0 {
397            Err(std::io::Error::new(
398                std::io::ErrorKind::Other,
399                format!("too small file size {file_size} for AppendVec"),
400            ))
401        } else if usize::try_from(MAXIMUM_APPEND_VEC_FILE_SIZE)
402            .map(|max| file_size > max)
403            .unwrap_or(true)
404        {
405            Err(std::io::Error::new(
406                std::io::ErrorKind::Other,
407                format!("too large file size {file_size} for AppendVec"),
408            ))
409        } else if current_len > file_size {
410            Err(std::io::Error::new(
411                std::io::ErrorKind::Other,
412                format!("current_len is larger than file size ({file_size})"),
413            ))
414        } else {
415            Ok(())
416        }
417    }
418
419    pub fn flush(&self) -> io::Result<()> {
420        self.map.flush()
421    }
422
423    pub fn reset(&self) {
424        // This mutex forces append to be single threaded, but concurrent with reads
425        // See UNSAFE usage in `append_ptr`
426        let _lock = self.append_lock.lock().unwrap();
427        self.current_len.store(0, Ordering::Release);
428    }
429
430    /// how many more bytes can be stored in this append vec
431    pub fn remaining_bytes(&self) -> u64 {
432        (self.capacity()).saturating_sub(self.len() as u64)
433    }
434
435    pub fn len(&self) -> usize {
436        self.current_len.load(Ordering::Acquire)
437    }
438
439    pub fn is_empty(&self) -> bool {
440        self.len() == 0
441    }
442
443    pub fn capacity(&self) -> u64 {
444        self.file_size
445    }
446
447    pub fn file_name(slot: Slot, id: impl std::fmt::Display) -> String {
448        format!("{slot}.{id}")
449    }
450
451    pub fn new_from_file<P: AsRef<Path>>(path: P, current_len: usize) -> io::Result<(Self, usize)> {
452        let new = Self::new_from_file_unchecked(&path, current_len)?;
453
454        let (sanitized, num_accounts) = new.sanitize_layout_and_length();
455        if !sanitized {
456            // This info show the failing accountvec file path.  It helps debugging
457            // the appendvec data corrupution issues related to recycling.
458            let err_msg = format!(
459                "incorrect layout/length/data in the appendvec at path {}",
460                path.as_ref().display()
461            );
462            return Err(std::io::Error::new(std::io::ErrorKind::Other, err_msg));
463        }
464
465        Ok((new, num_accounts))
466    }
467
468    /// Creates an appendvec from file without performing sanitize checks or counting the number of accounts
469    pub fn new_from_file_unchecked<P: AsRef<Path>>(
470        path: P,
471        current_len: usize,
472    ) -> io::Result<Self> {
473        let file_size = std::fs::metadata(&path)?.len();
474        Self::sanitize_len_and_size(current_len, file_size as usize)?;
475
476        let data = OpenOptions::new()
477            .read(true)
478            .write(true)
479            .create(false)
480            .open(&path)?;
481
482        let map = unsafe {
483            let result = MmapMut::map_mut(&data);
484            if result.is_err() {
485                // for vm.max_map_count, error is: {code: 12, kind: Other, message: "Cannot allocate memory"}
486                info!("memory map error: {:?}. This may be because vm.max_map_count is not set correctly.", result);
487            }
488            result?
489        };
490        APPEND_VEC_MMAPPED_FILES_OPEN.fetch_add(1, Ordering::Relaxed);
491
492        Ok(AppendVec {
493            path: path.as_ref().to_path_buf(),
494            map,
495            append_lock: Mutex::new(()),
496            current_len: AtomicUsize::new(current_len),
497            file_size,
498            remove_on_drop: true,
499        })
500    }
501
502    fn sanitize_layout_and_length(&self) -> (bool, usize) {
503        let mut offset = 0;
504
505        // This discards allocated accounts immediately after check at each loop iteration.
506        //
507        // This code should not reuse AppendVec.accounts() method as the current form or
508        // extend it to be reused here because it would allow attackers to accumulate
509        // some measurable amount of memory needlessly.
510        let mut num_accounts = 0;
511        while let Some((account, next_offset)) = self.get_account(offset) {
512            if !account.sanitize() {
513                return (false, num_accounts);
514            }
515            offset = next_offset;
516            num_accounts += 1;
517        }
518        let aligned_current_len = u64_align!(self.current_len.load(Ordering::Acquire));
519
520        (offset == aligned_current_len, num_accounts)
521    }
522
523    /// Get a reference to the data at `offset` of `size` bytes if that slice
524    /// doesn't overrun the internal buffer. Otherwise return None.
525    /// Also return the offset of the first byte after the requested data that
526    /// falls on a 64-byte boundary.
527    fn get_slice(&self, offset: usize, size: usize) -> Option<(&[u8], usize)> {
528        let (next, overflow) = offset.overflowing_add(size);
529        if overflow || next > self.len() {
530            return None;
531        }
532        let data = &self.map[offset..next];
533        let next = u64_align!(next);
534
535        Some((
536            //UNSAFE: This unsafe creates a slice that represents a chunk of self.map memory
537            //The lifetime of this slice is tied to &self, since it points to self.map memory
538            unsafe { std::slice::from_raw_parts(data.as_ptr() as *const u8, size) },
539            next,
540        ))
541    }
542
543    /// Copy `len` bytes from `src` to the first 64-byte boundary after position `offset` of
544    /// the internal buffer. Then update `offset` to the first byte after the copied data.
545    fn append_ptr(&self, offset: &mut usize, src: *const u8, len: usize) {
546        let pos = u64_align!(*offset);
547        let data = &self.map[pos..(pos + len)];
548        //UNSAFE: This mut append is safe because only 1 thread can append at a time
549        //Mutex<()> guarantees exclusive write access to the memory occupied in
550        //the range.
551        unsafe {
552            let dst = data.as_ptr() as *mut u8;
553            std::ptr::copy(src, dst, len);
554        };
555        *offset = pos + len;
556    }
557
558    /// Copy each value in `vals`, in order, to the first 64-byte boundary after position `offset`.
559    /// If there is sufficient space, then update `offset` and the internal `current_len` to the
560    /// first byte after the copied data and return the starting position of the copied data.
561    /// Otherwise return None and leave `offset` unchanged.
562    fn append_ptrs_locked(&self, offset: &mut usize, vals: &[(*const u8, usize)]) -> Option<usize> {
563        let mut end = *offset;
564        for val in vals {
565            end = u64_align!(end);
566            end += val.1;
567        }
568
569        if (self.file_size as usize) < end {
570            return None;
571        }
572
573        let pos = u64_align!(*offset);
574        for val in vals {
575            self.append_ptr(offset, val.0, val.1)
576        }
577        self.current_len.store(*offset, Ordering::Release);
578        Some(pos)
579    }
580
581    /// Return a reference to the type at `offset` if its data doesn't overrun the internal buffer.
582    /// Otherwise return None. Also return the offset of the first byte after the requested data
583    /// that falls on a 64-byte boundary.
584    fn get_type<'a, T>(&self, offset: usize) -> Option<(&'a T, usize)> {
585        let (data, next) = self.get_slice(offset, mem::size_of::<T>())?;
586        let ptr: *const T = data.as_ptr() as *const T;
587        //UNSAFE: The cast is safe because the slice is aligned and fits into the memory
588        //and the lifetime of the &T is tied to self, which holds the underlying memory map
589        Some((unsafe { &*ptr }, next))
590    }
591
592    /// Return stored account metadata for the account at `offset` if its data doesn't overrun
593    /// the internal buffer. Otherwise return None. Also return the offset of the first byte
594    /// after the requested data that falls on a 64-byte boundary.
595    pub fn get_account<'a>(&'a self, offset: usize) -> Option<(StoredAccountMeta<'a>, usize)> {
596        let (meta, next): (&'a StoredMeta, _) = self.get_type(offset)?;
597        let (account_meta, next): (&'a AccountMeta, _) = self.get_type(next)?;
598        let (hash, next): (&'a Hash, _) = self.get_type(next)?;
599        let (data, next) = self.get_slice(next, meta.data_len as usize)?;
600        let stored_size = next - offset;
601        Some((
602            StoredAccountMeta {
603                meta,
604                account_meta,
605                data,
606                offset,
607                stored_size,
608                hash,
609            },
610            next,
611        ))
612    }
613
614    fn get_account_meta<'a>(&self, offset: usize) -> Option<&'a AccountMeta> {
615        // Skip over StoredMeta data in the account
616        let offset = offset.checked_add(mem::size_of::<StoredMeta>())?;
617        // u64_align! does an unchecked add for alignment. Check that it won't cause an overflow.
618        offset.checked_add(ALIGN_BOUNDARY_OFFSET - 1)?;
619        let (account_meta, _): (&AccountMeta, _) = self.get_type(u64_align!(offset))?;
620        Some(account_meta)
621    }
622
623    /// Return Ok(index_of_matching_owner) if the account owner at `offset` is one of the pubkeys in `owners`.
624    /// Return Err(MatchAccountOwnerError::NoMatch) if the account has 0 lamports or the owner is not one of
625    /// the pubkeys in `owners`.
626    /// Return Err(MatchAccountOwnerError::UnableToLoad) if the `offset` value causes a data overrun.
627    pub fn account_matches_owners(
628        &self,
629        offset: usize,
630        owners: &[&Pubkey],
631    ) -> Result<usize, MatchAccountOwnerError> {
632        let account_meta = self
633            .get_account_meta(offset)
634            .ok_or(MatchAccountOwnerError::UnableToLoad)?;
635        if account_meta.lamports == 0 {
636            Err(MatchAccountOwnerError::NoMatch)
637        } else {
638            owners
639                .iter()
640                .position(|entry| &&account_meta.owner == entry)
641                .ok_or(MatchAccountOwnerError::NoMatch)
642        }
643    }
644
645    #[cfg(test)]
646    pub fn get_account_test(&self, offset: usize) -> Option<(StoredMeta, AccountSharedData)> {
647        let (stored_account, _) = self.get_account(offset)?;
648        let meta = stored_account.meta.clone();
649        Some((meta, stored_account.clone_account()))
650    }
651
652    pub fn get_path(&self) -> PathBuf {
653        self.path.clone()
654    }
655
656    /// Return iterator for account metadata
657    pub fn account_iter(&self) -> AppendVecAccountsIter {
658        AppendVecAccountsIter::new(self)
659    }
660
661    /// Return a vector of account metadata for each account, starting from `offset`.
662    pub fn accounts(&self, mut offset: usize) -> Vec<StoredAccountMeta> {
663        let mut accounts = vec![];
664        while let Some((account, next)) = self.get_account(offset) {
665            accounts.push(account);
666            offset = next;
667        }
668        accounts
669    }
670
671    /// Copy each account metadata, account and hash to the internal buffer.
672    /// If there is no room to write the first entry, None is returned.
673    /// Otherwise, returns the starting offset of each account metadata.
674    /// Plus, the final return value is the offset where the next entry would be appended.
675    /// So, return.len() is 1 + (number of accounts written)
676    /// After each account is appended, the internal `current_len` is updated
677    /// and will be available to other threads.
678    pub fn append_accounts<
679        'a,
680        'b,
681        T: ReadableAccount + Sync,
682        U: StorableAccounts<'a, T>,
683        V: Borrow<Hash>,
684    >(
685        &self,
686        accounts: &StorableAccountsWithHashesAndWriteVersions<'a, 'b, T, U, V>,
687        skip: usize,
688    ) -> Option<Vec<usize>> {
689        let _lock = self.append_lock.lock().unwrap();
690        let mut offset = self.len();
691
692        let len = accounts.accounts.len();
693        let mut rv = Vec::with_capacity(len);
694        for i in skip..len {
695            let (account, pubkey, hash, write_version_obsolete) = accounts.get(i);
696            let account_meta = account
697                .map(|account| AccountMeta {
698                    lamports: account.lamports(),
699                    owner: *account.owner(),
700                    rent_epoch: account.rent_epoch(),
701                    executable: account.executable(),
702                })
703                .unwrap_or_default();
704
705            let stored_meta = StoredMeta {
706                pubkey: *pubkey,
707                data_len: account
708                    .map(|account| account.data().len())
709                    .unwrap_or_default() as u64,
710                write_version_obsolete,
711            };
712            let meta_ptr = &stored_meta as *const StoredMeta;
713            let account_meta_ptr = &account_meta as *const AccountMeta;
714            let data_len = stored_meta.data_len as usize;
715            let data_ptr = account
716                .map(|account| account.data())
717                .unwrap_or_default()
718                .as_ptr();
719            let hash_ptr = hash.as_ref().as_ptr();
720            let ptrs = [
721                (meta_ptr as *const u8, mem::size_of::<StoredMeta>()),
722                (account_meta_ptr as *const u8, mem::size_of::<AccountMeta>()),
723                (hash_ptr as *const u8, mem::size_of::<Hash>()),
724                (data_ptr, data_len),
725            ];
726            if let Some(res) = self.append_ptrs_locked(&mut offset, &ptrs) {
727                rv.push(res)
728            } else {
729                break;
730            }
731        }
732
733        if rv.is_empty() {
734            None
735        } else {
736            // The last entry in this offset needs to be the u64 aligned offset, because that's
737            // where the *next* entry will begin to be stored.
738            rv.push(u64_align!(offset));
739
740            Some(rv)
741        }
742    }
743}
744
745#[cfg(test)]
746pub mod tests {
747    use {
748        super::{test_utils::*, *},
749        crate::accounts_db::INCLUDE_SLOT_IN_HASH_TESTS,
750        assert_matches::assert_matches,
751        memoffset::offset_of,
752        rand::{thread_rng, Rng},
753        solana_sdk::{
754            account::{accounts_equal, WritableAccount},
755            timing::duration_as_ms,
756        },
757        std::time::Instant,
758    };
759
760    impl AppendVec {
761        pub(crate) fn set_current_len_for_tests(&self, len: usize) {
762            self.current_len.store(len, Ordering::Release);
763        }
764
765        fn append_account_test(&self, data: &(StoredMeta, AccountSharedData)) -> Option<usize> {
766            let slot_ignored = Slot::MAX;
767            let accounts = [(&data.0.pubkey, &data.1)];
768            let slice = &accounts[..];
769            let account_data = (slot_ignored, slice);
770            let hash = Hash::default();
771            let storable_accounts =
772                StorableAccountsWithHashesAndWriteVersions::new_with_hashes_and_write_versions(
773                    &account_data,
774                    vec![&hash],
775                    vec![data.0.write_version_obsolete],
776                );
777
778            self.append_accounts(&storable_accounts, 0)
779                .map(|res| res[0])
780        }
781    }
782
783    impl<'a> StoredAccountMeta<'a> {
784        #[allow(clippy::cast_ref_to_mut)]
785        fn set_data_len_unsafe(&self, new_data_len: u64) {
786            // UNSAFE: cast away & (= const ref) to &mut to force to mutate append-only (=read-only) AppendVec
787            unsafe {
788                *(&self.meta.data_len as *const u64 as *mut u64) = new_data_len;
789            }
790        }
791
792        fn get_executable_byte(&self) -> u8 {
793            let executable_bool: bool = self.account_meta.executable;
794            // UNSAFE: Force to interpret mmap-backed bool as u8 to really read the actual memory content
795            let executable_byte: u8 = unsafe { std::mem::transmute::<bool, u8>(executable_bool) };
796            executable_byte
797        }
798
799        #[allow(clippy::cast_ref_to_mut)]
800        fn set_executable_as_byte(&self, new_executable_byte: u8) {
801            // UNSAFE: Force to interpret mmap-backed &bool as &u8 to write some crafted value;
802            unsafe {
803                *(&self.account_meta.executable as *const bool as *mut u8) = new_executable_byte;
804            }
805        }
806    }
807
808    static_assertions::const_assert_eq!(
809        STORE_META_OVERHEAD,
810        std::mem::size_of::<StoredMeta>()
811            + std::mem::size_of::<AccountMeta>()
812            + std::mem::size_of::<Hash>()
813    );
814
815    // Hash is [u8; 32], which has no alignment
816    static_assertions::assert_eq_align!(u64, StoredMeta, AccountMeta);
817
818    #[test]
819    #[should_panic(expected = "assertion failed: accounts.has_hash_and_write_version()")]
820    fn test_storable_accounts_with_hashes_and_write_versions_new() {
821        let account = AccountSharedData::default();
822        // for (Slot, &'a [(&'a Pubkey, &'a T)], IncludeSlotInHash)
823        let slot = 0 as Slot;
824        let pubkey = Pubkey::default();
825        StorableAccountsWithHashesAndWriteVersions::<'_, '_, _, _, &Hash>::new(&(
826            slot,
827            &[(&pubkey, &account)][..],
828            INCLUDE_SLOT_IN_HASH_TESTS,
829        ));
830    }
831
832    fn test_mismatch(correct_hashes: bool, correct_write_versions: bool) {
833        let account = AccountSharedData::default();
834        // for (Slot, &'a [(&'a Pubkey, &'a T)], IncludeSlotInHash)
835        let slot = 0 as Slot;
836        let pubkey = Pubkey::default();
837        // mismatch between lens of accounts, hashes, write_versions
838        let mut hashes = Vec::default();
839        if correct_hashes {
840            hashes.push(Hash::default());
841        }
842        let mut write_versions = Vec::default();
843        if correct_write_versions {
844            write_versions.push(0);
845        }
846        StorableAccountsWithHashesAndWriteVersions::new_with_hashes_and_write_versions(
847            &(slot, &[(&pubkey, &account)][..], INCLUDE_SLOT_IN_HASH_TESTS),
848            hashes,
849            write_versions,
850        );
851    }
852
853    #[test]
854    #[should_panic(expected = "assertion failed:")]
855    fn test_storable_accounts_with_hashes_and_write_versions_new2() {
856        test_mismatch(false, false);
857    }
858
859    #[test]
860    #[should_panic(expected = "assertion failed:")]
861    fn test_storable_accounts_with_hashes_and_write_versions_new3() {
862        test_mismatch(false, true);
863    }
864
865    #[test]
866    #[should_panic(expected = "assertion failed:")]
867    fn test_storable_accounts_with_hashes_and_write_versions_new4() {
868        test_mismatch(true, false);
869    }
870
871    #[test]
872    fn test_storable_accounts_with_hashes_and_write_versions_empty() {
873        // for (Slot, &'a [(&'a Pubkey, &'a T)], IncludeSlotInHash)
874        let account = AccountSharedData::default();
875        let slot = 0 as Slot;
876        let pubkeys = vec![Pubkey::default()];
877        let hashes = Vec::<Hash>::default();
878        let write_versions = Vec::default();
879        let mut accounts = vec![(&pubkeys[0], &account)];
880        accounts.clear();
881        let accounts2 = (slot, &accounts[..], INCLUDE_SLOT_IN_HASH_TESTS);
882        let storable =
883            StorableAccountsWithHashesAndWriteVersions::new_with_hashes_and_write_versions(
884                &accounts2,
885                hashes,
886                write_versions,
887            );
888        assert_eq!(storable.len(), 0);
889        assert!(storable.is_empty());
890    }
891
892    #[test]
893    fn test_storable_accounts_with_hashes_and_write_versions_hash_and_write_version() {
894        // for (Slot, &'a [(&'a Pubkey, &'a T)], IncludeSlotInHash)
895        let account = AccountSharedData::default();
896        let slot = 0 as Slot;
897        let pubkeys = vec![Pubkey::from([5; 32]), Pubkey::from([6; 32])];
898        let hashes = vec![Hash::new(&[3; 32]), Hash::new(&[4; 32])];
899        let write_versions = vec![42, 43];
900        let accounts = vec![(&pubkeys[0], &account), (&pubkeys[1], &account)];
901        let accounts2 = (slot, &accounts[..], INCLUDE_SLOT_IN_HASH_TESTS);
902        let storable =
903            StorableAccountsWithHashesAndWriteVersions::new_with_hashes_and_write_versions(
904                &accounts2,
905                hashes.clone(),
906                write_versions.clone(),
907            );
908        assert_eq!(storable.len(), pubkeys.len());
909        assert!(!storable.is_empty());
910        (0..2).for_each(|i| {
911            let (_, pubkey, hash, write_version) = storable.get(i);
912            assert_eq!(hash, &hashes[i]);
913            assert_eq!(write_version, write_versions[i]);
914            assert_eq!(pubkey, &pubkeys[i]);
915        });
916    }
917
918    #[test]
919    fn test_storable_accounts_with_hashes_and_write_versions_default() {
920        // 0 lamport account, should return default account (or None in this case)
921        let account = Account {
922            data: vec![0],
923            ..Account::default()
924        }
925        .to_account_shared_data();
926        // for (Slot, &'a [(&'a Pubkey, &'a T)], IncludeSlotInHash)
927        let slot = 0 as Slot;
928        let pubkey = Pubkey::default();
929        let hashes = vec![Hash::default()];
930        let write_versions = vec![0];
931        let accounts = vec![(&pubkey, &account)];
932        let accounts2 = (slot, &accounts[..], INCLUDE_SLOT_IN_HASH_TESTS);
933        let storable =
934            StorableAccountsWithHashesAndWriteVersions::new_with_hashes_and_write_versions(
935                &accounts2,
936                hashes.clone(),
937                write_versions.clone(),
938            );
939        let get_account = storable.account(0);
940        assert!(get_account.is_none());
941
942        // non-zero lamports, data should be correct
943        let account = Account {
944            lamports: 1,
945            data: vec![0],
946            ..Account::default()
947        }
948        .to_account_shared_data();
949        // for (Slot, &'a [(&'a Pubkey, &'a T)], IncludeSlotInHash)
950        let accounts = vec![(&pubkey, &account)];
951        let accounts2 = (slot, &accounts[..], INCLUDE_SLOT_IN_HASH_TESTS);
952        let storable =
953            StorableAccountsWithHashesAndWriteVersions::new_with_hashes_and_write_versions(
954                &accounts2,
955                hashes,
956                write_versions,
957            );
958        let get_account = storable.account(0);
959        assert!(accounts_equal(&account, get_account.unwrap()));
960    }
961
962    #[test]
963    fn test_account_meta_default() {
964        let def1 = AccountMeta::default();
965        let def2 = AccountMeta::from(&Account::default());
966        assert_eq!(&def1, &def2);
967        let def2 = AccountMeta::from(&AccountSharedData::default());
968        assert_eq!(&def1, &def2);
969        let def2 = AccountMeta::from(Some(&AccountSharedData::default()));
970        assert_eq!(&def1, &def2);
971        let none: Option<&AccountSharedData> = None;
972        let def2 = AccountMeta::from(none);
973        assert_eq!(&def1, &def2);
974    }
975
976    #[test]
977    fn test_account_meta_non_default() {
978        let def1 = AccountMeta {
979            lamports: 1,
980            owner: Pubkey::new_unique(),
981            executable: true,
982            rent_epoch: 3,
983        };
984        let def2_account = Account {
985            lamports: def1.lamports,
986            owner: def1.owner,
987            executable: def1.executable,
988            rent_epoch: def1.rent_epoch,
989            data: Vec::new(),
990        };
991        let def2 = AccountMeta::from(&def2_account);
992        assert_eq!(&def1, &def2);
993        let def2 = AccountMeta::from(&AccountSharedData::from(def2_account.clone()));
994        assert_eq!(&def1, &def2);
995        let def2 = AccountMeta::from(Some(&AccountSharedData::from(def2_account)));
996        assert_eq!(&def1, &def2);
997    }
998
999    #[test]
1000    #[should_panic(expected = "too small file size 0 for AppendVec")]
1001    fn test_append_vec_new_bad_size() {
1002        let path = get_append_vec_path("test_append_vec_new_bad_size");
1003        let _av = AppendVec::new(&path.path, true, 0);
1004    }
1005
1006    #[test]
1007    fn test_append_vec_new_from_file_bad_size() {
1008        let file = get_append_vec_path("test_append_vec_new_from_file_bad_size");
1009        let path = &file.path;
1010
1011        let _data = OpenOptions::new()
1012            .read(true)
1013            .write(true)
1014            .create(true)
1015            .open(path)
1016            .expect("create a test file for mmap");
1017
1018        let result = AppendVec::new_from_file(path, 0);
1019        assert_matches!(result, Err(ref message) if message.to_string() == *"too small file size 0 for AppendVec");
1020    }
1021
1022    #[test]
1023    fn test_append_vec_sanitize_len_and_size_too_small() {
1024        const LEN: usize = 0;
1025        const SIZE: usize = 0;
1026        let result = AppendVec::sanitize_len_and_size(LEN, SIZE);
1027        assert_matches!(result, Err(ref message) if message.to_string() == *"too small file size 0 for AppendVec");
1028    }
1029
1030    #[test]
1031    fn test_append_vec_sanitize_len_and_size_maximum() {
1032        const LEN: usize = 0;
1033        const SIZE: usize = 16 * 1024 * 1024 * 1024;
1034        let result = AppendVec::sanitize_len_and_size(LEN, SIZE);
1035        assert_matches!(result, Ok(_));
1036    }
1037
1038    #[test]
1039    fn test_append_vec_sanitize_len_and_size_too_large() {
1040        const LEN: usize = 0;
1041        const SIZE: usize = 16 * 1024 * 1024 * 1024 + 1;
1042        let result = AppendVec::sanitize_len_and_size(LEN, SIZE);
1043        assert_matches!(result, Err(ref message) if message.to_string() == *"too large file size 17179869185 for AppendVec");
1044    }
1045
1046    #[test]
1047    fn test_append_vec_sanitize_len_and_size_full_and_same_as_current_len() {
1048        const LEN: usize = 1024 * 1024;
1049        const SIZE: usize = 1024 * 1024;
1050        let result = AppendVec::sanitize_len_and_size(LEN, SIZE);
1051        assert_matches!(result, Ok(_));
1052    }
1053
1054    #[test]
1055    fn test_append_vec_sanitize_len_and_size_larger_current_len() {
1056        const LEN: usize = 1024 * 1024 + 1;
1057        const SIZE: usize = 1024 * 1024;
1058        let result = AppendVec::sanitize_len_and_size(LEN, SIZE);
1059        assert_matches!(result, Err(ref message) if message.to_string() == *"current_len is larger than file size (1048576)");
1060    }
1061
1062    #[test]
1063    fn test_append_vec_one() {
1064        let path = get_append_vec_path("test_append");
1065        let av = AppendVec::new(&path.path, true, 1024 * 1024);
1066        let account = create_test_account(0);
1067        let index = av.append_account_test(&account).unwrap();
1068        assert_eq!(av.get_account_test(index).unwrap(), account);
1069    }
1070
1071    #[test]
1072    fn test_remaining_bytes() {
1073        let path = get_append_vec_path("test_append");
1074        let sz = 1024 * 1024;
1075        let sz64 = sz as u64;
1076        let av = AppendVec::new(&path.path, true, sz);
1077        assert_eq!(av.capacity(), sz64);
1078        assert_eq!(av.remaining_bytes(), sz64);
1079        let account = create_test_account(0);
1080        av.append_account_test(&account).unwrap();
1081        assert_eq!(av.capacity(), sz64);
1082        assert_eq!(av.remaining_bytes(), sz64 - (STORE_META_OVERHEAD as u64));
1083    }
1084
1085    #[test]
1086    fn test_append_vec_data() {
1087        let path = get_append_vec_path("test_append_data");
1088        let av = AppendVec::new(&path.path, true, 1024 * 1024);
1089        let account = create_test_account(5);
1090        let index = av.append_account_test(&account).unwrap();
1091        assert_eq!(av.get_account_test(index).unwrap(), account);
1092        let account1 = create_test_account(6);
1093        let index1 = av.append_account_test(&account1).unwrap();
1094        assert_eq!(av.get_account_test(index).unwrap(), account);
1095        assert_eq!(av.get_account_test(index1).unwrap(), account1);
1096    }
1097
1098    #[test]
1099    fn test_account_matches_owners() {
1100        let path = get_append_vec_path("test_append_data");
1101        let av = AppendVec::new(&path.path, true, 1024 * 1024);
1102        let owners: Vec<Pubkey> = (0..2).map(|_| Pubkey::new_unique()).collect();
1103        let owners_refs: Vec<&Pubkey> = owners.iter().collect();
1104
1105        let mut account = create_test_account(5);
1106        account.1.set_owner(owners[0]);
1107        let index = av.append_account_test(&account).unwrap();
1108        assert_eq!(av.account_matches_owners(index, &owners_refs), Ok(0));
1109
1110        let mut account1 = create_test_account(6);
1111        account1.1.set_owner(owners[1]);
1112        let index1 = av.append_account_test(&account1).unwrap();
1113        assert_eq!(av.account_matches_owners(index1, &owners_refs), Ok(1));
1114        assert_eq!(av.account_matches_owners(index, &owners_refs), Ok(0));
1115
1116        let mut account2 = create_test_account(6);
1117        account2.1.set_owner(Pubkey::new_unique());
1118        let index2 = av.append_account_test(&account2).unwrap();
1119        assert_eq!(
1120            av.account_matches_owners(index2, &owners_refs),
1121            Err(MatchAccountOwnerError::NoMatch)
1122        );
1123
1124        // tests for overflow
1125        assert_eq!(
1126            av.account_matches_owners(usize::MAX - mem::size_of::<StoredMeta>(), &owners_refs),
1127            Err(MatchAccountOwnerError::UnableToLoad)
1128        );
1129
1130        assert_eq!(
1131            av.account_matches_owners(
1132                usize::MAX - mem::size_of::<StoredMeta>() - mem::size_of::<AccountMeta>() + 1,
1133                &owners_refs
1134            ),
1135            Err(MatchAccountOwnerError::UnableToLoad)
1136        );
1137    }
1138
1139    #[test]
1140    fn test_append_vec_append_many() {
1141        let path = get_append_vec_path("test_append_many");
1142        let av = AppendVec::new(&path.path, true, 1024 * 1024);
1143        let size = 1000;
1144        let mut indexes = vec![];
1145        let now = Instant::now();
1146        for sample in 0..size {
1147            let account = create_test_account(sample);
1148            let pos = av.append_account_test(&account).unwrap();
1149            assert_eq!(av.get_account_test(pos).unwrap(), account);
1150            indexes.push(pos)
1151        }
1152        trace!("append time: {} ms", duration_as_ms(&now.elapsed()),);
1153
1154        let now = Instant::now();
1155        for _ in 0..size {
1156            let sample = thread_rng().gen_range(0, indexes.len());
1157            let account = create_test_account(sample);
1158            assert_eq!(av.get_account_test(indexes[sample]).unwrap(), account);
1159        }
1160        trace!("random read time: {} ms", duration_as_ms(&now.elapsed()),);
1161
1162        let now = Instant::now();
1163        assert_eq!(indexes.len(), size);
1164        assert_eq!(indexes[0], 0);
1165        let mut accounts = av.accounts(indexes[0]);
1166        assert_eq!(accounts.len(), size);
1167        for (sample, v) in accounts.iter_mut().enumerate() {
1168            let account = create_test_account(sample);
1169            let recovered = v.clone_account();
1170            assert_eq!(recovered, account.1)
1171        }
1172        trace!(
1173            "sequential read time: {} ms",
1174            duration_as_ms(&now.elapsed()),
1175        );
1176    }
1177
1178    #[test]
1179    fn test_new_from_file_crafted_zero_lamport_account() {
1180        // This test verifies that when we sanitize on load, that we fail sanitizing if we load an account with zero lamports that does not have all default value fields.
1181        // This test writes an account with zero lamports, but with 3 bytes of data. On load, it asserts that load fails.
1182        // It used to be possible to use the append vec api to write an account to an append vec with zero lamports, but with non-default values for other account fields.
1183        // This will no longer be possible. Thus, to implement the write portion of this test would require additional test-only parameters to public apis or otherwise duplicating code paths.
1184        // So, the sanitizing on load behavior can be tested by capturing [u8] that would be created if such a write was possible (as it used to be).
1185        // The contents of [u8] written by an append vec cannot easily or reasonably change frequently since it has released a long time.
1186        /*
1187            solana_logger::setup();
1188            // uncomment this code to generate the invalid append vec that will fail on load
1189            let file = get_append_vec_path("test_append");
1190            let path = &file.path;
1191            let mut av = AppendVec::new(path, true, 256);
1192            av.set_no_remove_on_drop();
1193
1194            let pubkey = solana_sdk::pubkey::new_rand();
1195            let owner = Pubkey::default();
1196            let data_len = 3_u64;
1197            let mut account = AccountSharedData::new(0, data_len as usize, &owner);
1198            account.set_data(b"abc".to_vec());
1199            let stored_meta = StoredMeta {
1200                write_version: 0,
1201                pubkey,
1202                data_len,
1203            };
1204            let account_with_meta = (stored_meta, account);
1205            let index = av.append_account_test(&account_with_meta).unwrap();
1206            assert_eq!(av.get_account_test(index).unwrap(), account_with_meta);
1207
1208            av.flush().unwrap();
1209            let accounts_len = av.len();
1210            drop(av);
1211            // read file and log out as [u8]
1212            use std::fs::File;
1213            use std::io::BufReader;
1214            use std::io::Read;
1215            let f = File::open(path).unwrap();
1216            let mut reader = BufReader::new(f);
1217            let mut buffer = Vec::new();
1218            reader.read_to_end(&mut buffer).unwrap();
1219            error!("{:?}", buffer);
1220        */
1221
1222        // create an invalid append vec file using known bytes
1223        let file = get_append_vec_path("test_append_bytes");
1224        let path = &file.path;
1225
1226        let accounts_len = 139;
1227        {
1228            let append_vec_data = [
1229                0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 192, 118, 150, 1, 185, 209, 118,
1230                82, 154, 222, 172, 202, 110, 26, 218, 140, 143, 96, 61, 43, 212, 73, 203, 7, 190,
1231                88, 80, 222, 110, 114, 67, 254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1232                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1233                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1234                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 97, 98, 99, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1235                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1236                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1237                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1238                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1239            ];
1240
1241            let f = std::fs::File::create(path).unwrap();
1242            let mut writer = std::io::BufWriter::new(f);
1243            writer.write_all(append_vec_data.as_slice()).unwrap();
1244        }
1245
1246        let result = AppendVec::new_from_file(path, accounts_len);
1247        assert_matches!(result, Err(ref message) if message.to_string().starts_with("incorrect layout/length/data"));
1248    }
1249
1250    #[test]
1251    fn test_new_from_file_crafted_data_len() {
1252        let file = get_append_vec_path("test_new_from_file_crafted_data_len");
1253        let path = &file.path;
1254        let mut av = AppendVec::new(path, true, 1024 * 1024);
1255        av.set_no_remove_on_drop();
1256
1257        let crafted_data_len = 1;
1258
1259        av.append_account_test(&create_test_account(10)).unwrap();
1260
1261        let accounts = av.accounts(0);
1262        let account = accounts.first().unwrap();
1263        account.set_data_len_unsafe(crafted_data_len);
1264        assert_eq!(account.meta.data_len, crafted_data_len);
1265
1266        // Reload accounts and observe crafted_data_len
1267        let accounts = av.accounts(0);
1268        let account = accounts.first().unwrap();
1269        assert_eq!(account.meta.data_len, crafted_data_len);
1270
1271        av.flush().unwrap();
1272        let accounts_len = av.len();
1273        drop(av);
1274        let result = AppendVec::new_from_file(path, accounts_len);
1275        assert_matches!(result, Err(ref message) if message.to_string().starts_with("incorrect layout/length/data"));
1276    }
1277
1278    #[test]
1279    fn test_new_from_file_too_large_data_len() {
1280        let file = get_append_vec_path("test_new_from_file_too_large_data_len");
1281        let path = &file.path;
1282        let mut av = AppendVec::new(path, true, 1024 * 1024);
1283        av.set_no_remove_on_drop();
1284
1285        let too_large_data_len = u64::max_value();
1286        av.append_account_test(&create_test_account(10)).unwrap();
1287
1288        let accounts = av.accounts(0);
1289        let account = accounts.first().unwrap();
1290        account.set_data_len_unsafe(too_large_data_len);
1291        assert_eq!(account.meta.data_len, too_large_data_len);
1292
1293        // Reload accounts and observe no account with bad offset
1294        let accounts = av.accounts(0);
1295        assert_matches!(accounts.first(), None);
1296
1297        av.flush().unwrap();
1298        let accounts_len = av.len();
1299        drop(av);
1300        let result = AppendVec::new_from_file(path, accounts_len);
1301        assert_matches!(result, Err(ref message) if message.to_string().starts_with("incorrect layout/length/data"));
1302    }
1303
1304    #[test]
1305    fn test_new_from_file_crafted_executable() {
1306        let file = get_append_vec_path("test_new_from_crafted_executable");
1307        let path = &file.path;
1308        let mut av = AppendVec::new(path, true, 1024 * 1024);
1309        av.set_no_remove_on_drop();
1310        av.append_account_test(&create_test_account(10)).unwrap();
1311        {
1312            let mut executable_account = create_test_account(10);
1313            executable_account.1.set_executable(true);
1314            av.append_account_test(&executable_account).unwrap();
1315        }
1316
1317        // reload accounts
1318        let accounts = av.accounts(0);
1319
1320        // ensure false is 0u8 and true is 1u8 actually
1321        assert_eq!(*accounts[0].ref_executable_byte(), 0);
1322        assert_eq!(*accounts[1].ref_executable_byte(), 1);
1323
1324        let account = &accounts[0];
1325        let crafted_executable = u8::max_value() - 1;
1326
1327        account.set_executable_as_byte(crafted_executable);
1328
1329        // reload crafted accounts
1330        let accounts = av.accounts(0);
1331        let account = accounts.first().unwrap();
1332
1333        // upper 7-bits are not 0, so sanitization should fail
1334        assert!(!account.sanitize_executable());
1335
1336        // we can observe crafted value by ref
1337        {
1338            let executable_bool: &bool = &account.account_meta.executable;
1339            // Depending on use, *executable_bool can be truthy or falsy due to direct memory manipulation
1340            // assert_eq! thinks *executable_bool is equal to false but the if condition thinks it's not, contradictorily.
1341            assert!(!*executable_bool);
1342            #[cfg(not(target_arch = "aarch64"))]
1343            {
1344                const FALSE: bool = false; // keep clippy happy
1345                if *executable_bool == FALSE {
1346                    panic!("This didn't occur if this test passed.");
1347                }
1348            }
1349            assert_eq!(*account.ref_executable_byte(), crafted_executable);
1350        }
1351
1352        // we can NOT observe crafted value by value
1353        {
1354            let executable_bool: bool = account.account_meta.executable;
1355            assert!(!executable_bool);
1356            assert_eq!(account.get_executable_byte(), 0); // Wow, not crafted_executable!
1357        }
1358
1359        av.flush().unwrap();
1360        let accounts_len = av.len();
1361        drop(av);
1362        let result = AppendVec::new_from_file(path, accounts_len);
1363        assert_matches!(result, Err(ref message) if message.to_string().starts_with("incorrect layout/length/data"));
1364    }
1365
1366    #[test]
1367    fn test_type_layout() {
1368        assert_eq!(offset_of!(StoredMeta, write_version_obsolete), 0x00);
1369        assert_eq!(offset_of!(StoredMeta, data_len), 0x08);
1370        assert_eq!(offset_of!(StoredMeta, pubkey), 0x10);
1371        assert_eq!(mem::size_of::<StoredMeta>(), 0x30);
1372
1373        assert_eq!(offset_of!(AccountMeta, lamports), 0x00);
1374        assert_eq!(offset_of!(AccountMeta, rent_epoch), 0x08);
1375        assert_eq!(offset_of!(AccountMeta, owner), 0x10);
1376        assert_eq!(offset_of!(AccountMeta, executable), 0x30);
1377        assert_eq!(mem::size_of::<AccountMeta>(), 0x38);
1378    }
1379}