diff --git a/crates/psd/src/pixels.rs b/crates/psd/src/pixels.rs index 4e1a625e3..2222781df 100644 --- a/crates/psd/src/pixels.rs +++ b/crates/psd/src/pixels.rs @@ -293,6 +293,11 @@ impl Psd { if comp == 0 && size.checked_mul(want).is_none_or(|need| need > r.remaining()) { return Err(Error::Truncated); } + if let PlaneStream::Zip(v) = &stream + && size.checked_mul(want).is_none_or(|need| v.len() < need) + { + return Err(Error::Truncated); + } // Opaque black until a channel says otherwise. let mut data = budget.filled(n, [0.0f32, 0.0, 0.0, 1.0])?; let mut black = if self.mode == ColorMode::Cmyk && want > 3 { Some(budget.filled(n, 0.0f32)?) } else { None }; @@ -357,6 +362,15 @@ impl Psd { self.check_channel(c.offset, c.len, sw, sh)?; } } + // The layer mask's channel too: it is decoded after the result is allocated. + if let Some(m) = l.mask.filter(|m| !m.disabled) + && let Some(c) = l.channels.iter().find(|c| c.id == -2) + { + let (mw, mh) = (m.rect.width() as usize, m.rect.height() as usize); + if mw > 0 && mh > 0 { + self.check_channel(c.offset, c.len, mw, mh)?; + } + } let mut out = budget.filled(n, [0.0f32; 4])?; if sw > 0 && sh > 0 { // The source rows / columns that land inside the output (64-bit: file bounds can span