Commit fce177d8 authored by strobe@google.com's avatar strobe@google.com

Set error on unrecognized top-level BMFF boxes.

A common error when using BMFF with Media Source is to begin appending at an
incorrect offset, such that the parser sees an extremely large value for the
box size and consequently waits forever for input without failing. This change
adds a whitelist of permitted top-level boxes, which allows these situations
to be detected as soon as the header is read.

BUG=
TEST=BoxReaderTest.WrongFourCCTest


Review URL: https://chromiumcodereview.appspot.com/10823139

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@149735 0039d316-1c4b-4281-b951-d872f2087c98
parent 3426ce19
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include <set> #include <set>
#include "base/logging.h" #include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "media/mp4/box_definitions.h" #include "media/mp4/box_definitions.h"
#include "media/mp4/rcheck.h" #include "media/mp4/rcheck.h"
...@@ -95,14 +96,22 @@ BoxReader::~BoxReader() { ...@@ -95,14 +96,22 @@ BoxReader::~BoxReader() {
} }
} }
// static
BoxReader* BoxReader::ReadTopLevelBox(const uint8* buf, BoxReader* BoxReader::ReadTopLevelBox(const uint8* buf,
const int buf_size, const int buf_size,
bool* err) { bool* err) {
BoxReader* reader = new BoxReader(buf, buf_size); scoped_ptr<BoxReader> reader(new BoxReader(buf, buf_size));
if (reader->ReadHeader(err) && reader->size() <= buf_size) { if (!reader->ReadHeader(err))
return reader; return NULL;
if (!IsValidTopLevelBox(reader->type())) {
*err = true;
return NULL;
} }
delete reader;
if (reader->size() <= buf_size)
return reader.release();
return NULL; return NULL;
} }
...@@ -114,11 +123,41 @@ bool BoxReader::StartTopLevelBox(const uint8* buf, ...@@ -114,11 +123,41 @@ bool BoxReader::StartTopLevelBox(const uint8* buf,
bool* err) { bool* err) {
BoxReader reader(buf, buf_size); BoxReader reader(buf, buf_size);
if (!reader.ReadHeader(err)) return false; if (!reader.ReadHeader(err)) return false;
if (!IsValidTopLevelBox(reader.type())) {
*err = true;
return false;
}
*type = reader.type(); *type = reader.type();
*box_size = reader.size(); *box_size = reader.size();
return true; return true;
} }
// static
bool BoxReader::IsValidTopLevelBox(const FourCC& type) {
switch (type) {
case FOURCC_FTYP:
case FOURCC_PDIN:
case FOURCC_MOOV:
case FOURCC_MOOF:
case FOURCC_MFRA:
case FOURCC_MDAT:
case FOURCC_FREE:
case FOURCC_SKIP:
case FOURCC_META:
case FOURCC_MECO:
case FOURCC_STYP:
case FOURCC_SIDX:
case FOURCC_SSIX:
case FOURCC_PRFT:
return true;
default:
// Hex is used to show nonprintable characters and aid in debugging
LOG(WARNING) << "Unrecognized top-level box type 0x"
<< std::hex << type;
return false;
}
}
bool BoxReader::ScanChildren() { bool BoxReader::ScanChildren() {
DCHECK(!scanned_); DCHECK(!scanned_);
scanned_ = true; scanned_ = true;
......
...@@ -91,6 +91,11 @@ class MEDIA_EXPORT BoxReader : public BufferReader { ...@@ -91,6 +91,11 @@ class MEDIA_EXPORT BoxReader : public BufferReader {
int* box_size, int* box_size,
bool* err) WARN_UNUSED_RESULT; bool* err) WARN_UNUSED_RESULT;
// Returns true if |type| is recognized to be a top-level box, false
// otherwise. This returns true for some boxes which we do not parse.
// Helpful in debugging misaligned appends.
static bool IsValidTopLevelBox(const FourCC& type);
// Scan through all boxes within the current box, starting at the current // Scan through all boxes within the current box, starting at the current
// buffer position. Must be called before any of the *Child functions work. // buffer position. Must be called before any of the *Child functions work.
bool ScanChildren() WARN_UNUSED_RESULT; bool ScanChildren() WARN_UNUSED_RESULT;
......
...@@ -14,46 +14,46 @@ ...@@ -14,46 +14,46 @@
namespace media { namespace media {
namespace mp4 { namespace mp4 {
static const uint8 kTestBox[] = { static const uint8 kSkipBox[] = {
// Test box containing three children // Top-level test box containing three children
0x00, 0x00, 0x00, 0x40, 't', 'e', 's', 't', 0x00, 0x00, 0x00, 0x40, 's', 'k', 'i', 'p',
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0xf9, 0x0a, 0x0b, 0x0c, 0xfd, 0x0e, 0x0f, 0x10, 0xf9, 0x0a, 0x0b, 0x0c, 0xfd, 0x0e, 0x0f, 0x10,
// Ordinary child box // Ordinary (8-byte header) child box
0x00, 0x00, 0x00, 0x0c, 'c', 'h', 'l', 'd', 0xde, 0xad, 0xbe, 0xef, 0x00, 0x00, 0x00, 0x0c, 'p', 's', 's', 'h', 0xde, 0xad, 0xbe, 0xef,
// Extended-size child box // Extended-size header child box
0x00, 0x00, 0x00, 0x01, 'c', 'h', 'l', 'd', 0x00, 0x00, 0x00, 0x01, 'p', 's', 's', 'h',
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14,
0xfa, 0xce, 0xca, 0xfe, 0xfa, 0xce, 0xca, 0xfe,
// Empty box // Empty free box
0x00, 0x00, 0x00, 0x08, 'm', 'p', 't', 'y', 0x00, 0x00, 0x00, 0x08, 'f', 'r', 'e', 'e',
// Trailing garbage // Trailing garbage
0x00 }; 0x00 };
struct EmptyBox : Box { struct FreeBox : Box {
virtual bool Parse(BoxReader* reader) OVERRIDE { virtual bool Parse(BoxReader* reader) OVERRIDE {
return true; return true;
} }
virtual FourCC BoxType() const OVERRIDE { return FOURCC_MPTY; } virtual FourCC BoxType() const OVERRIDE { return FOURCC_FREE; }
}; };
struct ChildBox : Box { struct PsshBox : Box {
uint32 val; uint32 val;
virtual bool Parse(BoxReader* reader) OVERRIDE { virtual bool Parse(BoxReader* reader) OVERRIDE {
return reader->Read4(&val); return reader->Read4(&val);
} }
virtual FourCC BoxType() const OVERRIDE { return FOURCC_CHLD; } virtual FourCC BoxType() const OVERRIDE { return FOURCC_PSSH; }
}; };
struct TestBox : Box { struct SkipBox : Box {
uint8 a, b; uint8 a, b;
uint16 c; uint16 c;
int32 d; int32 d;
int64 e; int64 e;
std::vector<ChildBox> kids; std::vector<PsshBox> kids;
EmptyBox mpty; FreeBox mpty;
virtual bool Parse(BoxReader* reader) OVERRIDE { virtual bool Parse(BoxReader* reader) OVERRIDE {
RCHECK(reader->ReadFullBoxHeader() && RCHECK(reader->ReadFullBoxHeader() &&
...@@ -66,19 +66,19 @@ struct TestBox : Box { ...@@ -66,19 +66,19 @@ struct TestBox : Box {
reader->ReadChildren(&kids) && reader->ReadChildren(&kids) &&
reader->MaybeReadChild(&mpty); reader->MaybeReadChild(&mpty);
} }
virtual FourCC BoxType() const OVERRIDE { return FOURCC_TEST; } virtual FourCC BoxType() const OVERRIDE { return FOURCC_SKIP; }
TestBox(); SkipBox();
~TestBox(); ~SkipBox();
}; };
TestBox::TestBox() {} SkipBox::SkipBox() {}
TestBox::~TestBox() {} SkipBox::~SkipBox() {}
class BoxReaderTest : public testing::Test { class BoxReaderTest : public testing::Test {
protected: protected:
std::vector<uint8> GetBuf() { std::vector<uint8> GetBuf() {
return std::vector<uint8>(kTestBox, kTestBox + sizeof(kTestBox)); return std::vector<uint8>(kSkipBox, kSkipBox + sizeof(kSkipBox));
} }
}; };
...@@ -90,7 +90,7 @@ TEST_F(BoxReaderTest, ExpectedOperationTest) { ...@@ -90,7 +90,7 @@ TEST_F(BoxReaderTest, ExpectedOperationTest) {
EXPECT_FALSE(err); EXPECT_FALSE(err);
EXPECT_TRUE(reader.get()); EXPECT_TRUE(reader.get());
TestBox box; SkipBox box;
EXPECT_TRUE(box.Parse(reader.get())); EXPECT_TRUE(box.Parse(reader.get()));
EXPECT_EQ(0x01, reader->version()); EXPECT_EQ(0x01, reader->version());
EXPECT_EQ(0x020304u, reader->flags()); EXPECT_EQ(0x020304u, reader->flags());
...@@ -129,7 +129,7 @@ TEST_F(BoxReaderTest, InnerTooLongTest) { ...@@ -129,7 +129,7 @@ TEST_F(BoxReaderTest, InnerTooLongTest) {
scoped_ptr<BoxReader> reader( scoped_ptr<BoxReader> reader(
BoxReader::ReadTopLevelBox(&buf[0], buf.size(), &err)); BoxReader::ReadTopLevelBox(&buf[0], buf.size(), &err));
TestBox box; SkipBox box;
EXPECT_FALSE(box.Parse(reader.get())); EXPECT_FALSE(box.Parse(reader.get()));
} }
...@@ -137,23 +137,12 @@ TEST_F(BoxReaderTest, WrongFourCCTest) { ...@@ -137,23 +137,12 @@ TEST_F(BoxReaderTest, WrongFourCCTest) {
std::vector<uint8> buf = GetBuf(); std::vector<uint8> buf = GetBuf();
bool err; bool err;
// Use an unknown FourCC both on an outer box and an inner one. // Set an unrecognized top-level FourCC.
buf[5] = 1; buf[5] = 1;
buf[28] = 1;
scoped_ptr<BoxReader> reader( scoped_ptr<BoxReader> reader(
BoxReader::ReadTopLevelBox(&buf[0], buf.size(), &err)); BoxReader::ReadTopLevelBox(&buf[0], buf.size(), &err));
EXPECT_FALSE(reader.get());
TestBox box; EXPECT_TRUE(err);
std::vector<ChildBox> kids;
// This should still work; the outer box reader doesn't care about the FourCC,
// since it assumes you've already examined it before deciding what to parse.
EXPECT_TRUE(box.Parse(reader.get()));
EXPECT_EQ(0x74017374, reader->type());
// Parsing the TestBox should have left the modified inner box unread, which
// we collect here.
EXPECT_TRUE(reader->ReadAllChildren(&kids));
EXPECT_EQ(1u, kids.size());
EXPECT_EQ(0xdeadbeef, kids[0].val);
} }
TEST_F(BoxReaderTest, ChildrenTest) { TEST_F(BoxReaderTest, ChildrenTest) {
...@@ -164,12 +153,12 @@ TEST_F(BoxReaderTest, ChildrenTest) { ...@@ -164,12 +153,12 @@ TEST_F(BoxReaderTest, ChildrenTest) {
EXPECT_TRUE(reader->SkipBytes(16) && reader->ScanChildren()); EXPECT_TRUE(reader->SkipBytes(16) && reader->ScanChildren());
EmptyBox mpty; FreeBox free;
EXPECT_TRUE(reader->ReadChild(&mpty)); EXPECT_TRUE(reader->ReadChild(&free));
EXPECT_FALSE(reader->ReadChild(&mpty)); EXPECT_FALSE(reader->ReadChild(&free));
EXPECT_TRUE(reader->MaybeReadChild(&mpty)); EXPECT_TRUE(reader->MaybeReadChild(&free));
std::vector<ChildBox> kids; std::vector<PsshBox> kids;
EXPECT_TRUE(reader->ReadAllChildren(&kids)); EXPECT_TRUE(reader->ReadAllChildren(&kids));
EXPECT_EQ(2u, kids.size()); EXPECT_EQ(2u, kids.size());
......
...@@ -32,7 +32,9 @@ enum FourCC { ...@@ -32,7 +32,9 @@ enum FourCC {
FOURCC_MDAT = 0x6d646174, FOURCC_MDAT = 0x6d646174,
FOURCC_MDHD = 0x6d646864, FOURCC_MDHD = 0x6d646864,
FOURCC_MDIA = 0x6d646961, FOURCC_MDIA = 0x6d646961,
FOURCC_MECO = 0x6d65636f,
FOURCC_MEHD = 0x6d656864, FOURCC_MEHD = 0x6d656864,
FOURCC_META = 0x6d657461,
FOURCC_MFHD = 0x6d666864, FOURCC_MFHD = 0x6d666864,
FOURCC_MFRA = 0x6d667261, FOURCC_MFRA = 0x6d667261,
FOURCC_MINF = 0x6d696e66, FOURCC_MINF = 0x6d696e66,
...@@ -43,6 +45,8 @@ enum FourCC { ...@@ -43,6 +45,8 @@ enum FourCC {
FOURCC_MVEX = 0x6d766578, FOURCC_MVEX = 0x6d766578,
FOURCC_MVHD = 0x6d766864, FOURCC_MVHD = 0x6d766864,
FOURCC_PASP = 0x70617370, FOURCC_PASP = 0x70617370,
FOURCC_PDIN = 0x7064696e,
FOURCC_PRFT = 0x70726674,
FOURCC_PSSH = 0x70737368, FOURCC_PSSH = 0x70737368,
FOURCC_SAIO = 0x7361696f, FOURCC_SAIO = 0x7361696f,
FOURCC_SAIZ = 0x7361697a, FOURCC_SAIZ = 0x7361697a,
...@@ -54,6 +58,7 @@ enum FourCC { ...@@ -54,6 +58,7 @@ enum FourCC {
FOURCC_SKIP = 0x736b6970, FOURCC_SKIP = 0x736b6970,
FOURCC_SMHD = 0x736d6864, FOURCC_SMHD = 0x736d6864,
FOURCC_SOUN = 0x736f756e, FOURCC_SOUN = 0x736f756e,
FOURCC_SSIX = 0x73736978,
FOURCC_STBL = 0x7374626c, FOURCC_STBL = 0x7374626c,
FOURCC_STCO = 0x7374636f, FOURCC_STCO = 0x7374636f,
FOURCC_STSC = 0x73747363, FOURCC_STSC = 0x73747363,
...@@ -75,11 +80,6 @@ enum FourCC { ...@@ -75,11 +80,6 @@ enum FourCC {
FOURCC_VIDE = 0x76696465, FOURCC_VIDE = 0x76696465,
FOURCC_VMHD = 0x766d6864, FOURCC_VMHD = 0x766d6864,
FOURCC_WIDE = 0x77696465, FOURCC_WIDE = 0x77696465,
// The following are used for testing
FOURCC_CHLD = 0x63686c64,
FOURCC_MPTY = 0x6d707479,
FOURCC_TEST = 0x74657374,
}; };
const inline std::string FourCCToString(FourCC fourcc) { const inline std::string FourCCToString(FourCC fourcc) {
......
...@@ -462,7 +462,7 @@ bool MP4StreamParser::ReadAndDiscardMDATsUntil(const int64 offset) { ...@@ -462,7 +462,7 @@ bool MP4StreamParser::ReadAndDiscardMDATsUntil(const int64 offset) {
break; break;
if (type != FOURCC_MDAT) { if (type != FOURCC_MDAT) {
DLOG(WARNING) << "Unexpected type while parsing MDATs: " DLOG(WARNING) << "Unexpected box type while parsing MDATs: "
<< FourCCToString(type); << FourCCToString(type);
} }
mdat_tail_ += box_sz; mdat_tail_ += box_sz;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment