Bug 1314533: [MSE] P3. Reject invalid MP4 boxes. r?gerald draft
authorJean-Yves Avenard <jyavenard@mozilla.com>
Fri, 04 Nov 2016 01:49:19 +1100
changeset 434577 13d53739930d7158b559c60b1ffff53568f962a2
parent 434576 0221ea0067d169fd83d3c2b7084bfd6e133a1931
child 434578 76a1f942df5a24854f269e826244ac213c54381c
push id34777
push userbmo:jyavenard@mozilla.com
push dateMon, 07 Nov 2016 01:27:18 +0000
reviewersgerald
bugs1314533
milestone52.0a1
Bug 1314533: [MSE] P3. Reject invalid MP4 boxes. r?gerald MozReview-Commit-ID: F0FcQDqsLk7
dom/media/mediasource/ContainerParser.cpp
--- a/dom/media/mediasource/ContainerParser.cpp
+++ b/dom/media/mediasource/ContainerParser.cpp
@@ -340,42 +340,77 @@ public:
 
   MediaResult IsInitSegmentPresent(MediaByteBuffer* aData) override
   {
     ContainerParser::IsInitSegmentPresent(aData);
     // Each MP4 atom has a chunk size and chunk type. The root chunk in an MP4
     // file is the 'ftyp' atom followed by a file type. We just check for a
     // vaguely valid 'ftyp' atom.
     AtomParser parser(mType, aData);
+    if (!parser.IsValid()) {
+      return MediaResult(
+        NS_ERROR_FAILURE,
+        RESULT_DETAIL("Invalid Box:%s", parser.LastInvalidBox()));
+    }
     return parser.StartWithInitSegment() ? NS_OK : NS_ERROR_NOT_AVAILABLE;
   }
 
   MediaResult IsMediaSegmentPresent(MediaByteBuffer* aData) override
   {
     AtomParser parser(mType, aData);
+    if (!parser.IsValid()) {
+      return MediaResult(
+        NS_ERROR_FAILURE,
+        RESULT_DETAIL("Invalid Box:%s", parser.LastInvalidBox()));
+    }
     return parser.StartWithMediaSegment() ? NS_OK : NS_ERROR_NOT_AVAILABLE;
   }
 
 private:
   class AtomParser {
   public:
     AtomParser(const nsACString& aType, const MediaByteBuffer* aData)
     {
       const nsCString mType(aType); // for logging macro.
       mp4_demuxer::ByteReader reader(aData);
       mp4_demuxer::AtomType initAtom("ftyp");
       mp4_demuxer::AtomType mediaAtom("moof");
 
+      // Valid top-level boxes defined in ISO/IEC 14496-12 (Table 1)
+      static const mp4_demuxer::AtomType validBoxes[] = {
+        "ftyp", "moov", // init segment
+        "pdin", "free", "sidx", // optional prior moov box
+        "styp", "moof", "mdat", // media segment
+        "mfra", "skip", "meta", "meco", "ssix", "prft" // others.
+        "pssh", // optional with encrypted EME, though ignored.
+      };
+
       while (reader.Remaining() >= 8) {
         uint64_t size = reader.ReadU32();
         const uint8_t* typec = reader.Peek(4);
-        uint32_t type = reader.ReadU32();
+        mp4_demuxer::AtomType type(reader.ReadU32());
         MSE_DEBUGV(AtomParser ,"Checking atom:'%c%c%c%c' @ %u",
                    typec[0], typec[1], typec[2], typec[3],
                    (uint32_t)reader.Offset() - 8);
+
+        for (const auto& boxType : validBoxes) {
+          if (type == boxType) {
+            mValid = true;
+            break;
+          }
+        }
+        if (!mValid) {
+          // No point continuing.
+          mLastInvalidBox[0] = typec[3];
+          mLastInvalidBox[1] = typec[2];
+          mLastInvalidBox[2] = typec[1];
+          mLastInvalidBox[3] = typec[0];
+          mLastInvalidBox[4] = '\0';
+          break;
+        }
         if (mInitOffset.isNothing() &&
             mp4_demuxer::AtomType(type) == initAtom) {
           mInitOffset = Some(reader.Offset());
         }
         if (mMediaOffset.isNothing() &&
             mp4_demuxer::AtomType(type) == mediaAtom) {
           mMediaOffset = Some(reader.Offset());
         }
@@ -397,29 +432,33 @@ private:
         if (reader.Remaining() < size - 8) {
           // Incomplete atom.
           break;
         }
         reader.Read(size - 8);
       }
     }
 
-    bool StartWithInitSegment()
+    bool StartWithInitSegment() const
     {
       return mInitOffset.isSome() &&
         (mMediaOffset.isNothing() || mInitOffset.ref() < mMediaOffset.ref());
     }
-    bool StartWithMediaSegment()
+    bool StartWithMediaSegment() const
     {
       return mMediaOffset.isSome() &&
         (mInitOffset.isNothing() || mMediaOffset.ref() < mInitOffset.ref());
     }
+    bool IsValid() const { return mValid; }
+    const char* LastInvalidBox() const { return mLastInvalidBox; }
   private:
     Maybe<size_t> mInitOffset;
     Maybe<size_t> mMediaOffset;
+    bool mValid = false;
+    char mLastInvalidBox[5];
   };
 
 public:
   bool ParseStartAndEndTimestamps(MediaByteBuffer* aData,
                                   int64_t& aStart, int64_t& aEnd) override
   {
     bool initSegment = NS_SUCCEEDED(IsInitSegmentPresent(aData));
     if (initSegment) {