Skip to content

Commit 3c583a1

Browse files
author
HeroicKatora
authored
Merge pull request #1417 from fseegraeber/check-gif-frame-consistency
Enabled bounds check for gif frames
2 parents b7ba06b + 281e95e commit 3c583a1

File tree

1 file changed

+117
-66
lines changed

1 file changed

+117
-66
lines changed

src/codecs/gif.rs

Lines changed: 117 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -94,51 +94,61 @@ impl<'a, R: 'a + Read> ImageDecoder<'a> for GifDecoder<R> {
9494
fn read_image(mut self, buf: &mut [u8]) -> ImageResult<()> {
9595
assert_eq!(u64::try_from(buf.len()), Ok(self.total_bytes()));
9696

97-
let (f_width, f_height, left, top);
97+
let frame = match self.reader.next_frame_info()
98+
.map_err(ImageError::from_decoding)? {
99+
Some(frame) => FrameInfo::new_from_frame(frame),
100+
None => {
101+
return Err(ImageError::Parameter(ParameterError::from_kind(
102+
ParameterErrorKind::NoMoreData,
103+
)))
104+
}
105+
};
98106

99-
if let Some(frame) = self.reader.next_frame_info().map_err(ImageError::from_decoding)? {
100-
left = u32::from(frame.left);
101-
top = u32::from(frame.top);
102-
f_width = u32::from(frame.width);
103-
f_height = u32::from(frame.height);
104-
} else {
105-
return Err(ImageError::Parameter(
106-
ParameterError::from_kind(ParameterErrorKind::NoMoreData)
107-
));
108-
}
107+
let (width, height) = self.dimensions();
109108

110-
self.reader.read_into_buffer(buf).map_err(ImageError::from_decoding)?;
111-
112-
let (width, height) = (u32::from(self.reader.width()), u32::from(self.reader.height()));
113-
if (left, top) != (0, 0) || (width, height) != (f_width, f_height) {
114-
// This is somewhat of an annoying case. The image we read into `buf` doesn't take up
115-
// the whole buffer and now we need to properly insert borders. For simplicity this code
116-
// currently takes advantage of the `ImageBuffer::from_fn` function to make a second
117-
// ImageBuffer that is properly positioned, and then copies it back into `buf`.
118-
//
119-
// TODO: Implement this without any allocation.
120-
121-
// Recover the full image
122-
let image_buffer = {
123-
// See the comments inside `<GifFrameIterator as Iterator>::next` about
124-
// the error handling of `from_raw`.
125-
let image = ImageBuffer::from_raw(f_width, f_height, &mut *buf).ok_or_else(
126-
|| ImageError::Unsupported(UnsupportedError::from_format_and_kind(
109+
if (frame.left, frame.top) == (0, 0) && (frame.width, frame.height) != self.dimensions() {
110+
// If the frame matches the logical screen, directly read into it
111+
self.reader.read_into_buffer(buf).map_err(ImageError::from_decoding)?;
112+
} else {
113+
// If the frame does not match the logical screen, read into an extra buffer
114+
// and 'insert' the frame from left/top to logical screen width/height.
115+
let mut frame_buffer = vec![0; self.reader.buffer_size()];
116+
self.reader.read_into_buffer(&mut frame_buffer[..]).map_err(ImageError::from_decoding)?;
117+
118+
let frame_buffer = ImageBuffer::from_raw(frame.width, frame.height, frame_buffer);
119+
let image_buffer = ImageBuffer::from_raw(width, height, buf);
120+
121+
// `buffer_size` uses wrapping arithmetics, thus might not report the
122+
// correct storage requirement if the result does not fit in `usize`.
123+
// `ImageBuffer::from_raw` detects overflow and reports by returning `None`.
124+
if frame_buffer.is_none() || image_buffer.is_none() {
125+
return Err(ImageError::Unsupported(
126+
UnsupportedError::from_format_and_kind(
127127
ImageFormat::Gif.into(),
128-
UnsupportedErrorKind::GenericFeature(format!("Image dimensions ({}, {}) are too large", f_width, f_height)))))?;
129-
130-
ImageBuffer::from_fn(width, height, |x, y| {
131-
let x = x.wrapping_sub(left);
132-
let y = y.wrapping_sub(top);
133-
if x < image.width() && y < image.height() {
134-
*image.get_pixel(x, y)
135-
} else {
136-
Rgba([0, 0, 0, 0])
137-
}
138-
})
139-
};
140-
buf.copy_from_slice(&image_buffer.into_raw());
128+
UnsupportedErrorKind::GenericFeature(format!(
129+
"Image dimensions ({}, {}) are too large",
130+
frame.width, frame.height
131+
)),
132+
),
133+
));
134+
}
135+
136+
let frame_buffer = frame_buffer.unwrap();
137+
let mut image_buffer = image_buffer.unwrap();
138+
139+
for (x, y, pixel) in image_buffer.enumerate_pixels_mut() {
140+
let frame_x = x.wrapping_sub(frame.left);
141+
let frame_y = y.wrapping_sub(frame.top);
142+
143+
if frame_x < frame.width && frame_y < frame.height {
144+
*pixel = *frame_buffer.get_pixel(frame_x, frame_y);
145+
} else {
146+
// this is only necessary in case the buffer is not zeroed
147+
*pixel = Rgba([0, 0, 0, 0]);
148+
}
149+
}
141150
}
151+
142152
Ok(())
143153
}
144154
}
@@ -152,7 +162,6 @@ struct GifFrameIterator<R: Read> {
152162
non_disposed_frame: ImageBuffer<Rgba<u8>, Vec<u8>>,
153163
}
154164

