Bug 1116056 - Ensure all atoms read are valid. r=mattwoodrow, a=sledru
authorJean-Yves Avenard <jyavenard@mozilla.com>
Tue, 20 Jan 2015 13:42:27 +1100
changeset 242958 1f392909ff1f
parent 242957 54386fba64a7
child 242959 598cd9c2e480
push id4345
push userryanvm@gmail.com
push date2015-01-20 17:32 +0000
treeherdermozilla-beta@598cd9c2e480 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmattwoodrow, sledru
bugs1116056
milestone36.0
Bug 1116056 - Ensure all atoms read are valid. r=mattwoodrow, a=sledru
media/libstagefright/binding/Box.cpp
media/libstagefright/binding/MoofParser.cpp
media/libstagefright/binding/include/mp4_demuxer/ByteReader.h
media/libstagefright/binding/include/mp4_demuxer/MoofParser.h
--- a/media/libstagefright/binding/Box.cpp
+++ b/media/libstagefright/binding/Box.cpp
@@ -89,17 +89,17 @@ Box::FirstChild() const
   return Box(mContext, mChildOffset, this);
 }
 
 void
 Box::Read(nsTArray<uint8_t>* aDest)
 {
   aDest->SetLength(mRange.mEnd - mChildOffset);
   size_t bytes;
-  if (!mContext->mSource->CachedReadAt(mChildOffset, &(*aDest)[0],
+  if (!mContext->mSource->CachedReadAt(mChildOffset, aDest->Elements(),
                                        aDest->Length(), &bytes) ||
       bytes != aDest->Length()) {
     // Byte ranges are being reported incorrectly
     NS_WARNING("Read failed in mp4_demuxer::Box::Read()");
     aDest->Clear();
   }
 }
 }
--- a/media/libstagefright/binding/MoofParser.cpp
+++ b/media/libstagefright/binding/MoofParser.cpp
@@ -283,31 +283,53 @@ public:
   {
     return aA->mCompositionRange.start < aB->mCompositionRange.start;
   }
 };
 
 void
 Moof::ParseTrun(Box& aBox, Tfhd& aTfhd, Tfdt& aTfdt, Mdhd& aMdhd, Edts& aEdts)
 {
-  if (!aMdhd.mTimescale) {
+  if (!aTfhd.IsValid() || !aTfdt.IsValid() ||
+      !aMdhd.IsValid() || !aEdts.IsValid()) {
     return;
   }
 
   BoxReader reader(aBox);
+  if (!reader->CanReadType<uint32_t>()) {
+    return;
+  }
   uint32_t flags = reader->ReadU32();
   if ((flags & 0x404) == 0x404) {
     // Can't use these flags together
     reader->DiscardRemaining();
+    mValid = true;
     return;
   }
   uint8_t version = flags >> 24;
 
+  if (!reader->CanReadType<uint32_t>()) {
+    return;
+  }
   uint32_t sampleCount = reader->ReadU32();
   if (sampleCount == 0) {
+    mValid = true;
+    return;
+  }
+
+  size_t need =
+    ((flags & 1) ? sizeof(uint32_t) : 0) +
+    ((flags & 4) ? sizeof(uint32_t) : 0);
+  uint16_t flag[] = { 0x100, 0x200, 0x400, 0x800, 0 };
+  for (size_t i = 0; flag[i]; i++) {
+    if (flags & flag[i]) {
+      need += sizeof(uint32_t) * sampleCount;
+    }
+  }
+  if (reader->Remaining() < need) {
     return;
   }
 
   uint64_t offset = aTfhd.mBaseDataOffset + (flags & 1 ? reader->ReadU32() : 0);
   bool hasFirstSampleFlags = flags & 4;
   uint32_t firstSampleFlags = hasFirstSampleFlags ? reader->ReadU32() : 0;
   uint64_t decodeTime = aTfdt.mBaseMediaDecodeTime;
   nsTArray<Interval<Microseconds>> timeRanges;
@@ -351,23 +373,32 @@ Moof::ParseTrun(Box& aBox, Tfhd& aTfhd, 
 
   for (size_t i = 0; i < ctsOrder.Length(); i++) {
     if (i + 1 < ctsOrder.Length()) {
       ctsOrder[i]->mCompositionRange.end = ctsOrder[i + 1]->mCompositionRange.start;
     }
   }
   mTimeRange = Interval<Microseconds>(ctsOrder[0]->mCompositionRange.start,
       ctsOrder.LastElement()->mCompositionRange.end);
+  mValid = true;
 }
 
 Tkhd::Tkhd(Box& aBox)
 {
   BoxReader reader(aBox);
+  if (!reader->CanReadType<uint32_t>()) {
+    return;
+  }
   uint32_t flags = reader->ReadU32();
   uint8_t version = flags >> 24;
+  size_t need =
+    3*(version ? sizeof(int64_t) : sizeof(int32_t)) + 2*sizeof(int32_t);
+  if (reader->Remaining() < need) {
+    return;
+  }
   if (version == 0) {
     mCreationTime = reader->ReadU32();
     mModificationTime = reader->ReadU32();
     mTrackId = reader->ReadU32();
     uint32_t reserved = reader->ReadU32();
     NS_ASSERTION(!reserved, "reserved should be 0");
     mDuration = reader->ReadU32();
   } else if (version == 1) {
@@ -375,99 +406,145 @@ Tkhd::Tkhd(Box& aBox)
     mModificationTime = reader->ReadU64();
     mTrackId = reader->ReadU32();
     uint32_t reserved = reader->ReadU32();
     NS_ASSERTION(!reserved, "reserved should be 0");
     mDuration = reader->ReadU64();
   }
   // More stuff that we don't care about
   reader->DiscardRemaining();
+  mValid = true;
 }
 
 Mdhd::Mdhd(Box& aBox)
 {
   BoxReader reader(aBox);
+  if (!reader->CanReadType<uint32_t>()) {
+    return;
+  }
   uint32_t flags = reader->ReadU32();
   uint8_t version = flags >> 24;
+  size_t need =
+    3*(version ? sizeof(int64_t) : sizeof(int32_t)) + 2*sizeof(uint32_t);
+  if (reader->Remaining() < need) {
+    return;
+  }
+
   if (version == 0) {
     mCreationTime = reader->ReadU32();
     mModificationTime = reader->ReadU32();
     mTimescale = reader->ReadU32();
     mDuration = reader->ReadU32();
   } else if (version == 1) {
     mCreationTime = reader->ReadU64();
     mModificationTime = reader->ReadU64();
     mTimescale = reader->ReadU32();
     mDuration = reader->ReadU64();
   }
   // language and pre_defined=0
   reader->ReadU32();
+  if (mTimescale) {
+    mValid = true;
+  }
 }
 
 Trex::Trex(Box& aBox)
 {
   BoxReader reader(aBox);
+  if (reader->Remaining() < 6*sizeof(uint32_t)) {
+    return;
+  }
   mFlags = reader->ReadU32();
   mTrackId = reader->ReadU32();
   mDefaultSampleDescriptionIndex = reader->ReadU32();
   mDefaultSampleDuration = reader->ReadU32();
   mDefaultSampleSize = reader->ReadU32();
   mDefaultSampleFlags = reader->ReadU32();
+  mValid = true;
 }
 
 Tfhd::Tfhd(Box& aBox, Trex& aTrex) : Trex(aTrex)
 {
   MOZ_ASSERT(aBox.IsType("tfhd"));
   MOZ_ASSERT(aBox.Parent()->IsType("traf"));
   MOZ_ASSERT(aBox.Parent()->Parent()->IsType("moof"));
 
   BoxReader reader(aBox);
+  if (!reader->CanReadType<uint32_t>()) {
+    return;
+  }
   mFlags = reader->ReadU32();
+  size_t need = sizeof(uint32_t) /* trackid */;
+  uint8_t flag[] = { 1, 2, 8, 0x10, 0x20, 0 };
+  for (size_t i = 0; flag[i]; i++) {
+    if (mFlags & flag[i]) {
+      need += sizeof(uint32_t);
+    }
+  }
+  if (reader->Remaining() < need) {
+    return;
+  }
   mBaseDataOffset =
     mFlags & 1 ? reader->ReadU32() : aBox.Parent()->Parent()->Offset();
   mTrackId = reader->ReadU32();
   if (mFlags & 2) {
     mDefaultSampleDescriptionIndex = reader->ReadU32();
   }
   if (mFlags & 8) {
     mDefaultSampleDuration = reader->ReadU32();
   }
   if (mFlags & 0x10) {
     mDefaultSampleSize = reader->ReadU32();
   }
   if (mFlags & 0x20) {
     mDefaultSampleFlags = reader->ReadU32();
   }
+  mValid = true;
 }
 
 Tfdt::Tfdt(Box& aBox)
 {
   BoxReader reader(aBox);
+  if (!reader->CanReadType<uint32_t>()) {
+    return;
+  }
   uint32_t flags = reader->ReadU32();
   uint8_t version = flags >> 24;
+  size_t need = version ? sizeof(uint64_t) : sizeof(uint32_t) ;
+  if (reader->Remaining() < need) {
+    return;
+  }
   if (version == 0) {
     mBaseMediaDecodeTime = reader->ReadU32();
   } else if (version == 1) {
     mBaseMediaDecodeTime = reader->ReadU64();
   }
   reader->DiscardRemaining();
+  mValid = true;
 }
 
 Edts::Edts(Box& aBox)
   : mMediaStart(0)
 {
   Box child = aBox.FirstChild();
   if (!child.IsType("elst")) {
     return;
   }
 
   BoxReader reader(child);
+  if (!reader->CanReadType<uint32_t>()) {
+    return;
+  }
   uint32_t flags = reader->ReadU32();
   uint8_t version = flags >> 24;
-
+  size_t need =
+    sizeof(uint32_t) + 2*(version ? sizeof(int64_t) : sizeof(uint32_t));
+  if (reader->Remaining() < need) {
+    return;
+  }
   uint32_t entryCount = reader->ReadU32();
   NS_ASSERTION(entryCount == 1, "Can't handle videos with multiple edits");
   if (entryCount != 1) {
     reader->DiscardRemaining();
     return;
   }
 
   uint64_t segment_duration;
@@ -480,49 +557,70 @@ Edts::Edts(Box& aBox)
   }
   NS_ASSERTION(segment_duration == 0, "Can't handle edits with fixed durations");
   reader->DiscardRemaining();
 }
 
 Saiz::Saiz(Box& aBox) : mAuxInfoType("sinf"), mAuxInfoTypeParameter(0)
 {
   BoxReader reader(aBox);
+  if (!reader->CanReadType<uint32_t>()) {
+    return;
+  }
   uint32_t flags = reader->ReadU32();
   uint8_t version = flags >> 24;
-
+  size_t need =
+    ((flags & 1) ? 2*sizeof(uint32_t) : 0) + sizeof(uint8_t) + sizeof(uint32_t);
+  if (reader->Remaining() < need) {
+    return;
+  }
   if (flags & 1) {
     mAuxInfoType = reader->ReadU32();
     mAuxInfoTypeParameter = reader->ReadU32();
   }
   uint8_t defaultSampleInfoSize = reader->ReadU8();
   uint32_t count = reader->ReadU32();
   if (defaultSampleInfoSize) {
     for (int i = 0; i < count; i++) {
       mSampleInfoSize.AppendElement(defaultSampleInfoSize);
     }
   } else {
-    reader->ReadArray(mSampleInfoSize, count);
+    if (!reader->ReadArray(mSampleInfoSize, count)) {
+      return;
+    }
   }
+  mValid = true;
 }
 
 Saio::Saio(Box& aBox) : mAuxInfoType("sinf"), mAuxInfoTypeParameter(0)
 {
   BoxReader reader(aBox);
+  if (!reader->CanReadType<uint32_t>()) {
+    return;
+  }
   uint32_t flags = reader->ReadU32();
   uint8_t version = flags >> 24;
-
+  size_t need = ((flags & 1) ? (2*sizeof(uint32_t)) : 0) + sizeof(uint32_t);
+  if (reader->Remaining() < need) {
+    return;
+  }
   if (flags & 1) {
     mAuxInfoType = reader->ReadU32();
     mAuxInfoTypeParameter = reader->ReadU32();
   }
   size_t count = reader->ReadU32();
+  need = (version ? sizeof(uint64_t) : sizeof(uint32_t)) * count;
+  if (reader->Remaining() < count) {
+    return;
+  }
   mOffsets.SetCapacity(count);
   if (version == 0) {
     for (size_t i = 0; i < count; i++) {
       mOffsets.AppendElement(reader->ReadU32());
     }
   } else {
     for (size_t i = 0; i < count; i++) {
       mOffsets.AppendElement(reader->ReadU64());
     }
   }
+  mValid = true;
 }
 }
