Diff
Modified: trunk/LayoutTests/ChangeLog (234054 => 234055)
--- trunk/LayoutTests/ChangeLog 2018-07-20 17:45:38 UTC (rev 234054)
+++ trunk/LayoutTests/ChangeLog 2018-07-20 17:59:47 UTC (rev 234055)
@@ -1,3 +1,15 @@
+2018-07-19 Jer Noble <[email protected]>
+
+ HLS resources with remote subresources will not taint canvasses.
+ https://bugs.webkit.org/show_bug.cgi?id=187731
+ <rdar://problem/42290703>
+
+ Reviewed by Brady Eidson.
+
+ * http/tests/media/resources/hls/test-vod-localhost.m3u8: Added.
+ * http/tests/security/canvas-remote-read-remote-video-hls-expected.txt: Added.
+ * http/tests/security/canvas-remote-read-remote-video-hls.html: Added.
+
2018-07-20 Ryan Haddad <[email protected]>
Rebaseline editing/mac/attributed-string/attributed-string-for-typing-with-color-filter.html for Sierra.
Added: trunk/LayoutTests/http/tests/media/resources/hls/test-vod-localhost.m3u8 (0 => 234055)
--- trunk/LayoutTests/http/tests/media/resources/hls/test-vod-localhost.m3u8 (rev 0)
+++ trunk/LayoutTests/http/tests/media/resources/hls/test-vod-localhost.m3u8 2018-07-20 17:59:47 UTC (rev 234055)
@@ -0,0 +1,8 @@
+#EXTM3U
+#EXT-X-TARGETDURATION:6
+#EXT-X-VERSION:4
+#EXT-X-MEDIA-SEQUENCE:0
+#EXT-X-PLAYLIST-TYPE:VOD
+#EXTINF:6.0272,
+http://localhost:8000/media/resources/hls/test.ts
+#EXT-X-ENDLIST
Added: trunk/LayoutTests/http/tests/security/canvas-remote-read-remote-video-hls-expected.txt (0 => 234055)
--- trunk/LayoutTests/http/tests/security/canvas-remote-read-remote-video-hls-expected.txt (rev 0)
+++ trunk/LayoutTests/http/tests/security/canvas-remote-read-remote-video-hls-expected.txt 2018-07-20 17:59:47 UTC (rev 234055)
@@ -0,0 +1,17 @@
+CONSOLE MESSAGE: line 1: Unable to get image data from canvas because the canvas has been tainted by cross-origin data.
+Ensure that data cannot be retrieved from a canvas tainted by a video resource with remote subresources when CORS is not enabled.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Testing data retrieval on an untainted canvas:
+PASS canvas.getContext('2d').getImageData(0, 0, 100, 100) did not throw exception.
+PASS canvas.toDataURL() did not throw exception.
+
+Testing data retrieval on a canvas tainted by a pattern generated by a hls video resource containing remote contents:
+PASS context.getImageData(0, 0, 100, 100) threw exception SecurityError: The operation is insecure..
+PASS canvas.toDataURL() threw exception SecurityError: The operation is insecure..
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Added: trunk/LayoutTests/http/tests/security/canvas-remote-read-remote-video-hls.html (0 => 234055)
--- trunk/LayoutTests/http/tests/security/canvas-remote-read-remote-video-hls.html (rev 0)
+++ trunk/LayoutTests/http/tests/security/canvas-remote-read-remote-video-hls.html 2018-07-20 17:59:47 UTC (rev 234055)
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script src=""
+ <script src=""
+ <script src=""
+</head>
+<body>
+<pre id="console"></pre>
+<script>
+ description("Ensure that data cannot be retrieved from a canvas tainted by a video resource with remote subresources when CORS is not enabled.");
+
+ function test()
+ {
+ testDataRetrievalForbidden("hls video resource containing remote contents");
+ finishJSTest();
+ }
+
+ var video = document.createElement("video");
+ video.addEventListener("loadeddata", test);
+ video.src = ''
+
+ window.jsTestIsAsync = true;
+</script>
+<script src=""
+</body>
+</html>
Modified: trunk/Source/WebCore/ChangeLog (234054 => 234055)
--- trunk/Source/WebCore/ChangeLog 2018-07-20 17:45:38 UTC (rev 234054)
+++ trunk/Source/WebCore/ChangeLog 2018-07-20 17:59:47 UTC (rev 234055)
@@ -1,3 +1,56 @@
+2018-07-19 Jer Noble <[email protected]>
+
+ HLS resources with remote subresources will not taint canvasses.
+ https://bugs.webkit.org/show_bug.cgi?id=187731
+ <rdar://problem/42290703>
+
+ Reviewed by Brady Eidson.
+
+ Test: http/tests/security/canvas-remote-read-remote-video-hls.html
+
+ Most media sources are single-resource; they are accessed from a single origin. HLS manifests can contain many
+ subresources from arbitrary origins, and canvases should be tainted when painted from media elements whose
+ subresources were retrieved from tainting origins.
+
+ Add a new method to HTMLMediaElement, wouldTaintOrigin(), taking a SecurityOrigin, and returning whether the
+ media element would taint that origin. This gets piped all the way down to MediaPlayerPrivateAVFoundationObjC
+ which uses WebCoreNSURLSession to track all the origins of all the responses which resulted from the media
+ element's load.
+
+ Drive-by fix: also fix this issue for media elements which render to an AudioContext.
+
+ Drive-by fix #2: CanvasRenderingContext2DBase::createPattern() needs to check the return value of
+ ImageBuffer::create() before using it.
+
+ * Modules/webaudio/MediaElementAudioSourceNode.cpp:
+ (WebCore::MediaElementAudioSourceNode::wouldTaintOrigin):
+ * html/HTMLMediaElement.cpp:
+ (WebCore::HTMLMediaElement::didAttachRenderers):
+ (WebCore::HTMLMediaElement::didDetachRenderers):
+ (WebCore::HTMLMediaElement::scheduleUpdateShouldAutoplay):
+ * html/HTMLMediaElement.h:
+ (WebCore::HTMLMediaElement::wouldTaintOrigin const):
+ * html/canvas/CanvasRenderingContext.cpp:
+ (WebCore::CanvasRenderingContext::wouldTaintOrigin):
+ * html/canvas/CanvasRenderingContext2DBase.cpp:
+ (WebCore::CanvasRenderingContext2DBase::createPattern):
+ * platform/graphics/MediaPlayer.cpp:
+ (WebCore::MediaPlayer::wouldTaintOrigin const):
+ * platform/graphics/MediaPlayer.h:
+ * platform/graphics/MediaPlayerPrivate.h:
+ (WebCore::MediaPlayerPrivateInterface::hasSingleSecurityOrigin const):
+ (WebCore::MediaPlayerPrivateInterface::wouldTaintOrigin const):
+ * platform/graphics/avfoundation/objc/CDMSessionAVContentKeySession.mm:
+ (WebCore::CDMSessionAVContentKeySession::update):
+ * platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.h:
+ * platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.mm:
+ (WebCore::MediaPlayerPrivateAVFoundationObjC::wouldTaintOrigin const):
+ * platform/network/cocoa/WebCoreNSURLSession.h:
+ * platform/network/cocoa/WebCoreNSURLSession.mm:
+ (-[WebCoreNSURLSession task:didReceiveResponseFromOrigin:]):
+ (-[WebCoreNSURLSession wouldTaintOrigin:]):
+ (-[WebCoreNSURLSessionDataTask resource:receivedResponse:]):
+
2018-07-20 Zalan Bujtas <[email protected]>
Update FrameView::paintContents to use release logging.
Modified: trunk/Source/WebCore/Modules/webaudio/MediaElementAudioSourceNode.cpp (234054 => 234055)
--- trunk/Source/WebCore/Modules/webaudio/MediaElementAudioSourceNode.cpp 2018-07-20 17:45:38 UTC (rev 234054)
+++ trunk/Source/WebCore/Modules/webaudio/MediaElementAudioSourceNode.cpp 2018-07-20 17:59:47 UTC (rev 234055)
@@ -110,7 +110,12 @@
if (m_mediaElement->didPassCORSAccessCheck())
return false;
- return context().wouldTaintOrigin(m_mediaElement->currentSrc());
+ if (auto* scriptExecutionContext = context().scriptExecutionContext()) {
+ if (auto* origin = scriptExecutionContext->securityOrigin())
+ return m_mediaElement->wouldTaintOrigin(*origin);
+ }
+
+ return true;
}
void MediaElementAudioSourceNode::process(size_t numberOfFrames)
Modified: trunk/Source/WebCore/html/HTMLMediaElement.h (234054 => 234055)
--- trunk/Source/WebCore/html/HTMLMediaElement.h 2018-07-20 17:45:38 UTC (rev 234054)
+++ trunk/Source/WebCore/html/HTMLMediaElement.h 2018-07-20 17:59:47 UTC (rev 234055)
@@ -415,6 +415,7 @@
bool hasSingleSecurityOrigin() const { return !m_player || m_player->hasSingleSecurityOrigin(); }
bool didPassCORSAccessCheck() const { return m_player && m_player->didPassCORSAccessCheck(); }
+ bool wouldTaintOrigin(const SecurityOrigin& origin) const { return m_player && m_player->wouldTaintOrigin(origin); }
WEBCORE_EXPORT bool isFullscreen() const override;
bool isStandardFullscreen() const;
Modified: trunk/Source/WebCore/html/canvas/CanvasRenderingContext.cpp (234054 => 234055)
--- trunk/Source/WebCore/html/canvas/CanvasRenderingContext.cpp 2018-07-20 17:45:38 UTC (rev 234054)
+++ trunk/Source/WebCore/html/canvas/CanvasRenderingContext.cpp 2018-07-20 17:59:47 UTC (rev 234055)
@@ -109,7 +109,7 @@
if (!video->hasSingleSecurityOrigin())
return true;
- if (!(video->player() && video->player()->didPassCORSAccessCheck()) && wouldTaintOrigin(video->currentSrc()))
+ if (!(video->player() && video->player()->didPassCORSAccessCheck()) && video->wouldTaintOrigin(*m_canvas.securityOrigin()))
return true;
#else
Modified: trunk/Source/WebCore/html/canvas/CanvasRenderingContext2DBase.cpp (234054 => 234055)
--- trunk/Source/WebCore/html/canvas/CanvasRenderingContext2DBase.cpp 2018-07-20 17:45:38 UTC (rev 234054)
+++ trunk/Source/WebCore/html/canvas/CanvasRenderingContext2DBase.cpp 2018-07-20 17:59:47 UTC (rev 234055)
@@ -1970,6 +1970,9 @@
#endif
auto imageBuffer = ImageBuffer::create(size(videoElement), drawingContext() ? drawingContext()->renderingMode() : Accelerated);
+ if (!imageBuffer)
+ return nullptr;
+
videoElement.paintCurrentFrameInContext(imageBuffer->context(), FloatRect(FloatPoint(), size(videoElement)));
return RefPtr<CanvasPattern> { CanvasPattern::create(ImageBuffer::sinkIntoImage(WTFMove(imageBuffer), PreserveResolution::Yes).releaseNonNull(), repeatX, repeatY, originClean) };
Modified: trunk/Source/WebCore/platform/graphics/MediaPlayer.cpp (234054 => 234055)
--- trunk/Source/WebCore/platform/graphics/MediaPlayer.cpp 2018-07-20 17:45:38 UTC (rev 234054)
+++ trunk/Source/WebCore/platform/graphics/MediaPlayer.cpp 2018-07-20 17:59:47 UTC (rev 234055)
@@ -1046,6 +1046,18 @@
return m_private->didPassCORSAccessCheck();
}
+bool MediaPlayer::wouldTaintOrigin(const SecurityOrigin& origin) const
+{
+ auto wouldTaint = m_private->wouldTaintOrigin(origin);
+ if (wouldTaint.has_value())
+ return wouldTaint.value();
+
+ if (m_url.protocolIsData())
+ return true;
+
+ return origin.canRequest(m_url);
+}
+
MediaPlayer::MovieLoadType MediaPlayer::movieLoadType() const
{
return m_private->movieLoadType();
Modified: trunk/Source/WebCore/platform/graphics/MediaPlayer.h (234054 => 234055)
--- trunk/Source/WebCore/platform/graphics/MediaPlayer.h 2018-07-20 17:45:38 UTC (rev 234054)
+++ trunk/Source/WebCore/platform/graphics/MediaPlayer.h 2018-07-20 17:59:47 UTC (rev 234055)
@@ -461,8 +461,8 @@
#endif
bool hasSingleSecurityOrigin() const;
-
bool didPassCORSAccessCheck() const;
+ bool wouldTaintOrigin(const SecurityOrigin&) const;
MediaTime mediaTimeForTimeValue(const MediaTime&) const;
Modified: trunk/Source/WebCore/platform/graphics/MediaPlayerPrivate.h (234054 => 234055)
--- trunk/Source/WebCore/platform/graphics/MediaPlayerPrivate.h 2018-07-20 17:45:38 UTC (rev 234054)
+++ trunk/Source/WebCore/platform/graphics/MediaPlayerPrivate.h 2018-07-20 17:59:47 UTC (rev 234055)
@@ -189,8 +189,8 @@
virtual void setShouldMaintainAspectRatio(bool) { }
virtual bool hasSingleSecurityOrigin() const { return false; }
-
virtual bool didPassCORSAccessCheck() const { return false; }
+ virtual std::optional<bool> wouldTaintOrigin(const SecurityOrigin&) const { return std::nullopt; }
virtual MediaPlayer::MovieLoadType movieLoadType() const { return MediaPlayer::Unknown; }
Modified: trunk/Source/WebCore/platform/graphics/avfoundation/objc/CDMSessionAVContentKeySession.mm (234054 => 234055)
--- trunk/Source/WebCore/platform/graphics/avfoundation/objc/CDMSessionAVContentKeySession.mm 2018-07-20 17:45:38 UTC (rev 234054)
+++ trunk/Source/WebCore/platform/graphics/avfoundation/objc/CDMSessionAVContentKeySession.mm 2018-07-20 17:59:47 UTC (rev 234055)
@@ -237,18 +237,6 @@
{
UNUSED_PARAM(nextMessage);
- if (m_stopped) {
- errorCode = MediaPlayer::InvalidPlayerState;
- return false;
- }
-
- bool shouldGenerateKeyRequest = !m_certificate || isEqual(key, "renew");
- if (!m_certificate) {
- LOG(Media, "CDMSessionAVContentKeySession::update(%p) - certificate data", this);
-
- m_certificate = key;
- }
-
if (isEqual(key, "acknowledged")) {
LOG(Media, "CDMSessionAVContentKeySession::update(%p) - acknowleding secure stop message", this);
@@ -266,6 +254,18 @@
return true;
}
+ if (m_stopped) {
+ errorCode = MediaPlayer::InvalidPlayerState;
+ return false;
+ }
+
+ bool shouldGenerateKeyRequest = !m_certificate || isEqual(key, "renew");
+ if (!m_certificate) {
+ LOG(Media, "CDMSessionAVContentKeySession::update(%p) - certificate data", this);
+
+ m_certificate = key;
+ }
+
if (m_mode == KeyRelease)
return false;
Modified: trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.h (234054 => 234055)
--- trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.h 2018-07-20 17:45:38 UTC (rev 234054)
+++ trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.h 2018-07-20 17:59:47 UTC (rev 234055)
@@ -227,7 +227,9 @@
void updateVideoLayerGravity() override;
bool didPassCORSAccessCheck() const override;
+ std::optional<bool> wouldTaintOrigin(const SecurityOrigin&) const final;
+
MediaTime getStartDate() const override;
#if ENABLE(VIDEO_TRACK)
Modified: trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.mm (234054 => 234055)
--- trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.mm 2018-07-20 17:45:38 UTC (rev 234054)
+++ trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.mm 2018-07-20 17:59:47 UTC (rev 234055)
@@ -2219,6 +2219,22 @@
return false;
}
+std::optional<bool> MediaPlayerPrivateAVFoundationObjC::wouldTaintOrigin(const SecurityOrigin& origin) const
+{
+#if PLATFORM(IOS) || __MAC_OS_X_VERSION_MIN_REQUIRED > 101100
+ AVAssetResourceLoader *resourceLoader = m_avAsset.get().resourceLoader;
+ if (!DeprecatedGlobalSettings::isAVFoundationNSURLSessionEnabled()
+ || ![resourceLoader respondsToSelector:@selector(URLSession)])
+ return false;
+
+ WebCoreNSURLSession *session = (WebCoreNSURLSession *)resourceLoader.URLSession;
+ if ([session isKindOfClass:[WebCoreNSURLSession class]])
+ return [session wouldTaintOrigin:origin];
+#endif
+ return std::nullopt;
+}
+
+
#if HAVE(AVFOUNDATION_VIDEO_OUTPUT)
void MediaPlayerPrivateAVFoundationObjC::createVideoOutput()
Modified: trunk/Source/WebCore/platform/network/cocoa/WebCoreNSURLSession.h (234054 => 234055)
--- trunk/Source/WebCore/platform/network/cocoa/WebCoreNSURLSession.h 2018-07-20 17:45:38 UTC (rev 234054)
+++ trunk/Source/WebCore/platform/network/cocoa/WebCoreNSURLSession.h 2018-07-20 17:59:47 UTC (rev 234055)
@@ -26,6 +26,7 @@
#ifndef WebCoreNSURLSession_h
#define WebCoreNSURLSession_h
+#import "SecurityOrigin.h"
#import <Foundation/NSURLSession.h>
#import <wtf/HashSet.h>
#import <wtf/Lock.h>
@@ -62,6 +63,7 @@
RetainPtr<NSOperationQueue> _queue;
NSString *_sessionDescription;
HashSet<RetainPtr<WebCoreNSURLSessionDataTask>> _dataTasks;
+ HashSet<RefPtr<WebCore::SecurityOrigin>> _origins;
Lock _dataTasksLock;
BOOL _invalidated;
NSUInteger _nextTaskIdentifier;
@@ -76,6 +78,7 @@
@property (readonly) BOOL didPassCORSAccessChecks;
- (void)finishTasksAndInvalidate;
- (void)invalidateAndCancel;
+- (BOOL)wouldTaintOrigin:(const WebCore::SecurityOrigin&)origin;
- (void)resetWithCompletionHandler:(void (^)(void))completionHandler;
- (void)flushWithCompletionHandler:(void (^)(void))completionHandler;
Modified: trunk/Source/WebCore/platform/network/cocoa/WebCoreNSURLSession.mm (234054 => 234055)
--- trunk/Source/WebCore/platform/network/cocoa/WebCoreNSURLSession.mm 2018-07-20 17:45:38 UTC (rev 234054)
+++ trunk/Source/WebCore/platform/network/cocoa/WebCoreNSURLSession.mm 2018-07-20 17:59:47 UTC (rev 234055)
@@ -44,6 +44,7 @@
- (void)taskCompleted:(WebCoreNSURLSessionDataTask *)task;
- (void)addDelegateOperation:(Function<void()>&&)operation;
- (void)task:(WebCoreNSURLSessionDataTask *)task didReceiveCORSAccessCheckResult:(BOOL)result;
+- (void)task:(WebCoreNSURLSessionDataTask *)task didReceiveResponseFromOrigin:(Ref<WebCore::SecurityOrigin>&&)origin;
@end
@interface WebCoreNSURLSessionDataTask ()
@@ -147,6 +148,12 @@
_corsResults = WebCoreNSURLSessionCORSAccessCheckResults::Pass;
}
+- (void)task:(WebCoreNSURLSessionDataTask *)task didReceiveResponseFromOrigin:(Ref<WebCore::SecurityOrigin>&&)origin
+{
+ UNUSED_PARAM(task);
+ _origins.add(WTFMove(origin));
+}
+
#pragma mark - NSURLSession API
@synthesize sessionDescription=_sessionDescription;
@dynamic delegate;
@@ -184,6 +191,15 @@
return _corsResults == WebCoreNSURLSessionCORSAccessCheckResults::Pass;
}
+- (BOOL)wouldTaintOrigin:(const WebCore::SecurityOrigin &)origin
+{
+ for (auto& responseOrigin : _origins) {
+ if (!origin.canAccess(*responseOrigin))
+ return true;
+ }
+ return false;
+}
+
- (void)finishTasksAndInvalidate
{
_invalidated = YES;
@@ -614,6 +630,7 @@
ASSERT(response.source() == ResourceResponse::Source::Network || response.source() == ResourceResponse::Source::DiskCache || response.source() == ResourceResponse::Source::DiskCacheAfterValidation || response.source() == ResourceResponse::Source::ServiceWorker);
ASSERT_UNUSED(resource, &resource == _resource);
ASSERT(isMainThread());
+ [self.session task:self didReceiveResponseFromOrigin:SecurityOrigin::create(response.url())];
[self.session task:self didReceiveCORSAccessCheckResult:resource.didPassAccessControlCheck()];
self.countOfBytesExpectedToReceive = response.expectedContentLength();
[self _setDefersLoading:YES];