ref: 3e367ef80a356c86c39cbd67285d4ac42fd0a9bb
parent: 67bab2ba199ad42e6d18aac1198ec059489d3c20
author: Janne Grunau <janne-vlc@jannau.net>
date: Thu Oct 11 16:31:01 EDT 2018
implement non power of 2 tile rows and columns spec compliant Makes the tile parsing code simpler. Fixes a heap buffer overflow with clusterfuzz-testcase-minimized-dav1d_fuzzer-5726018392817664. Credit to oss-fuzz.
--- a/src/decode.c
+++ b/src/decode.c
@@ -2499,27 +2499,14 @@
memcpy(f->out_cdf.cdf, f->in_cdf.cdf, sizeof(*f->in_cdf.cdf));
// parse individual tiles per tile group
- int update_set = 0, tile_idx = 0;
- const unsigned tile_col_mask = (1 << f->frame_hdr.tiling.log2_cols) - 1;
+ int update_set = 0, tile_row = 0, tile_col = 0;
for (int i = 0; i < f->n_tile_data; i++) {
const uint8_t *data = f->tile[i].data.data;
size_t size = f->tile[i].data.sz;
- const int last_tile_row_plus1 = 1 + (f->tile[i].end >> f->frame_hdr.tiling.log2_cols);
- const int last_tile_col_plus1 = 1 + (f->tile[i].end & tile_col_mask);
- const int empty_tile_cols = imax(0, last_tile_col_plus1 - f->frame_hdr.tiling.cols);
- const int empty_tile_rows = imax(0, last_tile_row_plus1 - f->frame_hdr.tiling.rows);
- const int empty_tiles =
- (empty_tile_rows << f->frame_hdr.tiling.log2_cols) + empty_tile_cols;
- for (int j = f->tile[i].start; j <= f->tile[i].end - empty_tiles; j++) {
- const int tile_row = j >> f->frame_hdr.tiling.log2_cols;
- const int tile_col = j & tile_col_mask;
-
- if (tile_col >= f->frame_hdr.tiling.cols) continue;
- if (tile_row >= f->frame_hdr.tiling.rows) continue;
-
+ for (int j = f->tile[i].start; j <= f->tile[i].end; j++) {
size_t tile_sz;
- if (j == f->tile[i].end - empty_tiles) {
+ if (j == f->tile[i].end) {
tile_sz = size;
} else {
if (f->frame_hdr.tiling.n_bytes > size) goto error;
@@ -2531,9 +2518,13 @@
if (tile_sz > size) goto error;
}
- setup_tile(&f->ts[tile_row * f->frame_hdr.tiling.cols + tile_col],
- f, data, tile_sz, tile_row, tile_col,
- c->n_fc > 1 ? f->frame_thread.tile_start_off[tile_idx++] : 0);
+ setup_tile(&f->ts[j], f, data, tile_sz, tile_row, tile_col++,
+ c->n_fc > 1 ? f->frame_thread.tile_start_off[j] : 0);
+
+ if (tile_col == f->frame_hdr.tiling.cols) {
+ tile_col = 0;
+ tile_row++;
+ }
if (j == f->frame_hdr.tiling.update && f->frame_hdr.refresh_context)
update_set = 1;
data += tile_sz;
--- a/src/internal.h
+++ b/src/internal.h
@@ -75,7 +75,7 @@
int start, end;
} tile[256];
int n_tile_data, have_seq_hdr, have_frame_hdr;
- unsigned tile_mask;
+ int n_tiles;
Av1SequenceHeader seq_hdr; // FIXME make ref?
Av1FrameHeader frame_hdr; // FIXME make ref?
--- a/src/obu.c
+++ b/src/obu.c
@@ -483,6 +483,8 @@
if (hdr->tiling.log2_cols || hdr->tiling.log2_rows) {
hdr->tiling.update = dav1d_get_bits(gb, hdr->tiling.log2_cols +
hdr->tiling.log2_rows);
+ if (hdr->tiling.update >= hdr->tiling.cols * hdr->tiling.rows)
+ goto error;
hdr->tiling.n_bytes = dav1d_get_bits(gb, 2) + 1;
} else {
hdr->tiling.n_bytes = hdr->tiling.update = 0;
@@ -958,17 +960,18 @@
const uint8_t *const init_ptr = gb->ptr;
int have_tile_pos = 0;
- const int n_bits = c->frame_hdr.tiling.log2_cols +
- c->frame_hdr.tiling.log2_rows;
- if (n_bits)
+ const int n_tiles = c->frame_hdr.tiling.cols * c->frame_hdr.tiling.rows;
+ if (n_tiles > 1)
have_tile_pos = dav1d_get_bits(gb, 1);
if (have_tile_pos) {
+ const int n_bits = c->frame_hdr.tiling.log2_cols +
+ c->frame_hdr.tiling.log2_rows;
c->tile[c->n_tile_data].start = dav1d_get_bits(gb, n_bits);
c->tile[c->n_tile_data].end = dav1d_get_bits(gb, n_bits);
} else {
c->tile[c->n_tile_data].start = 0;
- c->tile[c->n_tile_data].end = (1 << n_bits) - 1;
+ c->tile[c->n_tile_data].end = n_tiles - 1;
}
return dav1d_flush_get_bits(gb) - init_ptr;
@@ -1023,7 +1026,7 @@
for (int n = 0; n < c->n_tile_data; n++)
dav1d_data_unref(&c->tile[n].data);
c->n_tile_data = 0;
- c->tile_mask = 0;
+ c->n_tiles = 0;
if (type == OBU_FRAME_HDR) break;
off += res;
// fall-through
@@ -1039,25 +1042,19 @@
c->tile[c->n_tile_data].data.ref = in->ref;
c->tile[c->n_tile_data].data.data = in->data + off;
c->tile[c->n_tile_data].data.sz = len + init_off - off;
- if (c->tile[c->n_tile_data].start > c->tile[c->n_tile_data].end) {
+ if (c->tile[c->n_tile_data].start > c->tile[c->n_tile_data].end ||
+ (c->n_tile_data > 0 &&
+ (c->tile[c->n_tile_data].start !=
+ c->tile[c->n_tile_data - 1].end + 1)))
+ {
for (int i = 0; i <= c->n_tile_data; i++)
dav1d_data_unref(&c->tile[i].data);
c->n_tile_data = 0;
- c->tile_mask = 0;
+ c->n_tiles = 0;
goto error;
}
-#define mask(a) ((1 << (a)) - 1)
- const unsigned tile_mask = mask(c->tile[c->n_tile_data].end + 1) -
- mask(c->tile[c->n_tile_data].start);
-#undef mask
- if (tile_mask & c->tile_mask) { // tile overlap
- for (int i = 0; i <= c->n_tile_data; i++)
- dav1d_data_unref(&c->tile[i].data);
- c->n_tile_data = 0;
- c->tile_mask = 0;
- goto error;
- }
- c->tile_mask |= tile_mask;
+ c->n_tiles += 1 + c->tile[c->n_tile_data].end -
+ c->tile[c->n_tile_data].start;
c->n_tile_data++;
break;
case OBU_PADDING:
@@ -1070,10 +1067,8 @@
return -EINVAL;
}
- const int n_tiles = 1 << (c->frame_hdr.tiling.log2_cols +
- c->frame_hdr.tiling.log2_rows);
if (c->have_seq_hdr && c->have_frame_hdr &&
- c->tile_mask == (1U << n_tiles) - 1)
+ c->n_tiles == c->frame_hdr.tiling.cols * c->frame_hdr.tiling.rows)
{
if (!c->n_tile_data)
return -EINVAL;
@@ -1081,7 +1076,7 @@
return res;
assert(!c->n_tile_data);
c->have_frame_hdr = 0;
- c->tile_mask = 0;
+ c->n_tiles = 0;
} else if (c->have_seq_hdr && c->have_frame_hdr &&
c->frame_hdr.show_existing_frame)
{