Index: media/formats/mp4/box_reader_unittest.cc |
diff --git a/media/formats/mp4/box_reader_unittest.cc b/media/formats/mp4/box_reader_unittest.cc |
index 09df3d7f3d36c3f88a5b6da421ff559512435442..818d80767fdd2766111275f1cc43fd2ed12ef162 100644 |
--- a/media/formats/mp4/box_reader_unittest.cc |
+++ b/media/formats/mp4/box_reader_unittest.cc |
@@ -103,6 +103,51 @@ class BoxReaderTest : public testing::Test { |
EXPECT_EQ(reader->box_size(), data_size); |
} |
+ template <typename ChildType> |
+ void TestParsing32bitOverflow(const uint8_t* buffer, |
+ size_t size, |
+ const std::string& overflow_error) { |
+ // Wrap whatever we're passed in a dummy EMSG so we can satisfy requirements |
+ // for ReadTopLevelBox and to kick off parsing. |
+ std::vector<uint8_t> buffer_wrapper = { |
+ 0x00, 0x00, 0x00, 0x00, // dummy size |
+ 'e', 'm', 's', 'g', // fourcc |
+ }; |
+ buffer_wrapper.insert(buffer_wrapper.end(), buffer, buffer + size); |
+ |
+ // Basic check of the nested buffer size. If box_size > buffer size the test |
+ // will exit early (waiting for more bytes to be appended). |
+ ASSERT_TRUE(base::IsValueInRangeForNumericType<uint8_t>(size)); |
+ ASSERT_LE(buffer[3], size); |
+ |
+ // Update the size (keep it simple). |
+ ASSERT_TRUE( |
+ base::IsValueInRangeForNumericType<uint8_t>(buffer_wrapper.size())); |
+ buffer_wrapper[3] = buffer_wrapper.size(); |
+ |
+ bool err; |
+ std::unique_ptr<BoxReader> reader(BoxReader::ReadTopLevelBox( |
+ &buffer_wrapper[0], buffer_wrapper.size(), media_log_, &err)); |
+ EXPECT_FALSE(err); |
+ EXPECT_TRUE(reader); |
+ EXPECT_EQ(FOURCC_EMSG, reader->type()); |
+ |
+// Overflow is only triggered/caught on 32-bit systems. 64-bit systems will |
+// instead fail parsing because tests are written such that |buffer| never |
+// contains enough bytes for parsing to succeed. |
+#if defined(ARCH_CPU_32_BITS) |
+ const int kOverflowLogCount = 1; |
+#else |
+ const int kOverflowLogCount = 0; |
+#endif |
+ |
+ if (!overflow_error.empty()) |
+ EXPECT_MEDIA_LOG(HasSubstr(overflow_error)).Times(kOverflowLogCount); |
+ |
+ std::vector<ChildType> children; |
+ EXPECT_FALSE(reader->ReadAllChildrenAndCheckFourCC(&children)); |
+ } |
+ |
scoped_refptr<StrictMock<MockMediaLog>> media_log_; |
}; |
@@ -276,13 +321,11 @@ TEST_F(BoxReaderTest, ScanChildrenWithInvalidChild) { |
// The sample specifies a large number of EditListEntry's, but only 1 is |
// actually included in the box. This test verifies that the code checks |
// properly that the buffer contains the specified number of EditListEntry's |
- // (does not cause an int32_t overflow when checking that the bytes are |
- // available, and does not read past the end of the buffer). |
static const uint8_t kData[] = { |
0x00, 0x00, 0x00, 0x2c, 'e', 'm', 's', 'g', // outer box |
0x00, 0x00, 0x00, 0x24, 'e', 'l', 's', 't', // nested box |
0x01, 0x00, 0x00, 0x00, // version = 1, flags = 0 |
- 0xff, 0xff, 0xff, 0xff, // count = max, but only 1 actually included |
+ 0x00, 0x00, 0x00, 0x0a, // count = 10, but only 1 actually included |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; |
@@ -301,36 +344,6 @@ TEST_F(BoxReaderTest, ScanChildrenWithInvalidChild) { |
EXPECT_FALSE(reader->ReadChild(&child)); |
} |
-TEST_F(BoxReaderTest, ReadAllChildrenWithInvalidChild) { |
- // This data is not a valid 'emsg' box. It is just used as a top-level box |
- // as ReadTopLevelBox() has a restricted set of boxes it allows. |
- // The nested 'trun' box is used as it includes a count of the number |
- // of samples. The data specifies a large number of samples, but only 1 |
- // is actually included in the box. Verifying that the large count does not |
- // cause an int32_t overflow which would allow parsing of TrackFragmentRun |
- // to read past the end of the buffer. |
- static const uint8_t kData[] = { |
- 0x00, 0x00, 0x00, 0x28, 'e', 'm', 's', 'g', // outer box |
- 0x00, 0x00, 0x00, 0x20, 't', 'r', 'u', 'n', // nested box |
- 0x00, 0x00, 0x0f, 0x00, // version = 0, flags = samples present |
- 0xff, 0xff, 0xff, 0xff, // count = max, but only 1 actually included |
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; |
- |
- bool err; |
- std::unique_ptr<BoxReader> reader( |
- BoxReader::ReadTopLevelBox(kData, sizeof(kData), media_log_, &err)); |
- |
- EXPECT_FALSE(err); |
- EXPECT_TRUE(reader); |
- EXPECT_EQ(FOURCC_EMSG, reader->type()); |
- |
- // Reading the child should fail since the number of samples specified |
- // doesn't match what is in the box. |
- std::vector<TrackFragmentRun> children; |
- EXPECT_FALSE(reader->ReadAllChildrenAndCheckFourCC(&children)); |
-} |
- |
TEST_F(BoxReaderTest, ReadAllChildrenWithChildLargerThanParent) { |
static const uint8_t kData[] = { |
0x00, 0x00, 0x00, 0x10, 's', 'k', 'i', 'p', // outer box |
@@ -349,5 +362,103 @@ TEST_F(BoxReaderTest, ReadAllChildrenWithChildLargerThanParent) { |
EXPECT_FALSE(reader->ReadAllChildren(&tmp)); |
} |
+TEST_F(BoxReaderTest, TrunSampleCount32bitOverflow) { |
+ // This 'trun' box specifies an unusually high sample count, though only one |
+ // sample is included in the bytes below. The values for "sample_count" and |
+ // "flags" are chosen such that the needed number of bytes will overflow 32 |
+ // bits to yield a very small number (4), potentially passing the |
+ // internal check for HasBytes(). http://crbug.com/679640 |
+ static const uint8_t kData[] = { |
+ 0x00, 0x00, 0x00, 0x18, 't', 'r', 'u', 'n', // header |
+ 0x00, 0x00, // version = 0 |
+ 0x03, 0x00, // flags = 2 fields present (sample duration and sample size) |
+ 0x80, 0x00, 0x00, 0x02, // sample count = 2147483650 |
+ 0x00, 0x00, 0x00, 0x00, // only one sample present |
+ 0x00, 0x00, 0x00, 0x00}; |
+ |
+ // Verify we catch the overflow to avoid OOB reads/writes. |
+ TestParsing32bitOverflow<TrackFragmentRun>( |
+ kData, sizeof(kData), |
+ "Extreme TRUN sample count exceeds implementation limit."); |
+} |
+ |
+TEST_F(BoxReaderTest, SaioCount32bitOverflow) { |
+ // This 'saio' box specifies an unusually high number of offset counts, though |
+ // only one offset is included in the bytes below. The values for "count" and |
+ // "version" are chosen such that the needed number of bytes will overflow 32 |
+ // bits to yield a very small number (4), potentially passing the internal |
+ // check for HasBytes(). http://crbug.com/679641 |
+ static const uint8_t kData[] = { |
+ 0x00, 0x00, 0x00, 0x14, 's', 'a', 'i', 'o', // header |
+ 0x00, 0x00, // version = 0 (4 bytes per offset entry) |
+ 0x00, 0x00, // flags = 0 |
+ 0x40, 0x00, 0x00, 0x01, // offsets count = 1073741825 |
+ 0x00, 0x00, 0x00, 0x00, // single offset entry |
+ }; |
+ |
+ // Verify we catch the overflow to avoid OOB reads/writes. |
+ TestParsing32bitOverflow<SampleAuxiliaryInformationOffset>( |
+ kData, sizeof(kData), "Extreme SAIO count exceeds implementation limit."); |
+} |
+ |
+TEST_F(BoxReaderTest, ElstCount32bitOverflow) { |
+ // This 'elst' box specifies an unusually high number of edit counts, though |
+ // only one edit is included in the bytes below. The values for "count" and |
+ // "version" are chosen such that the needed number of bytes will overflow 32 |
+ // bits to yield a very small number (12), potentially passing the internal |
+ // check for HasBytes(). http://crbug.com/679645 |
+ static const uint8_t kData[] = { |
+ 0x00, 0x00, 0x00, 0x1c, 'e', 'l', 's', 't', // header |
+ 0x00, 0x00, // version = 0 (12 bytes per edit entry) |
+ 0x00, 0x00, // flags = 0 |
+ 0x80, 0x00, 0x00, 0x01, // edits count = 2147483649 |
+ 0x00, 0x00, 0x00, 0x00, // single edit entry |
+ 0x00, 0x00, 0x00, 0x00, // ... |
+ 0x00, 0x00, 0x00, 0x00, |
+ }; |
+ |
+ // Verify we catch the overflow to avoid OOB reads/writes. |
+ TestParsing32bitOverflow<EditList>( |
+ kData, sizeof(kData), "Extreme ELST count exceeds implementation limit."); |
+} |
+ |
+TEST_F(BoxReaderTest, SbgpCount32bitOverflow) { |
+ // This 'sbgp' box specifies an unusually high count of entries, though only |
+ // one partial entry is included in the bytes below. The value for "count" is |
+ // chosen such that we could overflow attempting to allocate the vector for |
+ // parsed entries. http://crbug.com/679646 |
+ static const uint8_t kData[] = { |
+ 0x00, 0x00, 0x00, 0x1c, 's', 'b', 'g', 'p', // header |
+ 0x00, 0x00, 0x00, 0x00, // version = 0, flags = 0 |
+ 's', 'e', 'i', 'g', // required grouping "type" |
+ 0xff, 0xff, 0xff, 0xff, // count = 4294967295 |
+ 0x00, 0x00, 0x00, 0x00, // partial entry |
+ 0x00, 0x00, 0x00, 0x00, |
+ }; |
+ |
+ // Verify we catch the overflow to avoid OOB reads/writes. |
+ TestParsing32bitOverflow<SampleToGroup>( |
+ kData, sizeof(kData), "Extreme SBGP count exceeds implementation limit."); |
+} |
+ |
+TEST_F(BoxReaderTest, SgpdCount32bitOverflow) { |
+ // This 'sgpd' box specifies an unusually high count of entries, though only |
+ // one partial entry is included in the bytes below. The value for "count" is |
+ // chosen such that we could overflow attempting to allocate the vector for |
+ // parsed entries. http://crbug.com/679647 |
+ static const uint8_t kData[] = { |
+ 0x00, 0x00, 0x00, 0x1c, 's', 'g', 'p', 'd', // header |
+ 0x00, 0x00, 0x00, 0x00, // version = 0, flags = 0 |
+ 's', 'e', 'i', 'g', // required grouping "type" |
+ 0xff, 0xff, 0xff, 0xff, // count = 4294967295 |
+ 0x00, 0x00, 0x00, 0x00, // partial entry |
+ 0x00, 0x00, 0x00, 0x00, |
+ }; |
+ |
+ // Verify we catch the overflow to avoid OOB reads/writes. |
+ TestParsing32bitOverflow<SampleGroupDescription>( |
+ kData, sizeof(kData), "Extreme SGPD count exceeds implementation limit."); |
+} |
+ |
} // namespace mp4 |
} // namespace media |