[TieredStorage] Boundary check for accessing hot account meta (#34349)

#### Problem
Hot accounts are stored in accounts blocks, whose offset is smaller than
the index block offset.  However, the current code doesn't perform
any boundary checks when accessing hot account meta.

#### Summary of Changes
Adds boundary check when accessing hot account meta.
This commit is contained in:
Yueh-Hsuan Chiang 2023-12-18 14:32:41 -08:00 committed by GitHub
parent e858a5a7c0
commit eb948b1ddc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 39 additions and 3 deletions

View File

@ -281,9 +281,16 @@ impl HotStorageReader {
&self,
account_offset: HotAccountOffset,
) -> TieredStorageResult<&HotAccountMeta> {
let internal_account_offset = account_offset.offset();
let offset = account_offset.offset();
let (meta, _) = get_pod::<HotAccountMeta>(&self.mmap, internal_account_offset)?;
assert!(
offset.saturating_add(std::mem::size_of::<HotAccountMeta>())
<= self.footer.index_block_offset as usize,
"reading HotAccountOffset ({}) would exceed accounts blocks offset boundary ({}).",
offset,
self.footer.index_block_offset,
);
let (meta, _) = get_pod::<HotAccountMeta>(&self.mmap, offset)?;
Ok(meta)
}
@ -538,7 +545,7 @@ pub mod tests {
.collect();
let account_offsets: Vec<_>;
let footer = TieredStorageFooter {
let mut footer = TieredStorageFooter {
account_meta_format: AccountMetaFormat::Hot,
account_entry_count: NUM_ACCOUNTS,
..TieredStorageFooter::default()
@ -557,6 +564,7 @@ pub mod tests {
.collect();
// while the test only focuses on account metas, writing a footer
// here is necessary to make it a valid tiered-storage file.
footer.index_block_offset = current_offset as u64;
footer.write_footer_block(&file).unwrap();
}
@ -566,9 +574,37 @@ pub mod tests {
let meta = hot_storage.get_account_meta_from_offset(*offset).unwrap();
assert_eq!(meta, expected_meta);
}
assert_eq!(&footer, hot_storage.footer());
}
#[test]
#[should_panic(expected = "would exceed accounts blocks offset boundary")]
fn test_get_acount_meta_from_offset_out_of_bounds() {
// Generate a new temp path that is guaranteed to NOT already have a file.
let temp_dir = TempDir::new().unwrap();
let path = temp_dir
.path()
.join("test_get_acount_meta_from_offset_out_of_bounds");
let footer = TieredStorageFooter {
account_meta_format: AccountMetaFormat::Hot,
index_block_offset: 160,
..TieredStorageFooter::default()
};
{
let file = TieredStorageFile::new_writable(&path).unwrap();
footer.write_footer_block(&file).unwrap();
}
let hot_storage = HotStorageReader::new_from_path(&path).unwrap();
let offset = HotAccountOffset::new(footer.index_block_offset as usize).unwrap();
// Read from index_block_offset, which offset doesn't belong to
// account blocks. Expect assert failure here
hot_storage.get_account_meta_from_offset(offset).unwrap();
}
#[test]
fn test_hot_storage_get_account_offset_and_address() {
// Generate a new temp path that is guaranteed to NOT already have a file.