155-
156165
impl<R: Read> GifFrameIterator<R> {
157166
fn new(decoder: GifDecoder<R>) -> GifFrameIterator<R> {
158167
let (width, height) = decoder.dimensions();
@@ -174,32 +183,23 @@ impl<R: Read> GifFrameIterator<R> {
174183
}
175184
}
176185

177-
178186
impl<R: Read> Iterator for GifFrameIterator<R> {
179187
type Item = ImageResult<animation::Frame>;
180188

181189
fn next(&mut self) -> Option<ImageResult<animation::Frame>> {
182190
// begin looping over each frame
183-
let (left, top, delay, dispose, f_width, f_height);
184191

185-
match self.reader.next_frame_info() {
192+
let frame = match self.reader.next_frame_info() {
186193
Ok(frame_info) => {
187194
if let Some(frame) = frame_info {
188-
left = u32::from(frame.left);
189-
top = u32::from(frame.top);
190-
f_width = u32::from(frame.width);
191-
f_height = u32::from(frame.height);
192-
193-
// frame.delay is in units of 10ms so frame.delay*10 is in ms
194-
delay = Ratio::new(u32::from(frame.delay) * 10, 1);
195-
dispose = frame.dispose;
195+
FrameInfo::new_from_frame(frame)
196196
} else {
197197
// no more frames
198198
return None;
199199
}
200-
},
200+
}
201201
Err(err) => return Some(Err(ImageError::from_decoding(err))),
202-
}
202+
};
203203

204204
let mut vec = vec![0; self.reader.buffer_size()];
205205
if let Err(err) = self.reader.read_into_buffer(&mut vec) {
@@ -211,13 +211,18 @@ impl<R: Read> Iterator for GifFrameIterator<R> {
211211
// correct storage requirement if the result does not fit in `usize`.
212212
// on the other hand, `ImageBuffer::from_raw` detects overflow and
213213
// reports by returning `None`.
214-
let mut frame_buffer = match ImageBuffer::from_raw(f_width, f_height, vec) {
214+
let mut frame_buffer = match ImageBuffer::from_raw(frame.width, frame.height, vec) {
215215
Some(frame_buffer) => frame_buffer,
216216
None => {
217-
return Some(Err(ImageError::Unsupported(UnsupportedError::from_format_and_kind(
218-
ImageFormat::Gif.into(),
219-
UnsupportedErrorKind::GenericFeature(format!("Image dimensions ({}, {}) are too large", f_width, f_height)),
220-
))))
217+
return Some(Err(ImageError::Unsupported(
218+
UnsupportedError::from_format_and_kind(
219+
ImageFormat::Gif.into(),
220+
UnsupportedErrorKind::GenericFeature(format!(
221+
"Image dimensions ({}, {}) are too large",
222+
frame.width, frame.height
223+
)),
224+
),
225+
)))
221226
}
222227
};
223228

@@ -253,22 +258,22 @@ impl<R: Read> Iterator for GifFrameIterator<R> {
253258
// if `frame_buffer`'s frame exactly matches the entire image, then
254259
// use it directly, else create a new buffer to hold the composited
255260
// image.
256-
let image_buffer = if (left, top) == (0, 0)
261+
let image_buffer = if (frame.left, frame.top) == (0, 0)
257262
&& (self.width, self.height) == frame_buffer.dimensions() {
258263
for (x, y, pixel) in frame_buffer.enumerate_pixels_mut() {
259264
let previous_pixel = self.non_disposed_frame.get_pixel_mut(x, y);
260-
blend_and_dispose_pixel(dispose, previous_pixel, pixel);
265+
blend_and_dispose_pixel(frame.disposal_method, previous_pixel, pixel);
261266
}
262267
frame_buffer
263268
} else {
264269
ImageBuffer::from_fn(self.width, self.height, |x, y| {
265-
let frame_x = x.wrapping_sub(left);
266-
let frame_y = y.wrapping_sub(top);
270+
let frame_x = x.wrapping_sub(frame.left);
271+
let frame_y = y.wrapping_sub(frame.top);
267272
let previous_pixel = self.non_disposed_frame.get_pixel_mut(x, y);
268273

269274
if frame_x < frame_buffer.width() && frame_y < frame_buffer.height() {
270275
let mut pixel = *frame_buffer.get_pixel(frame_x, frame_y);
271-
blend_and_dispose_pixel(dispose, previous_pixel, &mut pixel);
276+
blend_and_dispose_pixel(frame.disposal_method, previous_pixel, &mut pixel);
272277
pixel
273278
} else {
274279
// out of bounds, return pixel from previous frame
@@ -278,7 +283,7 @@ impl<R: Read> Iterator for GifFrameIterator<R> {
278283
};
279284

280285
Some(Ok(animation::Frame::from_parts(
281-
image_buffer, 0, 0, animation::Delay::from_ratio(delay),
286+
image_buffer, 0,0, frame.delay,
282287
)))
283288
}
284289
}
@@ -289,6 +294,29 @@ impl<'a, R: Read + 'a> AnimationDecoder<'a> for GifDecoder<R> {
289294
}
290295
}
291296

297+
struct FrameInfo {
298+
left: u32,
299+
top: u32,
300+
width: u32,
301+
height: u32,
302+
disposal_method: DisposalMethod,
303+
delay: animation::Delay,
304+
}
305+
306+
impl FrameInfo {
307+
fn new_from_frame(frame: &Frame) -> FrameInfo {
308+
FrameInfo {
309+
left: u32::from(frame.left),
310+
top: u32::from(frame.top),
311+
width: u32::from(frame.width),
312+
height: u32::from(frame.height),
313+
disposal_method: frame.dispose,
314+
// frame.delay is in units of 10ms so frame.delay*10 is in ms
315+
delay: animation::Delay::from_ratio(Ratio::new(u32::from(frame.delay) * 10, 1)),
316+
}
317+
}
318+
}
319+
292320
/// Number of repetitions for a GIF animation
293321
#[derive(Clone, Copy)]
294322
pub enum Repeat {
@@ -477,3 +505,26 @@ impl ImageError {
477505
}
478506
}
479507
}
508+
509+
#[cfg(test)]
510+
mod test {
511+
use super::*;
512+
513+
#[test]
514+
fn frames_exceeding_logical_screen_size() {
515+
// This is a gif with 10x10 logical screen, but a 16x16 frame + 6px offset inside.
516+
let data = vec![
517+
0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x0A, 0x00, 0x0A, 0x00, 0xF0, 0x00, 0x00, 0x00,
518+
0x00, 0x00, 0x0E, 0xFF, 0x1F, 0x21, 0xF9, 0x04, 0x09, 0x64, 0x00, 0x00, 0x00, 0x2C,
519+
0x06, 0x00, 0x06, 0x00, 0x10, 0x00, 0x10, 0x00, 0x00, 0x02, 0x23, 0x84, 0x8F, 0xA9,
520+
0xBB, 0xE1, 0xE8, 0x42, 0x8A, 0x0F, 0x50, 0x79, 0xAE, 0xD1, 0xF9, 0x7A, 0xE8, 0x71,
521+
0x5B, 0x48, 0x81, 0x64, 0xD5, 0x91, 0xCA, 0x89, 0x4D, 0x21, 0x63, 0x89, 0x4C, 0x09,
522+
0x77, 0xF5, 0x6D, 0x14, 0x00, 0x3B,
523+
];
524+
525+
let decoder = GifDecoder::new(Cursor::new(data)).unwrap();
526+
let mut buf = vec![0u8; decoder.total_bytes() as usize];
527+
528+
assert!(decoder.read_image(&mut buf).is_ok());
529+
}
530+
}

0 commit comments

Comments
 (0)