Skip to content

Commit 74edf93

Browse files
committed
uncompressed: add sanity limit check on number of tile rows and cols
1 parent 7a8c58e commit 74edf93

File tree

3 files changed

+153
-2
lines changed

3 files changed

+153
-2
lines changed

libheif/codecs/uncompressed_box.cc

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include <cassert>
2828

2929
#include "libheif/heif.h"
30+
#include "security_limits.h"
3031
#include "uncompressed.h"
3132
#include "uncompressed_box.h"
3233

@@ -260,9 +261,19 @@ Error Box_uncC::parse(BitstreamRange& range)
260261

261262
m_tile_align_size = range.read32();
262263

263-
m_num_tile_cols = range.read32() + 1;
264+
uint32_t num_tile_cols_minus_one = range.read32();
265+
uint32_t num_tile_rows_minus_one = range.read32();
266+
if ((num_tile_cols_minus_one >= MAX_TILE_GRID_ROW_OR_COLUMN) || (num_tile_rows_minus_one >= MAX_TILE_GRID_ROW_OR_COLUMN)) {
267+
std::stringstream sstr;
268+
sstr << "Tiling size " << (num_tile_cols_minus_one + 1) << " x " << (num_tile_rows_minus_one + 1) << " exceeds the maximum allowed size "
269+
<< MAX_TILE_GRID_ROW_OR_COLUMN << " x " << MAX_TILE_GRID_ROW_OR_COLUMN;
270+
return Error(heif_error_Memory_allocation_error,
271+
heif_suberror_Security_limit_exceeded,
272+
sstr.str());
273+
}
274+
m_num_tile_cols = num_tile_cols_minus_one + 1;
264275

265-
m_num_tile_rows = range.read32() + 1;
276+
m_num_tile_rows = num_tile_rows_minus_one + 1;
266277
}
267278
return range.get_error();
268279
}

libheif/security_limits.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ static const int MAX_COLOR_PROFILE_SIZE = 100 * 1024 * 1024; // 100 MB
3434
static const int MAX_IMAGE_WIDTH = 32768;
3535
static const int MAX_IMAGE_HEIGHT = 32768;
3636

37+
// Artificial limit on how many tiles or grid items (TODO) in either
38+
// row direction or column direction
39+
static const int MAX_TILE_GRID_ROW_OR_COLUMN = 32768;
40+
3741
// Maximum nesting level of boxes in input files.
3842
// We put a limit on this to avoid unlimited stack usage by malicious input files.
3943
static const int MAX_BOX_NESTING_LEVEL = 20;

