Commit 95628760 authored by Jonathan Backer's avatar Jonathan Backer Committed by Commit Bot

Reland: SkiaRenderer: Fix subtle use after free with WeakPtr

SkiaOutputSurfaceImpl owns the SkiaOutputSurfaceDependency. After Impl
goes out of scope, there is no more Dependency. We reference the
Dependency when we call CreateSafe{Once,Repeating}Callback for things
like presentation feedback callbacks. It is possible for presentation
feedback to occur after Impl is gone, in which case we can no longer use
Dependency.

Bug: 1020699
Change-Id: I54a13f1d65c2b2e8603883457bdd2e1195a3f9ac
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1943047
Commit-Queue: Jonathan Backer <backer@chromium.org>
Reviewed-by: default avatarkylechar <kylechar@chromium.org>
Cr-Commit-Position: refs/heads/master@{#720081}
parent 0ac5b2e9
...@@ -54,40 +54,6 @@ sk_sp<SkPromiseImageTexture> Fulfill(void* texture_context) { ...@@ -54,40 +54,6 @@ sk_sp<SkPromiseImageTexture> Fulfill(void* texture_context) {
void DoNothing(void* texture_context) {} void DoNothing(void* texture_context) {}
template <typename... Args>
void PostAsyncTaskRepeatedly(
SkiaOutputSurfaceDependency* dependency,
const base::RepeatingCallback<void(Args...)>& callback,
Args... args) {
dependency->PostTaskToClientThread(base::BindOnce(callback, args...));
}
template <typename... Args>
base::RepeatingCallback<void(Args...)> CreateSafeRepeatingCallback(
SkiaOutputSurfaceDependency* dependency,
const base::RepeatingCallback<void(Args...)>& callback) {
DCHECK(dependency);
return base::BindRepeating(&PostAsyncTaskRepeatedly<Args...>, dependency,
callback);
}
template <typename... Args>
void PostAsyncTaskOnce(SkiaOutputSurfaceDependency* dependency,
base::OnceCallback<void(Args...)> callback,
Args... args) {
dependency->PostTaskToClientThread(
base::BindOnce(std::move(callback), args...));
}
template <typename... Args>
base::OnceCallback<void(Args...)> CreateSafeOnceCallback(
SkiaOutputSurfaceDependency* dependency,
base::OnceCallback<void(Args...)> callback) {
DCHECK(dependency);
return base::BindOnce(&PostAsyncTaskOnce<Args...>, dependency,
std::move(callback));
}
gpu::ContextUrl& GetActiveUrl() { gpu::ContextUrl& GetActiveUrl() {
static base::NoDestructor<gpu::ContextUrl> active_url( static base::NoDestructor<gpu::ContextUrl> active_url(
GURL("chrome://gpu/SkiaRenderer")); GURL("chrome://gpu/SkiaRenderer"));
...@@ -672,43 +638,60 @@ bool SkiaOutputSurfaceImpl::Initialize() { ...@@ -672,43 +638,60 @@ bool SkiaOutputSurfaceImpl::Initialize() {
task_sequence_ = dependency_->CreateSequence(); task_sequence_ = dependency_->CreateSequence();
weak_ptr_ = weak_ptr_factory_.GetWeakPtr(); weak_ptr_ = weak_ptr_factory_.GetWeakPtr();
// This runner could be called from vsync or GPU thread after |this| is
// destroyed. We post directly to display compositor thread to check
// |weak_ptr_| as |dependency_| may have been destroyed.
GpuVSyncCallback vsync_callback_runner =
#if defined(OS_ANDROID)
// Callback is never used on Android. Doesn't work with WebView because
// calling it bypasses SkiaOutputSurfaceDependency.
base::DoNothing();
#else
base::BindRepeating(
[](scoped_refptr<base::SingleThreadTaskRunner> runner,
base::WeakPtr<SkiaOutputSurfaceImpl> weak_ptr,
base::TimeTicks timebase, base::TimeDelta interval) {
runner->PostTask(FROM_HERE,
base::BindOnce(&SkiaOutputSurfaceImpl::OnGpuVSync,
weak_ptr, timebase, interval));
},
base::ThreadTaskRunnerHandle::Get(), weak_ptr_);
#endif
base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL, base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED); base::WaitableEvent::InitialState::NOT_SIGNALED);
bool result = false; bool result = false;
auto callback = base::BindOnce(&SkiaOutputSurfaceImpl::InitializeOnGpuThread, auto callback = base::BindOnce(&SkiaOutputSurfaceImpl::InitializeOnGpuThread,
base::Unretained(this), &event, &result); base::Unretained(this), vsync_callback_runner,
&event, &result);
ScheduleGpuTask(std::move(callback), {}); ScheduleGpuTask(std::move(callback), {});
event.Wait(); event.Wait();
return result; return result;
} }
void SkiaOutputSurfaceImpl::InitializeOnGpuThread(base::WaitableEvent* event, void SkiaOutputSurfaceImpl::InitializeOnGpuThread(
bool* result) { GpuVSyncCallback vsync_callback_runner,
base::WaitableEvent* event,
bool* result) {
base::Optional<base::ScopedClosureRunner> scoped_runner; base::Optional<base::ScopedClosureRunner> scoped_runner;
if (event) { if (event) {
scoped_runner.emplace( scoped_runner.emplace(
base::BindOnce(&base::WaitableEvent::Signal, base::Unretained(event))); base::BindOnce(&base::WaitableEvent::Signal, base::Unretained(event)));
} }
auto did_swap_buffer_complete_callback = CreateSafeRepeatingCallback( auto did_swap_buffer_complete_callback = base::BindRepeating(
dependency_.get(), &SkiaOutputSurfaceImpl::DidSwapBuffersComplete, weak_ptr_);
base::BindRepeating(&SkiaOutputSurfaceImpl::DidSwapBuffersComplete, auto buffer_presented_callback =
weak_ptr_)); base::BindRepeating(&SkiaOutputSurfaceImpl::BufferPresented, weak_ptr_);
auto buffer_presented_callback = CreateSafeRepeatingCallback( auto context_lost_callback =
dependency_.get(), base::BindOnce(&SkiaOutputSurfaceImpl::ContextLost, weak_ptr_);
base::BindRepeating(&SkiaOutputSurfaceImpl::BufferPresented, weak_ptr_));
auto context_lost_callback = CreateSafeOnceCallback(
dependency_.get(),
base::BindOnce(&SkiaOutputSurfaceImpl::ContextLost, weak_ptr_));
auto gpu_vsync_callback = CreateSafeRepeatingCallback(
dependency_.get(),
base::BindRepeating(&SkiaOutputSurfaceImpl::OnGpuVSync, weak_ptr_));
impl_on_gpu_ = SkiaOutputSurfaceImplOnGpu::Create( impl_on_gpu_ = SkiaOutputSurfaceImplOnGpu::Create(
dependency_.get(), renderer_settings_, task_sequence_->GetSequenceId(), dependency_.get(), renderer_settings_, task_sequence_->GetSequenceId(),
std::move(did_swap_buffer_complete_callback), std::move(did_swap_buffer_complete_callback),
std::move(buffer_presented_callback), std::move(context_lost_callback), std::move(buffer_presented_callback), std::move(context_lost_callback),
std::move(gpu_vsync_callback)); std::move(vsync_callback_runner));
if (!impl_on_gpu_) { if (!impl_on_gpu_) {
*result = false; *result = false;
} else { } else {
......
...@@ -161,7 +161,9 @@ class VIZ_SERVICE_EXPORT SkiaOutputSurfaceImpl : public SkiaOutputSurface { ...@@ -161,7 +161,9 @@ class VIZ_SERVICE_EXPORT SkiaOutputSurfaceImpl : public SkiaOutputSurface {
private: private:
bool Initialize(); bool Initialize();
void InitializeOnGpuThread(base::WaitableEvent* event, bool* result); void InitializeOnGpuThread(GpuVSyncCallback vsync_callback_runner,
base::WaitableEvent* event,
bool* result);
SkSurfaceCharacterization CreateSkSurfaceCharacterization( SkSurfaceCharacterization CreateSkSurfaceCharacterization(
const gfx::Size& surface_size, const gfx::Size& surface_size,
ResourceFormat format, ResourceFormat format,
......
...@@ -84,6 +84,25 @@ namespace viz { ...@@ -84,6 +84,25 @@ namespace viz {
namespace { namespace {
template <typename... Args>
void PostAsyncTaskRepeatedly(
base::WeakPtr<SkiaOutputSurfaceImplOnGpu> impl_on_gpu,
const base::RepeatingCallback<void(Args...)>& callback,
Args... args) {
// Callbacks generated by this function may be executed asynchronously
// (e.g. by presentation feedback) after |impl_on_gpu| has been destroyed.
if (impl_on_gpu)
impl_on_gpu->PostTaskToClientThread(base::BindOnce(callback, args...));
}
template <typename... Args>
base::RepeatingCallback<void(Args...)> CreateSafeRepeatingCallback(
base::WeakPtr<SkiaOutputSurfaceImplOnGpu> impl_on_gpu,
const base::RepeatingCallback<void(Args...)>& callback) {
return base::BindRepeating(&PostAsyncTaskRepeatedly<Args...>, impl_on_gpu,
callback);
}
struct ReadPixelsContext { struct ReadPixelsContext {
ReadPixelsContext(std::unique_ptr<CopyOutputRequest> request, ReadPixelsContext(std::unique_ptr<CopyOutputRequest> request,
const gfx::Rect& result_rect) const gfx::Rect& result_rect)
...@@ -645,14 +664,18 @@ SkiaOutputSurfaceImplOnGpu::SkiaOutputSurfaceImplOnGpu( ...@@ -645,14 +664,18 @@ SkiaOutputSurfaceImplOnGpu::SkiaOutputSurfaceImplOnGpu(
dawn_context_provider_(dependency_->GetDawnContextProvider()), dawn_context_provider_(dependency_->GetDawnContextProvider()),
renderer_settings_(renderer_settings), renderer_settings_(renderer_settings),
sequence_id_(sequence_id), sequence_id_(sequence_id),
did_swap_buffer_complete_callback_(
std::move(did_swap_buffer_complete_callback)),
buffer_presented_callback_(std::move(buffer_presented_callback)),
context_lost_callback_(std::move(context_lost_callback)), context_lost_callback_(std::move(context_lost_callback)),
gpu_vsync_callback_(std::move(gpu_vsync_callback)), gpu_vsync_callback_(std::move(gpu_vsync_callback)),
gpu_preferences_(dependency_->GetGpuPreferences()), gpu_preferences_(dependency_->GetGpuPreferences()),
copier_active_url_(GURL("chrome://gpu/SkiaRendererGLRendererCopier")) { copier_active_url_(GURL("chrome://gpu/SkiaRendererGLRendererCopier")) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
weak_ptr_ = weak_ptr_factory_.GetWeakPtr();
did_swap_buffer_complete_callback_ = CreateSafeRepeatingCallback(
weak_ptr_, std::move(did_swap_buffer_complete_callback));
buffer_presented_callback_ = CreateSafeRepeatingCallback(
weak_ptr_, std::move(buffer_presented_callback));
dependency_->RegisterDisplayContext(this); dependency_->RegisterDisplayContext(this);
} }
...@@ -1297,7 +1320,6 @@ bool SkiaOutputSurfaceImplOnGpu::Initialize() { ...@@ -1297,7 +1320,6 @@ bool SkiaOutputSurfaceImplOnGpu::Initialize() {
TRACE_EVENT1("viz", "SkiaOutputSurfaceImplOnGpu::Initialize", TRACE_EVENT1("viz", "SkiaOutputSurfaceImplOnGpu::Initialize",
"is_using_vulkan", is_using_vulkan()); "is_using_vulkan", is_using_vulkan());
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
weak_ptr_ = weak_ptr_factory_.GetWeakPtr();
#if defined(USE_OZONE) #if defined(USE_OZONE)
window_surface_ = window_surface_ =
ui::OzonePlatform::GetInstance() ui::OzonePlatform::GetInstance()
...@@ -1591,7 +1613,7 @@ void SkiaOutputSurfaceImplOnGpu::RenderToOverlay( ...@@ -1591,7 +1613,7 @@ void SkiaOutputSurfaceImplOnGpu::RenderToOverlay(
void SkiaOutputSurfaceImplOnGpu::MarkContextLost() { void SkiaOutputSurfaceImplOnGpu::MarkContextLost() {
context_state_->MarkContextLost(); context_state_->MarkContextLost();
if (context_lost_callback_) { if (context_lost_callback_) {
std::move(context_lost_callback_).Run(); PostTaskToClientThread(std::move(context_lost_callback_));
if (context_provider_) if (context_provider_)
context_provider_->MarkContextLost(); context_provider_->MarkContextLost();
} }
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include "components/viz/service/display/output_surface_frame.h" #include "components/viz/service/display/output_surface_frame.h"
#include "components/viz/service/display/overlay_processor.h" #include "components/viz/service/display/overlay_processor.h"
#include "components/viz/service/display_embedder/skia_output_device.h" #include "components/viz/service/display_embedder/skia_output_device.h"
#include "components/viz/service/display_embedder/skia_output_surface_dependency.h"
#include "gpu/command_buffer/common/mailbox.h" #include "gpu/command_buffer/common/mailbox.h"
#include "gpu/command_buffer/common/sync_token.h" #include "gpu/command_buffer/common/sync_token.h"
#include "gpu/command_buffer/service/shared_context_state.h" #include "gpu/command_buffer/service/shared_context_state.h"
...@@ -64,7 +65,6 @@ class DawnContextProvider; ...@@ -64,7 +65,6 @@ class DawnContextProvider;
class DirectContextProvider; class DirectContextProvider;
class GLRendererCopier; class GLRendererCopier;
class ImageContextImpl; class ImageContextImpl;
class SkiaOutputSurfaceDependency;
class TextureDeleter; class TextureDeleter;
class VulkanContextProvider; class VulkanContextProvider;
...@@ -86,6 +86,8 @@ class SkiaOutputSurfaceImplOnGpu : public gpu::ImageTransportSurfaceDelegate, ...@@ -86,6 +86,8 @@ class SkiaOutputSurfaceImplOnGpu : public gpu::ImageTransportSurfaceDelegate,
base::RepeatingCallback<void(const gfx::PresentationFeedback& feedback)>; base::RepeatingCallback<void(const gfx::PresentationFeedback& feedback)>;
using ContextLostCallback = base::OnceClosure; using ContextLostCallback = base::OnceClosure;
// |gpu_vsync_callback| must be safe to call on any thread. The other
// callbacks will only be called via |deps->PostTaskToClientThread|.
static std::unique_ptr<SkiaOutputSurfaceImplOnGpu> Create( static std::unique_ptr<SkiaOutputSurfaceImplOnGpu> Create(
SkiaOutputSurfaceDependency* deps, SkiaOutputSurfaceDependency* deps,
const RendererSettings& renderer_settings, const RendererSettings& renderer_settings,
...@@ -201,6 +203,10 @@ class SkiaOutputSurfaceImplOnGpu : public gpu::ImageTransportSurfaceDelegate, ...@@ -201,6 +203,10 @@ class SkiaOutputSurfaceImplOnGpu : public gpu::ImageTransportSurfaceDelegate,
// gpu::DisplayContext implementation: // gpu::DisplayContext implementation:
void MarkContextLost() override; void MarkContextLost() override;
void PostTaskToClientThread(base::OnceClosure closure) {
dependency_->PostTaskToClientThread(std::move(closure));
}
private: private:
class ScopedPromiseImageAccess; class ScopedPromiseImageAccess;
...@@ -268,10 +274,10 @@ class SkiaOutputSurfaceImplOnGpu : public gpu::ImageTransportSurfaceDelegate, ...@@ -268,10 +274,10 @@ class SkiaOutputSurfaceImplOnGpu : public gpu::ImageTransportSurfaceDelegate,
// readback using GLRendererCopier. // readback using GLRendererCopier.
// TODO(samans): Remove |sequence_id| once readback always uses Skia. // TODO(samans): Remove |sequence_id| once readback always uses Skia.
const gpu::SequenceId sequence_id_; const gpu::SequenceId sequence_id_;
const DidSwapBufferCompleteCallback did_swap_buffer_complete_callback_; DidSwapBufferCompleteCallback did_swap_buffer_complete_callback_;
const BufferPresentedCallback buffer_presented_callback_; BufferPresentedCallback buffer_presented_callback_;
ContextLostCallback context_lost_callback_; ContextLostCallback context_lost_callback_;
const GpuVSyncCallback gpu_vsync_callback_; GpuVSyncCallback gpu_vsync_callback_;
#if defined(USE_OZONE) #if defined(USE_OZONE)
// This should outlive gl_surface_ and vulkan_surface_. // This should outlive gl_surface_ and vulkan_surface_.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment