Make the GIF dirty_rect be in the frame_rect
Updates https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=11823
diff --git a/fuzz/c/std/gif_fuzzer.c b/fuzz/c/std/gif_fuzzer.c
index abb5cc0..58f911d 100644
--- a/fuzz/c/std/gif_fuzzer.c
+++ b/fuzz/c/std/gif_fuzzer.c
@@ -106,6 +106,15 @@
bool seen_ok = false;
while (true) {
+ wuffs_base__frame_config fc = ((wuffs_base__frame_config){});
+ status = wuffs_gif__decoder__decode_frame_config(&dec, &fc, src_reader);
+ if (status) {
+ if ((status != wuffs_base__warning__end_of_data) || !seen_ok) {
+ ret = status;
+ }
+ goto exit;
+ }
+
status = wuffs_gif__decoder__decode_frame(&dec, &pb, src_reader, workbuf,
NULL);
if (status) {
@@ -115,6 +124,15 @@
goto exit;
}
seen_ok = true;
+
+ wuffs_base__rect_ie_u32 frame_rect =
+ wuffs_base__frame_config__bounds(&fc);
+ wuffs_base__rect_ie_u32 dirty_rect =
+ wuffs_gif__decoder__frame_dirty_rect(&dec);
+ if (!wuffs_base__rect_ie_u32__contains_rect(&frame_rect, dirty_rect)) {
+ ret = "internal error: frame_rect does not contain dirty_rect";
+ goto exit;
+ }
}
}
diff --git a/release/c/wuffs-unsupported-snapshot.c b/release/c/wuffs-unsupported-snapshot.c
index 764ec51..debb131 100644
--- a/release/c/wuffs-unsupported-snapshot.c
+++ b/release/c/wuffs-unsupported-snapshot.c
@@ -8639,9 +8639,6 @@
status = wuffs_base__error__bad_call_sequence;
goto exit;
}
- (memset(&self->private_impl.f_dirty_y, 0,
- sizeof((wuffs_base__range_ie_u32){})),
- wuffs_base__return_empty_struct());
WUFFS_BASE__COROUTINE_SUSPENSION_POINT(1);
status = wuffs_gif__decoder__decode_header(self, a_src);
if (status) {
@@ -8747,9 +8744,11 @@
}
return wuffs_base__utility__make_rect_ie_u32(
- self->private_impl.f_frame_rect_x0,
+ wuffs_base__u32__min(self->private_impl.f_frame_rect_x0,
+ self->private_impl.f_width),
wuffs_base__range_ie_u32__get_min_incl(&self->private_impl.f_dirty_y),
- self->private_impl.f_frame_rect_x1,
+ wuffs_base__u32__min(self->private_impl.f_frame_rect_x1,
+ self->private_impl.f_width),
wuffs_base__range_ie_u32__get_max_excl(&self->private_impl.f_dirty_y));
}
@@ -8826,6 +8825,9 @@
switch (coro_susp_point) {
WUFFS_BASE__COROUTINE_SUSPENSION_POINT_0;
+ (memset(&self->private_impl.f_dirty_y, 0,
+ sizeof((wuffs_base__range_ie_u32){})),
+ wuffs_base__return_empty_struct());
if (!self->private_impl.f_end_of_data) {
if (self->private_impl.f_call_sequence == 0) {
WUFFS_BASE__COROUTINE_SUSPENSION_POINT(1);
diff --git a/std/gif/decode_gif.wuffs b/std/gif/decode_gif.wuffs
index 469e37f..6241fd0 100644
--- a/std/gif/decode_gif.wuffs
+++ b/std/gif/decode_gif.wuffs
@@ -132,7 +132,6 @@
if this.call_sequence >= 1 {
return base."?bad call sequence"
}
- this.dirty_y.reset!()
this.decode_header?(src:args.src)
this.decode_lsd?(src:args.src)
@@ -177,10 +176,16 @@
}
pub func decoder.frame_dirty_rect() base.rect_ie_u32 {
+ // TODO: intersect this with the frame_rect? In theory, that should be
+ // unnecessary, and could hide bugs, but it'd be a cheap way to ensure that
+ // the dirty_rect is inside the frame_rect.
+ //
+ // Note that this method is pure, so it cannot set a sticky error bit if
+ // the dirty_rect is too big.
return this.util.make_rect_ie_u32(
- min_incl_x:this.frame_rect_x0,
+ min_incl_x:this.frame_rect_x0.min(x:this.width),
min_incl_y:this.dirty_y.get_min_incl(),
- max_excl_x:this.frame_rect_x1,
+ max_excl_x:this.frame_rect_x1.min(x:this.width),
max_excl_y:this.dirty_y.get_max_excl())
}
@@ -206,6 +211,8 @@
pub func decoder.decode_frame_config?(dst nptr base.frame_config, src base.io_reader) {
var blend base.u8
+ this.dirty_y.reset!()
+
if not this.end_of_data {
if this.call_sequence == 0 {
this.decode_image_config?(dst:nullptr, src:args.src)
diff --git a/test/c/std/gif.c b/test/c/std/gif.c
index 27d2699..158a10c 100644
--- a/test/c/std/gif.c
+++ b/test/c/std/gif.c
@@ -611,6 +611,13 @@
if (status) {
RETURN_FAIL("decode_frame #%" PRIu32 ": got \"%s\"", i, status);
}
+
+ wuffs_base__rect_ie_u32 frame_rect = wuffs_base__frame_config__bounds(&fc);
+ wuffs_base__rect_ie_u32 dirty_rect =
+ wuffs_gif__decoder__frame_dirty_rect(&dec);
+ if (!wuffs_base__rect_ie_u32__contains_rect(&frame_rect, dirty_rect)) {
+ RETURN_FAIL("internal error: frame_rect does not contain dirty_rect");
+ }
}
// There should be no more frames, no matter how many times we call
@@ -730,8 +737,8 @@
uint32_t i;
for (i = 0; true; i++) {
+ wuffs_base__frame_config fc = ((wuffs_base__frame_config){});
{
- wuffs_base__frame_config fc = ((wuffs_base__frame_config){});
status = wuffs_gif__decoder__decode_frame_config(&dec, &fc, src_reader);
if (i == WUFFS_TESTLIB_ARRAY_SIZE(want_frame_config_bounds)) {
if (status != wuffs_base__warning__end_of_data) {
@@ -771,6 +778,14 @@
RETURN_FAIL("decode_frame #%" PRIu32 ": got \"%s\"", i, status);
}
+ wuffs_base__rect_ie_u32 frame_rect =
+ wuffs_base__frame_config__bounds(&fc);
+ wuffs_base__rect_ie_u32 dirty_rect =
+ wuffs_gif__decoder__frame_dirty_rect(&dec);
+ if (!wuffs_base__rect_ie_u32__contains_rect(&frame_rect, dirty_rect)) {
+ RETURN_FAIL("internal error: frame_rect does not contain dirty_rect");
+ }
+
char got[(width * height) + 1];
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {