mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-15 21:57:04 +00:00
PSD: the slices' version 6 descriptors share one limit of values
Each record's look-ahead descriptor read started with its own allowance of 200,000 values, so a file with 10,000 records could make one Open read about two billion. The descriptors of a slices resource now spend one allowance between them, and a look-ahead that runs it out refuses the slices with a warning instead of being read past.
This commit is contained in:
@@ -29,9 +29,12 @@ const MAX_SLICES: usize = 10_000;
|
||||
/// The deepest descriptor objects and lists nest.
|
||||
const MAX_DEPTH: usize = 16;
|
||||
|
||||
/// The most values one descriptor holds, nested ones included.
|
||||
/// The most values the descriptors of one slices resource hold together, nested ones included.
|
||||
const MAX_VALUES: usize = 200_000;
|
||||
|
||||
/// The error of descriptors that hold more than [`MAX_VALUES`].
|
||||
const TOO_MANY_VALUES: &str = "the slices' descriptor holds too many values";
|
||||
|
||||
/// One slice as the file stores it.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
struct PsdSlice {
|
||||
@@ -60,13 +63,14 @@ fn read(bytes: &[u8]) -> Result<Option<Stored>, String> {
|
||||
/// The slices of resource 1050's `data`.
|
||||
fn decode(data: &[u8]) -> Result<Vec<PsdSlice>, String> {
|
||||
let mut r = Reader::new(data);
|
||||
// The values every descriptor of the resource may hold together.
|
||||
let mut budget = MAX_VALUES;
|
||||
match r.u32()? {
|
||||
6 => version_6(&mut r),
|
||||
6 => version_6(&mut r, &mut budget),
|
||||
7 | 8 => {
|
||||
if r.u32()? != 16 {
|
||||
return Err("the slices' descriptor has a version other than 16".into());
|
||||
}
|
||||
let mut budget = MAX_VALUES;
|
||||
let d = descriptor(&mut r, 0, &mut budget)?;
|
||||
from_descriptor(&d)
|
||||
}
|
||||
@@ -75,7 +79,8 @@ fn decode(data: &[u8]) -> Result<Vec<PsdSlice>, String> {
|
||||
}
|
||||
|
||||
/// Version 6: the bounds of all the slices, the group's name, the count, then each slice's record.
|
||||
fn version_6(r: &mut Reader<'_>) -> Result<Vec<PsdSlice>, String> {
|
||||
/// The descriptors that follow the records spend one `budget` of values between them.
|
||||
fn version_6(r: &mut Reader<'_>, budget: &mut usize) -> Result<Vec<PsdSlice>, String> {
|
||||
r.take(16)?;
|
||||
unicode(r)?;
|
||||
let count = r.u32()? as usize;
|
||||
@@ -111,13 +116,18 @@ fn version_6(r: &mut Reader<'_>) -> Result<Vec<PsdSlice>, String> {
|
||||
format!("#{red:02x}{green:02x}{blue:02x}")
|
||||
};
|
||||
// Photoshop 7 and later may follow a record with a descriptor (version 16); a record that
|
||||
// doesn't start one is the next slice.
|
||||
// doesn't start one is the next slice. A descriptor read that uses up the budget is an
|
||||
// error.
|
||||
if r.rest().starts_with(&16u32.to_be_bytes()) {
|
||||
let mut ahead = Reader::new(r.rest());
|
||||
let mut budget = MAX_VALUES;
|
||||
if ahead.u32().is_ok() && descriptor(&mut ahead, 0, &mut budget).is_ok() {
|
||||
let used = r.rest().len() - ahead.rest().len();
|
||||
r.take(used)?;
|
||||
ahead.u32()?;
|
||||
match descriptor(&mut ahead, 0, budget) {
|
||||
Ok(_) => {
|
||||
let used = r.rest().len() - ahead.rest().len();
|
||||
r.take(used)?;
|
||||
}
|
||||
Err(e) if e == TOO_MANY_VALUES => return Err(e),
|
||||
Err(_) => {}
|
||||
}
|
||||
}
|
||||
// Origin 0 is a slice generated automatically.
|
||||
@@ -278,7 +288,7 @@ fn descriptor(r: &mut Reader<'_>, depth: usize, budget: &mut usize) -> Result<Ve
|
||||
|
||||
/// A value of type `kind`.
|
||||
fn value(r: &mut Reader<'_>, kind: &[u8; 4], depth: usize, budget: &mut usize) -> Result<Value, String> {
|
||||
*budget = budget.checked_sub(1).ok_or("the slices' descriptor holds too many values")?;
|
||||
*budget = budget.checked_sub(1).ok_or(TOO_MANY_VALUES)?;
|
||||
Ok(match kind {
|
||||
b"long" => Value::Long(r.u32()? as i32),
|
||||
b"doub" => Value::Double(f64::from_be_bytes(r.array()?)),
|
||||
@@ -322,7 +332,7 @@ fn value(r: &mut Reader<'_>, kind: &[u8; 4], depth: usize, budget: &mut usize) -
|
||||
b"obj " => {
|
||||
let count = r.u32()? as usize;
|
||||
for _ in 0..count {
|
||||
*budget = budget.checked_sub(1).ok_or("the slices' descriptor holds too many values")?;
|
||||
*budget = budget.checked_sub(1).ok_or(TOO_MANY_VALUES)?;
|
||||
match &r.array::<4>()? {
|
||||
b"prop" => {
|
||||
class(r)?;
|
||||
|
||||
@@ -262,6 +262,24 @@ fn photoshop_7_descriptors_after_version_6_records_are_read_past() {
|
||||
assert_eq!(names, ["a", "b"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn version_6_descriptors_share_one_budget_of_values() {
|
||||
// Each record's descriptor holds 2,501 values, well under the 200,000 one descriptor may hold;
|
||||
// 100 of them hold 250,100 together.
|
||||
let list = D::List((0..2_500).map(D::Long).collect());
|
||||
let trailing = [16u32.to_be_bytes().to_vec(), descriptor(&[("many", list)])].concat();
|
||||
let records: Vec<S> = (0..100).map(|_| user("a", [0, 0, 5, 5])).collect();
|
||||
// Alone, one such descriptor is read past.
|
||||
let (s, r) = open(&with_resource(&psd(false), 1050, &v6(&records[..1], &trailing)), json!({}));
|
||||
assert!(warnings(&r).is_empty(), "{r}");
|
||||
assert_eq!(slices(&s).len(), 1);
|
||||
// Together they are refused, and the file opens without slices.
|
||||
let (s, r) = open(&with_resource(&psd(false), 1050, &v6(&records, &trailing)), json!({}));
|
||||
let w = warnings(&r);
|
||||
assert!(w.iter().any(|w| w.starts_with("the slices can't be read (") && w.contains("too many values")), "{w:?}");
|
||||
assert!(slices(&s).is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn version_7_and_8_descriptors_open_as_user_slices() {
|
||||
let slice = |origin: &'static str, kind: &'static str, l: i32, t: i32, r: i32, b: i32| {
|
||||
|
||||
Reference in New Issue
Block a user