--- a/media/libstagefright/binding/include/mp4_demuxer/ByteReader.h
+++ b/media/libstagefright/binding/include/mp4_demuxer/ByteReader.h
@@ -31,24 +31,24 @@ public:
   explicit ByteReader(const nsTArray<uint8_t>& aData)
     : mPtr(&aData[0]), mRemaining(aData.Length()), mLength(aData.Length())
   {
   }
 
   void SetData(const nsTArray<uint8_t>& aData)
   {
     MOZ_ASSERT(!mPtr && !mRemaining);
-    mPtr = &aData[0];
+    mPtr = aData.Elements();
     mRemaining = aData.Length();
     mLength = mRemaining;
   }
 
   ~ByteReader()
   {
-    MOZ_ASSERT(!mRemaining);
+    NS_ASSERTION(!mRemaining, "Not all bytes have been processed");
   }
 
   size_t Offset()
   {
     return mLength - mRemaining;
   }
 
   // Make it explicit if we're not using the extra bytes.
--- a/media/libstagefright/binding/include/mp4_demuxer/MoofParser.h
+++ b/media/libstagefright/binding/include/mp4_demuxer/MoofParser.h
@@ -11,17 +11,32 @@
 
 namespace mp4_demuxer {
 
 class Stream;
 class Box;
 class BoxContext;
 class Moof;
 
-class Tkhd
+class Atom
+{
+public:
+  Atom()
+    : mValid(false)
+  {
+  }
+  virtual bool IsValid()
+  {
+    return mValid;
+  }
+protected:
+  bool mValid;
+};
+
+class Tkhd : public Atom
 {
 public:
   Tkhd()
     : mCreationTime(0)
     , mModificationTime(0)
     , mTrackId(0)
     , mDuration(0)
   {
@@ -29,17 +44,17 @@ public:
   explicit Tkhd(Box& aBox);
 
   uint64_t mCreationTime;
   uint64_t mModificationTime;
   uint32_t mTrackId;
   uint64_t mDuration;
 };
 
-class Mdhd
+class Mdhd : public Atom
 {
 public:
   Mdhd()
     : mCreationTime(0)
     , mModificationTime(0)
     , mTimescale(0)
     , mDuration(0)
   {
@@ -52,17 +67,17 @@ public:
   }
 
   uint64_t mCreationTime;
   uint64_t mModificationTime;
   uint32_t mTimescale;
   uint64_t mDuration;
 };
 
-class Trex
+class Trex : public Atom
 {
 public:
   explicit Trex(uint32_t aTrackId)
     : mFlags(0)
     , mTrackId(aTrackId)
     , mDefaultSampleDescriptionIndex(0)
     , mDefaultSampleDuration(0)
     , mDefaultSampleSize(0)
@@ -78,82 +93,97 @@ public:
   uint32_t mDefaultSampleDuration;
   uint32_t mDefaultSampleSize;
   uint32_t mDefaultSampleFlags;
 };
 
 class Tfhd : public Trex
 {
 public:
-  explicit Tfhd(Trex& aTrex) : Trex(aTrex), mBaseDataOffset(0) {}
+  explicit Tfhd(Trex& aTrex)
+    : Trex(aTrex)
+    , mBaseDataOffset(0)
+  {
+    mValid = aTrex.IsValid();
+  }
   Tfhd(Box& aBox, Trex& aTrex);
 
   uint64_t mBaseDataOffset;
 };
 
-class Tfdt
+class Tfdt : public Atom
 {
 public:
-  Tfdt() : mBaseMediaDecodeTime(0) {}
+  Tfdt()
+    : mBaseMediaDecodeTime(0)
+  {
+  }
   explicit Tfdt(Box& aBox);
 
   uint64_t mBaseMediaDecodeTime;
 };
 
-class Edts
+class Edts : public Atom
 {
 public:
-  Edts() : mMediaStart(0) {}
+  Edts()
+    : mMediaStart(0)
+  {
+  }
   explicit Edts(Box& aBox);
+  virtual bool IsValid()
+  {
+    // edts is optional
+    return true;
+  }
 
   int64_t mMediaStart;
 };
 
 struct Sample
 {
   mozilla::MediaByteRange mByteRange;
   mozilla::MediaByteRange mCencRange;
   Microseconds mDecodeTime;
   Interval<Microseconds> mCompositionRange;
   bool mSync;
 };
 
-class Saiz
+class Saiz : public Atom
 {
 public:
   explicit Saiz(Box& aBox);
 
   AtomType mAuxInfoType;
   uint32_t mAuxInfoTypeParameter;
   nsTArray<uint8_t> mSampleInfoSize;
 };
 
-class Saio
+class Saio : public Atom
 {
 public:
   explicit Saio(Box& aBox);
 
   AtomType mAuxInfoType;
   uint32_t mAuxInfoTypeParameter;
   nsTArray<uint64_t> mOffsets;
 };
 
 class AuxInfo {
 public:
   AuxInfo(int64_t aMoofOffset, Saiz& aSaiz, Saio& aSaio);
   bool GetByteRanges(nsTArray<MediaByteRange>* aByteRanges);
 
 private:
-
   int64_t mMoofOffset;
   Saiz& mSaiz;
   Saio& mSaio;
 };
 
-class Moof
+class Moof : public Atom
 {
 public:
   Moof(Box& aBox, Trex& aTrex, Mdhd& aMdhd, Edts& aEdts, Microseconds aTimestampOffset);
   bool GetAuxInfo(AtomType aType, nsTArray<MediaByteRange>* aByteRanges);
   void FixRounding(const Moof& aMoof);
 
   mozilla::MediaByteRange mRange;
   mozilla::MediaByteRange mMdatRange;