Skip to content

Commit

Permalink
uncompressed: add sanity limit check on number of tile rows and cols
Browse files Browse the repository at this point in the history
  • Loading branch information
bradh committed Jul 5, 2024
1 parent 9a8f1e1 commit bd5748f
Show file tree
Hide file tree
Showing 2 changed files with 149 additions and 2 deletions.
15 changes: 13 additions & 2 deletions libheif/codecs/uncompressed_box.cc
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include <cassert>

#include "libheif/heif.h"
#include "security_limits.h"
#include "uncompressed.h"
#include "uncompressed_box.h"

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

m_tile_align_size = range.read32();

m_num_tile_cols = range.read32() + 1;
uint32_t num_tile_cols_minus_one = range.read32();
uint32_t num_tile_rows_minus_one = range.read32();
if ((num_tile_cols_minus_one >= UINT32_MAX) || (num_tile_rows_minus_one >= UINT32_MAX)) {
std::stringstream sstr;
sstr << "Tiling size " << ((uint64_t)num_tile_cols_minus_one + 1) << " x " << ((uint64_t)num_tile_rows_minus_one + 1) << " exceeds the maximum allowed size "
<< UINT32_MAX << " x " << UINT32_MAX;
return Error(heif_error_Memory_allocation_error,
heif_suberror_Security_limit_exceeded,
sstr.str());
}
m_num_tile_cols = num_tile_cols_minus_one + 1;

m_num_tile_rows = range.read32() + 1;
m_num_tile_rows = num_tile_rows_minus_one + 1;
}
return range.get_error();
}
Expand Down
136 changes: 136 additions & 0 deletions tests/uncompressed_box.cc
Original file line number Diff line number Diff line change
Expand Up @@ -216,3 +216,139 @@ TEST_CASE( "uncC" )
std::string dump_output = uncC->dump(indent);
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");
}

TEST_CASE("uncC_parse") {
std::vector<uint8_t> byteArray{
0x00, 0x00, 0x00, 0x40, 'u', 'n', 'c', 'C',
0x00, 0x00, 0x00, 0x00, 'r', 'g', 'b', 'a',
0x00, 0x00, 0x00, 0x04, 0, 0, 7, 0x00,
0x00, 0x00, 0x01, 0x07, 0x00, 0x00, 0x00, 0x02,
0x07, 0x00, 0x00, 0x00, 0x03, 0x07, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02
};

auto reader = std::make_shared<StreamReader_memory>(byteArray.data(),
byteArray.size(), false);

BitstreamRange range(reader, byteArray.size());
std::shared_ptr<Box> box;
Error error = Box::read(range, &box);
REQUIRE(error == Error::Ok);
REQUIRE(range.error() == 0);

REQUIRE(box->get_short_type() == fourcc("uncC"));
REQUIRE(box->get_type_string() == "uncC");
std::shared_ptr<Box_uncC> uncC = std::dynamic_pointer_cast<Box_uncC>(box);
REQUIRE(uncC->get_number_of_tile_columns() == 2);
REQUIRE(uncC->get_number_of_tile_rows() == 3);
Indent indent;
std::string dumpResult = box->dump(indent);
REQUIRE(dumpResult == "Box: uncC -----\n"
"size: 64 (header size: 12)\n"
"profile: 1919378017 (rgba)\n"
"component_index: 0\n"
"component_bit_depth: 8\n"
"component_format: unsigned\n"
"component_align_size: 0\n"
"component_index: 1\n"
"component_bit_depth: 8\n"
"component_format: unsigned\n"
"component_align_size: 0\n"
"component_index: 2\n"
"component_bit_depth: 8\n"
"component_format: unsigned\n"
"component_align_size: 0\n"
"component_index: 3\n"
"component_bit_depth: 8\n"
"component_format: unsigned\n"
"component_align_size: 0\n"
"sampling_type: no subsampling\n"
"interleave_type: pixel\n"
"block_size: 0\n"
"components_little_endian: 0\n"
"block_pad_lsb: 0\n"
"block_little_endian: 0\n"
"block_reversed: 0\n"
"pad_unknown: 0\n"
"pixel_size: 0\n"
"row_align_size: 0\n"
"tile_align_size: 0\n"
"num_tile_cols: 2\n"
"num_tile_rows: 3\n");
}

