Commit 5491b007 authored by Matt Menke's avatar Matt Menke Committed by Commit Bot

Fix double-delete in ResolveProxyMsgHelper.

The class is reference counted, and makes a mojo call, with a callback,
on the UI thread. The callback was passed a raw pointer to the class,
but when it was invoked, it grabbed a reference to the class. When the
class's last reference is released, it posts a delete task to the UI
thread.

So if the class's last reference was released, and then it received a
a Mojo callback, there would be a pending delete task, we'd grab a
new ref (Increasing the refcount from 0 to 1), the delete task would
run, and then a new delete task would be posted when the reference went
to 0 again, resulting in a double-delete.

This CL fixes that by making the class keep a reference to itself
whebever there's a pending mojo callback. MessageFilters are designed
to be able to call Send() after the class they want to send messages
to has been deleted, so the increased lifetime is completely safe.

Bug: 870675
Change-Id: I64f6656e61dc9222a67cd40555d3dd73fb48e208
Reviewed-on: https://chromium-review.googlesource.com/1161967
Commit-Queue: Matt Menke <mmenke@chromium.org>
Reviewed-by: default avatarJohn Abd-El-Malek <jam@chromium.org>
Reviewed-by: default avatarEric Roman <eroman@chromium.org>
Cr-Commit-Position: refs/heads/master@{#580932}
parent f2ffbf8d
......@@ -21,11 +21,6 @@ ResolveProxyMsgHelper::ResolveProxyMsgHelper(int render_process_host_id)
render_process_host_id_(render_process_host_id),
binding_(this) {}
void ResolveProxyMsgHelper::OnDestruct() const {
// Have to delete on the UI thread, since Mojo objects aren't threadsafe.
BrowserThread::DeleteSoon(BrowserThread::UI, FROM_HERE, this);
}
void ResolveProxyMsgHelper::OverrideThreadForMessage(
const IPC::Message& message,
BrowserThread::ID* thread) {
......@@ -56,7 +51,10 @@ void ResolveProxyMsgHelper::OnResolveProxy(const GURL& url,
}
}
ResolveProxyMsgHelper::~ResolveProxyMsgHelper() = default;
ResolveProxyMsgHelper::~ResolveProxyMsgHelper() {
DCHECK(!owned_self_);
DCHECK(!binding_.is_bound());
}
void ResolveProxyMsgHelper::StartPendingRequest() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
......@@ -69,6 +67,7 @@ void ResolveProxyMsgHelper::StartPendingRequest() {
binding_.set_connection_error_handler(
base::BindOnce(&ResolveProxyMsgHelper::OnProxyLookupComplete,
base::Unretained(this), base::nullopt));
owned_self_ = this;
if (!SendRequestToNetworkService(pending_requests_.front().url,
std::move(proxy_lookup_client))) {
OnProxyLookupComplete(base::nullopt);
......@@ -98,6 +97,15 @@ void ResolveProxyMsgHelper::OnProxyLookupComplete(
binding_.Close();
// If all references except |owned_self_| have been released, just release
// the last reference, without doing anything.
if (HasOneRef()) {
scoped_refptr<ResolveProxyMsgHelper> self = std::move(owned_self_);
return;
}
owned_self_ = nullptr;
// Clear the current (completed) request.
PendingRequest completed_req = std::move(pending_requests_.front());
pending_requests_.pop_front();
......
......@@ -33,14 +33,16 @@ namespace content {
// outstanding proxy resolve requests with the proxy service. It also deletes
// the stored IPC::Message pointers for pending requests.
//
// This object does most of its work, and destroys itself, on the UI thread.
// This object does most of its work on the UI thread. It holds onto a
// self-reference as long as there's a pending Mojo call, as losing its last
// reference on the IO thread with an open mojo pipe that lives on the UI
// thread leads to problems.
class CONTENT_EXPORT ResolveProxyMsgHelper : public BrowserMessageFilter,
network::mojom::ProxyLookupClient {
public:
explicit ResolveProxyMsgHelper(int render_process_host_id);
// BrowserMessageFilter implementation
void OnDestruct() const override;
void OverrideThreadForMessage(const IPC::Message& message,
BrowserThread::ID* thread) override;
bool OnMessageReceived(const IPC::Message& message) override;
......@@ -94,6 +96,12 @@ class CONTENT_EXPORT ResolveProxyMsgHelper : public BrowserMessageFilter,
using PendingRequestList = base::circular_deque<PendingRequest>;
PendingRequestList pending_requests_;
// Self-reference. Owned as long as there's an outstanding proxy lookup.
// Needed to shut down safely, since this class is refcounted, with some
// references owned on multiple threads, while |binding_| lives on the UI
// thread, and may receive callbacks there whenever there's a pending request.
scoped_refptr<ResolveProxyMsgHelper> owned_self_;
// Binding for the currently in-progress request, if any.
mojo::Binding<network::mojom::ProxyLookupClient> binding_;
......
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