CRASH at WebCore::TrackListBase::remove
authorjer.noble@apple.com <jer.noble@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 20 Jan 2017 01:09:20 +0000 (01:09 +0000)
committerjer.noble@apple.com <jer.noble@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 20 Jan 2017 01:09:20 +0000 (01:09 +0000)
https://bugs.webkit.org/show_bug.cgi?id=167217

Reviewed by Brent Fulgham.

Source/WebCore:

Test: media/media-source/media-source-error-crash.html

In very specific conditions, a HTMLMediaElement backed by a MediaSource can try to remove
the same track from its track list twice. If there are two SourceBuffers attached to a
HTMLMediaElement, and one has not yet been initialized, when the second fails to parse an
appended buffer after receiving an initialization segment, the HTMLMediaElement will remove
all its tracks in mediaLoadingFailed(), then MediaSource object itself will attempt remove
the same track in removeSourceBuffer().

Solving this the safest way possible: bail early from TrackListBase if asked to remove a
track which the list does not contain.

* html/track/TrackListBase.cpp:
(TrackListBase::remove):

LayoutTests:

* media/media-source/media-source-error-crash-expected.txt: Added.
* media/media-source/media-source-error-crash.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@210945 268f45cc-cd09-0410-ab3c-d52691b4dbfc

LayoutTests/ChangeLog
LayoutTests/media/media-source/media-source-error-crash-expected.txt [new file with mode: 0644]
LayoutTests/media/media-source/media-source-error-crash.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/html/track/TrackListBase.cpp

index 420c6a4..95e1318 100644 (file)
@@ -1,3 +1,13 @@
+2017-01-19  Jer Noble  <jer.noble@apple.com>
+
+        CRASH at WebCore::TrackListBase::remove
+        https://bugs.webkit.org/show_bug.cgi?id=167217
+
+        Reviewed by Brent Fulgham.
+
+        * media/media-source/media-source-error-crash-expected.txt: Added.
+        * media/media-source/media-source-error-crash.html: Added.
+
 2017-01-19  Megan Gardner  <megan_gardner@apple.com>
 
         Additional selection tests and interpolation fix
diff --git a/LayoutTests/media/media-source/media-source-error-crash-expected.txt b/LayoutTests/media/media-source/media-source-error-crash-expected.txt
new file mode 100644 (file)
index 0000000..49b34d4
--- /dev/null
@@ -0,0 +1,11 @@
+
+RUN(video.src = URL.createObjectURL(source))
+EVENT(sourceopen)
+RUN(source.duration = loader.duration())
+RUN(sourceBuffer = source.addSourceBuffer(loader.type()))
+RUN(sourceBuffer2 = source.addSourceBuffer(loader.type()))
+Append an invalid media segment; should not crash.
+RUN(sourceBuffer.appendBuffer(concatArrayBuffers(loader.initSegment(), new ArrayBuffer(512))))
+EVENT(error)
+END OF TEST
+
diff --git a/LayoutTests/media/media-source/media-source-error-crash.html b/LayoutTests/media/media-source/media-source-error-crash.html
new file mode 100644 (file)
index 0000000..8f90238
--- /dev/null
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>media-source-error-crash</title>
+    <script src="media-source-loader.js"></script>
+    <script src="../video-test.js"></script>
+    <script>
+    var loader;
+    var source;
+    var sourceBuffer;
+    var sourceBuffer2;
+
+    function concatArrayBuffers(buffer1, buffer2) {
+       var view = new Uint8Array(buffer1.byteLength + buffer2.byteLength);
+       view.set(new Uint8Array(buffer1), 0);
+       view.set(new Uint8Array(buffer2), buffer1.byteLength);
+       return view.buffer;
+    }
+
+    function runTest() {
+        findMediaElement();
+
+        loader = new MediaSourceLoader('content/test-fragmented-manifest.json');
+        loader.onload = mediaDataLoaded;
+        loader.onerror = mediaDataLoadingFailed;
+    }
+
+    function mediaDataLoadingFailed() {
+        failTest('Media data loading failed');
+    }
+
+    function mediaDataLoaded() {
+        source = new MediaSource();
+        waitForEvent('sourceopen', sourceOpen, false, false, source);
+        run('video.src = URL.createObjectURL(source)');
+    }
+
+    function sourceOpen() {
+        run('source.duration = loader.duration()');
+        run('sourceBuffer = source.addSourceBuffer(loader.type())');
+        run('sourceBuffer2 = source.addSourceBuffer(loader.type())');
+        waitForEventAndEnd('error');
+        consoleWrite('Append an invalid media segment; should not crash.')
+        run('sourceBuffer.appendBuffer(concatArrayBuffers(loader.initSegment(), new ArrayBuffer(512)))');
+    }
+
+    </script>
+</head>
+<body onload="runTest()">
+    <video controls></video>
+</body>
+</html>
\ No newline at end of file
index c672a92..f2d22a6 100644 (file)
@@ -1,3 +1,25 @@
+2017-01-19  Jer Noble  <jer.noble@apple.com>
+
+        CRASH at WebCore::TrackListBase::remove
+        https://bugs.webkit.org/show_bug.cgi?id=167217
+
+        Reviewed by Brent Fulgham.
+
+        Test: media/media-source/media-source-error-crash.html
+
+        In very specific conditions, a HTMLMediaElement backed by a MediaSource can try to remove
+        the same track from its track list twice. If there are two SourceBuffers attached to a
+        HTMLMediaElement, and one has not yet been initialized, when the second fails to parse an
+        appended buffer after receiving an initialization segment, the HTMLMediaElement will remove
+        all its tracks in mediaLoadingFailed(), then MediaSource object itself will attempt remove
+        the same track in removeSourceBuffer().
+
+        Solving this the safest way possible: bail early from TrackListBase if asked to remove a
+        track which the list does not contain.
+
+        * html/track/TrackListBase.cpp:
+        (TrackListBase::remove):
+
 2017-01-19  Andy Estes  <aestes@apple.com>
 
         [iOS] Move the PDF password view into its own class for reuse
index 8b2987c..ef892cb 100644 (file)
@@ -71,7 +71,8 @@ unsigned TrackListBase::length() const
 void TrackListBase::remove(TrackBase& track, bool scheduleEvent)
 {
     size_t index = m_inbandTracks.find(&track);
-    ASSERT(index != notFound);
+    if (index == notFound)
+        return;
 
     if (track.mediaElement()) {
         ASSERT(track.mediaElement() == m_element);