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:
parent
154dfae9b9
commit
6686adba04
@ -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
|
||||||
|
@ -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),
|
||||||
}
|
}
|
||||||
|
@ -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),
|
||||||
|
@ -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));
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user