tests/uncompressed_box.cc

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,3 +216,139 @@ TEST_CASE( "uncC" )
216216
std::string dump_output = uncC->dump(indent);
217217
REQUIRE(dump_output == "Box: uncC -----\nsize: 0 (header size: 0)\nprofile: 1919378017 (rgba)\ncomponent_index: 0\ncomponent_bit_depth: 8\ncomponent_format: unsigned\ncomponent_align_size: 0\ncomponent_index: 1\ncomponent_bit_depth: 8\ncomponent_format: unsigned\ncomponent_align_size: 0\ncomponent_index: 2\ncomponent_bit_depth: 8\ncomponent_format: unsigned\ncomponent_align_size: 0\ncomponent_index: 3\ncomponent_bit_depth: 8\ncomponent_format: unsigned\ncomponent_align_size: 0\nsampling_type: no subsampling\ninterleave_type: pixel\nblock_size: 0\ncomponents_little_endian: 0\nblock_pad_lsb: 0\nblock_little_endian: 0\nblock_reversed: 0\npad_unknown: 0\npixel_size: 0\nrow_align_size: 0\ntile_align_size: 0\nnum_tile_cols: 1\nnum_tile_rows: 1\n");
218218
}
219+
220+
TEST_CASE("uncC_parse") {
221+
std::vector<uint8_t> byteArray{
222+
0x00, 0x00, 0x00, 0x40, 'u', 'n', 'c', 'C',
223+
0x00, 0x00, 0x00, 0x00, 'r', 'g', 'b', 'a',
224+
0x00, 0x00, 0x00, 0x04, 0, 0, 7, 0x00,
225+
0x00, 0x00, 0x01, 0x07, 0x00, 0x00, 0x00, 0x02,
226+
0x07, 0x00, 0x00, 0x00, 0x03, 0x07, 0x00, 0x00,
227+
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
228+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
229+
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02
230+
};
231+
232+
auto reader = std::make_shared<StreamReader_memory>(byteArray.data(),
233+
byteArray.size(), false);
234+
235+
BitstreamRange range(reader, byteArray.size());
236+
std::shared_ptr<Box> box;
237+
Error error = Box::read(range, &box);
238+
REQUIRE(error == Error::Ok);
239+
REQUIRE(range.error() == 0);
240+
241+
REQUIRE(box->get_short_type() == fourcc("uncC"));
242+
REQUIRE(box->get_type_string() == "uncC");
243+
std::shared_ptr<Box_uncC> uncC = std::dynamic_pointer_cast<Box_uncC>(box);
244+
REQUIRE(uncC->get_number_of_tile_columns() == 2);
245+
REQUIRE(uncC->get_number_of_tile_rows() == 3);
246+
Indent indent;
247+
std::string dumpResult = box->dump(indent);
248+
REQUIRE(dumpResult == "Box: uncC -----\n"
249+
"size: 64 (header size: 12)\n"
250+
"profile: 1919378017 (rgba)\n"
251+
"component_index: 0\n"
252+
"component_bit_depth: 8\n"
253+
"component_format: unsigned\n"
254+
"component_align_size: 0\n"
255+
"component_index: 1\n"
256+
"component_bit_depth: 8\n"
257+
"component_format: unsigned\n"
258+
"component_align_size: 0\n"
259+
"component_index: 2\n"
260+
"component_bit_depth: 8\n"
261+
"component_format: unsigned\n"
262+
"component_align_size: 0\n"
263+
"component_index: 3\n"
264+
"component_bit_depth: 8\n"
265+
"component_format: unsigned\n"
266+
"component_align_size: 0\n"
267+
"sampling_type: no subsampling\n"
268+
"interleave_type: pixel\n"
269+
"block_size: 0\n"
270+
"components_little_endian: 0\n"
271+
"block_pad_lsb: 0\n"
272+
"block_little_endian: 0\n"
273+
"block_reversed: 0\n"
274+
"pad_unknown: 0\n"
275+
"pixel_size: 0\n"
276+
"row_align_size: 0\n"
277+
"tile_align_size: 0\n"
278+
"num_tile_cols: 2\n"
279+
"num_tile_rows: 3\n");
280+
}
281+
282+
TEST_CASE("uncC_parse_no_overflow") {
283+
std::vector<uint8_t> byteArray{
284+
0x00, 0x00, 0x00, 0x40, 'u', 'n', 'c', 'C',
285+
0x00, 0x00, 0x00, 0x00, 'r', 'g', 'b', 'a',
286+
0x00, 0x00, 0x00, 0x04, 0, 0, 7, 0x00,
287+
0x00, 0x00, 0x01, 0x07, 0x00, 0x00, 0x00, 0x02,
288+
0x07, 0x00, 0x00, 0x00, 0x03, 0x07, 0x00, 0x00,
289+
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
290+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
291+
0x00, 0x00, 0x7f, 0xff, 0x00, 0x00, 0x7f, 0xff
292+
};
293+
294+
auto reader = std::make_shared<StreamReader_memory>(byteArray.data(),
295+
byteArray.size(), false);
296+
297+
BitstreamRange range(reader, byteArray.size());
298+
std::shared_ptr<Box> box;
299+
Error error = Box::read(range, &box);
300+
REQUIRE(error == Error::Ok);
301+
REQUIRE(range.error() == 0);
302+
303+
REQUIRE(box->get_short_type() == fourcc("uncC"));
304+
REQUIRE(box->get_type_string() == "uncC");
305+
std::shared_ptr<Box_uncC> uncC = std::dynamic_pointer_cast<Box_uncC>(box);
306+
REQUIRE(uncC->get_number_of_tile_columns() == 32768);
307+
REQUIRE(uncC->get_number_of_tile_rows() == 32768);
308+
}
309+
310+
TEST_CASE("uncC_parse_excess_tile_cols") {
311+
std::vector<uint8_t> byteArray{
312+
0x00, 0x00, 0x00, 0x40, 'u', 'n', 'c', 'C',
313+
0x00, 0x00, 0x00, 0x00, 'r', 'g', 'b', 'a',
314+
0x00, 0x00, 0x00, 0x04, 0, 0, 7, 0x00,
315+
0x00, 0x00, 0x01, 0x07, 0x00, 0x00, 0x00, 0x02,
316+
0x07, 0x00, 0x00, 0x00, 0x03, 0x07, 0x00, 0x00,
317+
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
318+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
319+
0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x7f, 0xff
320+
};
321+
322+
auto reader = std::make_shared<StreamReader_memory>(byteArray.data(),
323+
byteArray.size(), false);
324+
BitstreamRange range(reader, byteArray.size());
325+
std::shared_ptr<Box> box;
326+
Error error = Box::read(range, &box);
327+
REQUIRE(range.error() == 0);
328+
REQUIRE(error.error_code == 6);
329+
REQUIRE(error.sub_error_code == 1000);
330+
REQUIRE(error.message == "Tiling size 32769 x 32768 exceeds the maximum allowed size 32768 x 32768");
331+
}
332+
333+
TEST_CASE("uncC_parse_excess_tile_rows") {
334+
std::vector<uint8_t> byteArray{
335+
0x00, 0x00, 0x00, 0x40, 'u', 'n', 'c', 'C',
336+
0x00, 0x00, 0x00, 0x00, 'r', 'g', 'b', 'a',
337+
0x00, 0x00, 0x00, 0x04, 0, 0, 7, 0x00,
338+
0x00, 0x00, 0x01, 0x07, 0x00, 0x00, 0x00, 0x02,
339+
0x07, 0x00, 0x00, 0x00, 0x03, 0x07, 0x00, 0x00,
340+
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
341+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
342+
0x00, 0x00, 0x7f, 0xff, 0x00, 0x00, 0x80, 0x00
343+
};
344+
345+
auto reader = std::make_shared<StreamReader_memory>(byteArray.data(),
346+
byteArray.size(), false);
347+
BitstreamRange range(reader, byteArray.size());
348+
std::shared_ptr<Box> box;
349+
Error error = Box::read(range, &box);
350+
REQUIRE(range.error() == 0);
351+
REQUIRE(error.error_code == 6);
352+
REQUIRE(error.sub_error_code == 1000);
353+
REQUIRE(error.message == "Tiling size 32768 x 32769 exceeds the maximum allowed size 32768 x 32768");
354+
}

0 commit comments

Comments
 (0)