mirror of
https://github.com/storytold/craft-libs.git
synced 2026-11-12 18:13:14 +00:00
font-catalog: cap the font file read where the file has grown since its size was checked (#873)
This commit is contained in:
@@ -22,6 +22,19 @@ const MAX_SCAN_READ: u64 = 256 << 20;
|
||||
/// The largest font file loaded.
|
||||
const MAX_LOAD: u64 = 512 << 20;
|
||||
|
||||
/// The bytes of the file at `path`, at most `max`: the size was checked before, but a file can
|
||||
/// grow between that check and the read, so the read itself stops one byte past the limit.
|
||||
pub(crate) fn read_capped(path: &Path, max: u64) -> Result<Vec<u8>, FontFileError> {
|
||||
use std::io::Read as _;
|
||||
let mut bytes = Vec::new();
|
||||
std::fs::File::open(path)?.take(max.saturating_add(1)).read_to_end(&mut bytes)?;
|
||||
let size = bytes.len() as u64;
|
||||
if size > max {
|
||||
return Err(FontFileError::TooLarge { size, max });
|
||||
}
|
||||
Ok(bytes)
|
||||
}
|
||||
|
||||
/// Why a font file or font data wasn't added or loaded.
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum FontFileError {
|
||||
@@ -544,7 +557,7 @@ impl Catalog {
|
||||
if size > MAX_LOAD {
|
||||
return Err(FontFileError::TooLarge { size, max: MAX_LOAD });
|
||||
}
|
||||
let bytes: Arc<[u8]> = std::fs::read(path)?.into();
|
||||
let bytes: Arc<[u8]> = read_capped(path, MAX_LOAD)?.into();
|
||||
let mut files = lock(&self.files);
|
||||
files.retain(|_, w| w.strong_count() > 0);
|
||||
files.insert(path.to_path_buf(), Arc::downgrade(&bytes));
|
||||
|
||||
@@ -556,3 +556,15 @@ fn repeated_name_records_are_bounded_before_pairing() {
|
||||
assert!(!face.keys.legacy.is_empty());
|
||||
assert_eq!(face.keys.legacy.len(), 256, "the pair budget counts distinct pairs");
|
||||
}
|
||||
|
||||
/// A read stops at its cap, whatever the file has grown to since its size was checked.
|
||||
#[test]
|
||||
fn a_read_is_capped_where_the_file_has_grown() {
|
||||
use crate::catalog::read_capped;
|
||||
let path = std::env::temp_dir().join(format!("craft-font-catalog-cap-{}", std::process::id()));
|
||||
std::fs::write(&path, b"0123456789").unwrap();
|
||||
assert_eq!(read_capped(&path, 10).unwrap(), b"0123456789");
|
||||
assert!(matches!(read_capped(&path, 9), Err(FontFileError::TooLarge { size: 10, max: 9 })));
|
||||
assert!(matches!(read_capped(&path.with_extension("missing"), 9), Err(FontFileError::Io(_))));
|
||||
let _ = std::fs::remove_file(&path);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user