1
1
mirror of https://github.com/wez/wezterm.git synced 2024-11-29 21:44:24 +03:00

fix panic with corrupt webp file

There were a couple of layers of issue here:

* In the ImageDataType::decode method, we didn't detect and do something
  reasonable when the decoded image had 0 frames, later leading to
  a panic in glyphcache when trying to index frame 0 of an empty vec.
* We shouldn't have been using ImageDataType::decode for window
  background images
* Make the fallback/placeholder black rather than fully transparent
  in the more modern decoder thread routine that we use for image
  decoding at the gui layer.

refs: #3614
This commit is contained in:
Wez Furlong 2023-04-25 08:37:10 -07:00
parent 154dfae9b9
commit 6686adba04
No known key found for this signature in database
GPG Key ID: 7A7F66A31EC9B387
4 changed files with 43 additions and 10 deletions

View File

@ -77,6 +77,7 @@ As features stabilize some brief notes about them will accumulate here.
The about dialog has been replaced with a menu item that you can click to The about dialog has been replaced with a menu item that you can click to
copy the version number. #3507 #3585 copy the version number. #3507 #3585
* Synthesized italics were double-skewed. Thanks to @rozbb! #3613 #3555 * Synthesized italics were double-skewed. Thanks to @rozbb! #3613 #3555
* Panic when using corrupt/invalid webp images as window background #3614
#### Updated #### Updated
* Bundled harfbuzz to 7.1.0 * Bundled harfbuzz to 7.1.0

View File

@ -284,6 +284,16 @@ impl ImageDataType {
} }
} }
/// Black pixels
pub fn placeholder() -> Self {
let mut data = vec![];
let size = 8;
for _ in 0..size * size {
data.extend_from_slice(&[0, 0, 0, 0xff]);
}
ImageDataType::new_single_frame(size, size, data)
}
pub fn hash_bytes(bytes: &[u8]) -> [u8; 32] { pub fn hash_bytes(bytes: &[u8]) -> [u8; 32] {
use sha2::Digest; use sha2::Digest;
let mut hasher = sha2::Sha256::new(); let mut hasher = sha2::Sha256::new();
@ -380,7 +390,14 @@ impl ImageDataType {
match format { match format {
ImageFormat::Gif => image::codecs::gif::GifDecoder::new(&*data) ImageFormat::Gif => image::codecs::gif::GifDecoder::new(&*data)
.and_then(|decoder| decoder.into_frames().collect_frames()) .and_then(|decoder| decoder.into_frames().collect_frames())
.and_then(|frames| Ok(Self::decode_frames(frames))) .and_then(|frames| {
if frames.is_empty() {
log::error!("decoded image has 0 frames, using placeholder");
Ok(Self::placeholder())
} else {
Ok(Self::decode_frames(frames))
}
})
.unwrap_or_else(|err| { .unwrap_or_else(|err| {
log::error!( log::error!(
"Unable to parse animated gif: {:#}, trying as single frame", "Unable to parse animated gif: {:#}, trying as single frame",
@ -395,6 +412,10 @@ impl ImageDataType {
}; };
if decoder.is_apng() { if decoder.is_apng() {
match decoder.apng().into_frames().collect_frames() { match decoder.apng().into_frames().collect_frames() {
Ok(frames) if frames.is_empty() => {
log::error!("decoded image has 0 frames, using placeholder");
Self::placeholder()
}
Ok(frames) => Self::decode_frames(frames), Ok(frames) => Self::decode_frames(frames),
_ => Self::EncodedFile(data), _ => Self::EncodedFile(data),
} }
@ -408,6 +429,10 @@ impl ImageDataType {
_ => return Self::EncodedFile(data), _ => return Self::EncodedFile(data),
}; };
match decoder.into_frames().collect_frames() { match decoder.into_frames().collect_frames() {
Ok(frames) if frames.is_empty() => {
log::error!("decoded image has 0 frames, using placeholder");
Self::placeholder()
}
Ok(frames) => Self::decode_frames(frames), Ok(frames) => Self::decode_frames(frames),
_ => Self::EncodedFile(data), _ => Self::EncodedFile(data),
} }

View File

@ -317,7 +317,8 @@ impl FrameDecoder {
.next() .next()
.ok_or_else(|| { .ok_or_else(|| {
anyhow::anyhow!( anyhow::anyhow!(
"Image format is not fully supported by \ "Unable to decode image data. Either it is corrupt, or \
the Image format is not fully supported by \
https://github.com/image-rs/image/blob/master/README.md#supported-image-formats") https://github.com/image-rs/image/blob/master/README.md#supported-image-formats")
})?; })?;
let frame = frame.context("first frame result")?; let frame = frame.context("first frame result")?;
@ -386,15 +387,22 @@ struct FrameState {
impl FrameState { impl FrameState {
fn new(rx: Receiver<DecodedFrame>) -> Self { fn new(rx: Receiver<DecodedFrame>) -> Self {
static EMPTY: Lazy<BlobLease> = Lazy::new(|| BlobManager::store(&[0u8; 4]).unwrap()); const BLACK_SIZE: usize = 8;
static BLACK: Lazy<BlobLease> = Lazy::new(|| {
let mut data = vec![];
for _ in 0..BLACK_SIZE * BLACK_SIZE {
data.extend_from_slice(&[0, 0, 0, 0xff]);
}
BlobManager::store(&data).unwrap()
});
Self { Self {
source: FrameSource::Decoder(rx), source: FrameSource::Decoder(rx),
frames: vec![], frames: vec![],
current_frame: DecodedFrame { current_frame: DecodedFrame {
lease: EMPTY.clone(), lease: BLACK.clone(),
width: 1, width: BLACK_SIZE,
height: 1, height: BLACK_SIZE,
duration: Duration::from_millis(0), duration: Duration::from_millis(0),
}, },
} }
@ -412,7 +420,7 @@ impl FrameState {
Err(TryRecvError::Disconnected) => { Err(TryRecvError::Disconnected) => {
self.source = FrameSource::FrameIndex(0); self.source = FrameSource::FrameIndex(0);
if self.frames.is_empty() { if self.frames.is_empty() {
log::warn!("decoder thread terminated"); log::warn!("image decoder thread terminated");
self.current_frame.duration = Duration::from_secs(86400); self.current_frame.duration = Duration::from_secs(86400);
self.frames.push(self.current_frame.clone()); self.frames.push(self.current_frame.clone());
false false
@ -463,8 +471,7 @@ pub struct DecodedImage {
impl DecodedImage { impl DecodedImage {
fn placeholder() -> Self { fn placeholder() -> Self {
// A single black pixel let image = ImageData::with_data(ImageDataType::placeholder());
let image = ImageData::with_data(ImageDataType::new_single_frame(1, 1, vec![0, 0, 0, 0]));
Self { Self {
frame_start: RefCell::new(Instant::now()), frame_start: RefCell::new(Instant::now()),
current_frame: RefCell::new(0), current_frame: RefCell::new(0),

View File

@ -206,7 +206,7 @@ impl CachedImage {
let data = std::fs::read(path) let data = std::fs::read(path)
.with_context(|| format!("Failed to load window_background_image {}", path))?; .with_context(|| format!("Failed to load window_background_image {}", path))?;
log::trace!("loaded {}", path); log::trace!("loaded {}", path);
let mut data = ImageDataType::EncodedFile(data).decode(); let mut data = ImageDataType::EncodedFile(data);
data.adjust_speed(speed); data.adjust_speed(speed);
let image = Arc::new(ImageData::with_data(data)); let image = Arc::new(ImageData::with_data(data));