TEST_CASE("uncC_parse_no_overflow") {
std::vector<uint8_t> byteArray{
0x00, 0x00, 0x00, 0x40, 'u', 'n', 'c', 'C',
0x00, 0x00, 0x00, 0x00, 'r', 'g', 'b', 'a',
0x00, 0x00, 0x00, 0x04, 0, 0, 7, 0x00,
0x00, 0x00, 0x01, 0x07, 0x00, 0x00, 0x00, 0x02,
0x07, 0x00, 0x00, 0x00, 0x03, 0x07, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe
};

auto reader = std::make_shared<StreamReader_memory>(byteArray.data(),
byteArray.size(), false);

BitstreamRange range(reader, byteArray.size());
std::shared_ptr<Box> box;
Error error = Box::read(range, &box);
REQUIRE(error == Error::Ok);
REQUIRE(range.error() == 0);

REQUIRE(box->get_short_type() == fourcc("uncC"));
REQUIRE(box->get_type_string() == "uncC");
std::shared_ptr<Box_uncC> uncC = std::dynamic_pointer_cast<Box_uncC>(box);
REQUIRE(uncC->get_number_of_tile_columns() == 4294967295);
REQUIRE(uncC->get_number_of_tile_rows() == 4294967295);
}

TEST_CASE("uncC_parse_excess_tile_cols") {
std::vector<uint8_t> byteArray{
0x00, 0x00, 0x00, 0x40, 'u', 'n', 'c', 'C',
0x00, 0x00, 0x00, 0x00, 'r', 'g', 'b', 'a',
0x00, 0x00, 0x00, 0x04, 0, 0, 7, 0x00,
0x00, 0x00, 0x01, 0x07, 0x00, 0x00, 0x00, 0x02,
0x07, 0x00, 0x00, 0x00, 0x03, 0x07, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x7f, 0xff
};

auto reader = std::make_shared<StreamReader_memory>(byteArray.data(),
byteArray.size(), false);
BitstreamRange range(reader, byteArray.size());
std::shared_ptr<Box> box;
Error error = Box::read(range, &box);
REQUIRE(range.error() == 0);
REQUIRE(error.error_code == 6);
REQUIRE(error.sub_error_code == 1000);
REQUIRE(error.message == "Tiling size 4294967296 x 32768 exceeds the maximum allowed size 4294967295 x 4294967295");
}

TEST_CASE("uncC_parse_excess_tile_rows") {
std::vector<uint8_t> byteArray{
0x00, 0x00, 0x00, 0x40, 'u', 'n', 'c', 'C',
0x00, 0x00, 0x00, 0x00, 'r', 'g', 'b', 'a',
0x00, 0x00, 0x00, 0x04, 0, 0, 7, 0x00,
0x00, 0x00, 0x01, 0x07, 0x00, 0x00, 0x00, 0x02,
0x07, 0x00, 0x00, 0x00, 0x03, 0x07, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff
};

auto reader = std::make_shared<StreamReader_memory>(byteArray.data(),
byteArray.size(), false);
BitstreamRange range(reader, byteArray.size());
std::shared_ptr<Box> box;
Error error = Box::read(range, &box);
REQUIRE(range.error() == 0);
REQUIRE(error.error_code == 6);
REQUIRE(error.sub_error_code == 1000);
REQUIRE(error.message == "Tiling size 32768 x 4294967296 exceeds the maximum allowed size 4294967295 x 4294967295");
}

0 comments on commit bd5748f

Please sign in to comment.