Commit bfb9e9ab authored by fsamuel@chromium.org's avatar fsamuel@chromium.org

BrowserPlugin: Allow stack to unwind before denying permission.

This prevents a use-after-free bug where it is possible WebContentsImpl attempts
to access a newly created window after BrowserPluginGuest has freed it because the
permission was instantly denied. This can happen if BrowserPluginGuest has no
delegate: it's not a <webview> or <adview>.

BUG=338345

Review URL: https://codereview.chromium.org/149643002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@247761 0039d316-1c4b-4281-b951-d872f2087c98
parent ee03b3a1
...@@ -63,29 +63,41 @@ BrowserPluginHostFactory* BrowserPluginGuest::factory_ = NULL; ...@@ -63,29 +63,41 @@ BrowserPluginHostFactory* BrowserPluginGuest::factory_ = NULL;
class BrowserPluginGuest::PermissionRequest : class BrowserPluginGuest::PermissionRequest :
public base::RefCounted<BrowserPluginGuest::PermissionRequest> { public base::RefCounted<BrowserPluginGuest::PermissionRequest> {
public: public:
virtual void Respond(bool should_allow, const std::string& user_input) = 0; void Respond(bool should_allow, const std::string& user_input) {
if (!guest_)
return;
RespondImpl(should_allow, user_input);
}
virtual bool AllowedByDefault() const { virtual bool AllowedByDefault() const {
return false; return false;
} }
protected: protected:
PermissionRequest() { explicit PermissionRequest(const base::WeakPtr<BrowserPluginGuest>& guest)
: guest_(guest) {
RecordAction( RecordAction(
base::UserMetricsAction("BrowserPlugin.Guest.PermissionRequest")); base::UserMetricsAction("BrowserPlugin.Guest.PermissionRequest"));
} }
virtual ~PermissionRequest() {} virtual ~PermissionRequest() {}
virtual void RespondImpl(bool should_allow,
const std::string& user_input) = 0;
// Friend RefCounted so that the dtor can be non-public. // Friend RefCounted so that the dtor can be non-public.
friend class base::RefCounted<BrowserPluginGuest::PermissionRequest>; friend class base::RefCounted<BrowserPluginGuest::PermissionRequest>;
base::WeakPtr<BrowserPluginGuest> guest_;
}; };
class BrowserPluginGuest::DownloadRequest : public PermissionRequest { class BrowserPluginGuest::DownloadRequest : public PermissionRequest {
public: public:
explicit DownloadRequest(base::Callback<void(bool)> callback) DownloadRequest(const base::WeakPtr<BrowserPluginGuest>& guest,
: callback_(callback) { const base::Callback<void(bool)>& callback)
: PermissionRequest(guest),
callback_(callback) {
RecordAction( RecordAction(
base::UserMetricsAction("BrowserPlugin.Guest.PermissionRequest.Download")); base::UserMetricsAction("BrowserPlugin.Guest.PermissionRequest.Download"));
} }
virtual void Respond(bool should_allow, virtual void RespondImpl(bool should_allow,
const std::string& user_input) OVERRIDE { const std::string& user_input) OVERRIDE {
callback_.Run(should_allow); callback_.Run(should_allow);
} }
...@@ -96,21 +108,19 @@ class BrowserPluginGuest::DownloadRequest : public PermissionRequest { ...@@ -96,21 +108,19 @@ class BrowserPluginGuest::DownloadRequest : public PermissionRequest {
class BrowserPluginGuest::GeolocationRequest : public PermissionRequest { class BrowserPluginGuest::GeolocationRequest : public PermissionRequest {
public: public:
GeolocationRequest(GeolocationCallback callback, GeolocationRequest(const base::WeakPtr<BrowserPluginGuest>& guest,
int bridge_id, GeolocationCallback callback,
base::WeakPtrFactory<BrowserPluginGuest>* weak_ptr_factory) int bridge_id)
: callback_(callback), : PermissionRequest(guest),
bridge_id_(bridge_id), callback_(callback),
weak_ptr_factory_(weak_ptr_factory) { bridge_id_(bridge_id) {
RecordAction( RecordAction(
base::UserMetricsAction("BrowserPlugin.Guest.PermissionRequest.Geolocation")); base::UserMetricsAction("BrowserPlugin.Guest.PermissionRequest.Geolocation"));
} }
virtual void Respond(bool should_allow, virtual void RespondImpl(bool should_allow,
const std::string& user_input) OVERRIDE { const std::string& user_input) OVERRIDE {
base::WeakPtr<BrowserPluginGuest> guest(weak_ptr_factory_->GetWeakPtr()); WebContents* web_contents = guest_->embedder_web_contents();
WebContents* web_contents = guest->embedder_web_contents();
if (should_allow && web_contents) { if (should_allow && web_contents) {
// If renderer side embedder decides to allow gelocation, we need to check // If renderer side embedder decides to allow gelocation, we need to check
// if the app/embedder itself has geolocation access. // if the app/embedder itself has geolocation access.
...@@ -121,7 +131,7 @@ class BrowserPluginGuest::GeolocationRequest : public PermissionRequest { ...@@ -121,7 +131,7 @@ class BrowserPluginGuest::GeolocationRequest : public PermissionRequest {
if (geolocation_context) { if (geolocation_context) {
base::Callback<void(bool)> geolocation_callback = base::Bind( base::Callback<void(bool)> geolocation_callback = base::Bind(
&BrowserPluginGuest::SetGeolocationPermission, &BrowserPluginGuest::SetGeolocationPermission,
guest, guest_,
callback_, callback_,
bridge_id_); bridge_id_);
geolocation_context->RequestGeolocationPermission( geolocation_context->RequestGeolocationPermission(
...@@ -138,30 +148,29 @@ class BrowserPluginGuest::GeolocationRequest : public PermissionRequest { ...@@ -138,30 +148,29 @@ class BrowserPluginGuest::GeolocationRequest : public PermissionRequest {
} }
} }
} }
guest->SetGeolocationPermission(callback_, bridge_id_, false); guest_->SetGeolocationPermission(callback_, bridge_id_, false);
} }
private: private:
virtual ~GeolocationRequest() {} virtual ~GeolocationRequest() {}
base::Callback<void(bool)> callback_; base::Callback<void(bool)> callback_;
int bridge_id_; int bridge_id_;
base::WeakPtrFactory<BrowserPluginGuest>* weak_ptr_factory_;
}; };
class BrowserPluginGuest::MediaRequest : public PermissionRequest { class BrowserPluginGuest::MediaRequest : public PermissionRequest {
public: public:
MediaRequest(const MediaStreamRequest& request, MediaRequest(const base::WeakPtr<BrowserPluginGuest>& guest,
const MediaResponseCallback& callback, const MediaStreamRequest& request,
BrowserPluginGuest* guest) const MediaResponseCallback& callback)
: request_(request), : PermissionRequest(guest),
callback_(callback), request_(request),
guest_(guest) { callback_(callback) {
RecordAction( RecordAction(
base::UserMetricsAction("BrowserPlugin.Guest.PermissionRequest.Media")); base::UserMetricsAction("BrowserPlugin.Guest.PermissionRequest.Media"));
} }
virtual void Respond(bool should_allow, virtual void RespondImpl(bool should_allow,
const std::string& user_input) OVERRIDE { const std::string& user_input) OVERRIDE {
WebContentsImpl* web_contents = guest_->embedder_web_contents(); WebContentsImpl* web_contents = guest_->embedder_web_contents();
if (should_allow && web_contents) { if (should_allow && web_contents) {
// Re-route the request to the embedder's WebContents; the guest gets the // Re-route the request to the embedder's WebContents; the guest gets the
...@@ -177,20 +186,20 @@ class BrowserPluginGuest::MediaRequest : public PermissionRequest { ...@@ -177,20 +186,20 @@ class BrowserPluginGuest::MediaRequest : public PermissionRequest {
virtual ~MediaRequest() {} virtual ~MediaRequest() {}
MediaStreamRequest request_; MediaStreamRequest request_;
MediaResponseCallback callback_; MediaResponseCallback callback_;
BrowserPluginGuest* guest_;
}; };
class BrowserPluginGuest::NewWindowRequest : public PermissionRequest { class BrowserPluginGuest::NewWindowRequest : public PermissionRequest {
public: public:
NewWindowRequest(int instance_id, BrowserPluginGuest* guest) NewWindowRequest(const base::WeakPtr<BrowserPluginGuest>& guest,
: instance_id_(instance_id), int instance_id)
guest_(guest) { : PermissionRequest(guest),
instance_id_(instance_id) {
RecordAction( RecordAction(
base::UserMetricsAction("BrowserPlugin.Guest.PermissionRequest.NewWindow")); base::UserMetricsAction("BrowserPlugin.Guest.PermissionRequest.NewWindow"));
} }
virtual void Respond(bool should_allow, virtual void RespondImpl(bool should_allow,
const std::string& user_input) OVERRIDE { const std::string& user_input) OVERRIDE {
int embedder_render_process_id = int embedder_render_process_id =
guest_->embedder_web_contents()->GetRenderProcessHost()->GetID(); guest_->embedder_web_contents()->GetRenderProcessHost()->GetID();
BrowserPluginGuest* guest = BrowserPluginGuest* guest =
...@@ -209,19 +218,20 @@ class BrowserPluginGuest::NewWindowRequest : public PermissionRequest { ...@@ -209,19 +218,20 @@ class BrowserPluginGuest::NewWindowRequest : public PermissionRequest {
private: private:
virtual ~NewWindowRequest() {} virtual ~NewWindowRequest() {}
int instance_id_; int instance_id_;
BrowserPluginGuest* guest_;
}; };
class BrowserPluginGuest::JavaScriptDialogRequest : public PermissionRequest { class BrowserPluginGuest::JavaScriptDialogRequest : public PermissionRequest {
public: public:
JavaScriptDialogRequest(const DialogClosedCallback& callback) JavaScriptDialogRequest(const base::WeakPtr<BrowserPluginGuest>& guest,
: callback_(callback) { const DialogClosedCallback& callback)
: PermissionRequest(guest),
callback_(callback) {
RecordAction( RecordAction(
base::UserMetricsAction("BrowserPlugin.Guest.PermissionRequest.JSDialog")); base::UserMetricsAction("BrowserPlugin.Guest.PermissionRequest.JSDialog"));
} }
virtual void Respond(bool should_allow, virtual void RespondImpl(bool should_allow,
const std::string& user_input) OVERRIDE { const std::string& user_input) OVERRIDE {
callback_.Run(should_allow, base::UTF8ToUTF16(user_input)); callback_.Run(should_allow, base::UTF8ToUTF16(user_input));
} }
...@@ -232,21 +242,20 @@ class BrowserPluginGuest::JavaScriptDialogRequest : public PermissionRequest { ...@@ -232,21 +242,20 @@ class BrowserPluginGuest::JavaScriptDialogRequest : public PermissionRequest {
class BrowserPluginGuest::PointerLockRequest : public PermissionRequest { class BrowserPluginGuest::PointerLockRequest : public PermissionRequest {
public: public:
PointerLockRequest(BrowserPluginGuest* guest) explicit PointerLockRequest(const base::WeakPtr<BrowserPluginGuest>& guest)
: guest_(guest) { : PermissionRequest(guest) {
RecordAction( RecordAction(
base::UserMetricsAction("BrowserPlugin.Guest.PermissionRequest.PointerLock")); base::UserMetricsAction("BrowserPlugin.Guest.PermissionRequest.PointerLock"));
} }
virtual void Respond(bool should_allow, virtual void RespondImpl(bool should_allow,
const std::string& user_input) OVERRIDE { const std::string& user_input) OVERRIDE {
guest_->SendMessageToEmbedder( guest_->SendMessageToEmbedder(
new BrowserPluginMsg_SetMouseLock(guest_->instance_id(), should_allow)); new BrowserPluginMsg_SetMouseLock(guest_->instance_id(), should_allow));
} }
private: private:
virtual ~PointerLockRequest() {} virtual ~PointerLockRequest() {}
BrowserPluginGuest* guest_;
}; };
namespace { namespace {
...@@ -426,7 +435,14 @@ int BrowserPluginGuest::RequestPermission( ...@@ -426,7 +435,14 @@ int BrowserPluginGuest::RequestPermission(
scoped_refptr<BrowserPluginGuest::PermissionRequest> request, scoped_refptr<BrowserPluginGuest::PermissionRequest> request,
const base::DictionaryValue& request_info) { const base::DictionaryValue& request_info) {
if (!delegate_) { if (!delegate_) {
request->Respond(false, ""); // Let the stack unwind before we deny the permission request so that
// objects held by the permission request are not destroyed immediately
// after creation. This is to allow those same objects to be accessed again
// in the same scope without fear of use after freeing.
base::MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&BrowserPluginGuest::PermissionRequest::Respond,
request, false, ""));
return browser_plugin::kInvalidPermissionRequestID; return browser_plugin::kInvalidPermissionRequestID;
} }
...@@ -950,7 +966,8 @@ void BrowserPluginGuest::RequestNewWindowPermission( ...@@ -950,7 +966,8 @@ void BrowserPluginGuest::RequestNewWindowPermission(
WindowOpenDispositionToString(disposition))); WindowOpenDispositionToString(disposition)));
RequestPermission(BROWSER_PLUGIN_PERMISSION_TYPE_NEW_WINDOW, RequestPermission(BROWSER_PLUGIN_PERMISSION_TYPE_NEW_WINDOW,
new NewWindowRequest(guest->instance_id(), this), new NewWindowRequest(weak_ptr_factory_.GetWeakPtr(),
guest->instance_id()),
request_info); request_info);
} }
...@@ -1015,8 +1032,9 @@ void BrowserPluginGuest::AskEmbedderForGeolocationPermission( ...@@ -1015,8 +1032,9 @@ void BrowserPluginGuest::AskEmbedderForGeolocationPermission(
int request_id = int request_id =
RequestPermission(BROWSER_PLUGIN_PERMISSION_TYPE_GEOLOCATION, RequestPermission(BROWSER_PLUGIN_PERMISSION_TYPE_GEOLOCATION,
new GeolocationRequest( new GeolocationRequest(weak_ptr_factory_.GetWeakPtr(),
callback, bridge_id, &weak_ptr_factory_), callback,
bridge_id),
request_info); request_info);
DCHECK(bridge_id_to_request_id_map_.find(bridge_id) == DCHECK(bridge_id_to_request_id_map_.find(bridge_id) ==
...@@ -1414,7 +1432,7 @@ void BrowserPluginGuest::OnLockMouse(bool user_gesture, ...@@ -1414,7 +1432,7 @@ void BrowserPluginGuest::OnLockMouse(bool user_gesture,
web_contents()->GetLastCommittedURL().spec())); web_contents()->GetLastCommittedURL().spec()));
RequestPermission(BROWSER_PLUGIN_PERMISSION_TYPE_POINTER_LOCK, RequestPermission(BROWSER_PLUGIN_PERMISSION_TYPE_POINTER_LOCK,
new PointerLockRequest(this), new PointerLockRequest(weak_ptr_factory_.GetWeakPtr()),
request_info); request_info);
} }
...@@ -1694,7 +1712,9 @@ void BrowserPluginGuest::RequestMediaAccessPermission( ...@@ -1694,7 +1712,9 @@ void BrowserPluginGuest::RequestMediaAccessPermission(
base::Value::CreateStringValue(request.security_origin.spec())); base::Value::CreateStringValue(request.security_origin.spec()));
RequestPermission(BROWSER_PLUGIN_PERMISSION_TYPE_MEDIA, RequestPermission(BROWSER_PLUGIN_PERMISSION_TYPE_MEDIA,
new MediaRequest(request, callback, this), new MediaRequest(weak_ptr_factory_.GetWeakPtr(),
request,
callback),
request_info); request_info);
} }
...@@ -1723,7 +1743,8 @@ void BrowserPluginGuest::RunJavaScriptDialog( ...@@ -1723,7 +1743,8 @@ void BrowserPluginGuest::RunJavaScriptDialog(
base::Value::CreateStringValue(origin_url.spec())); base::Value::CreateStringValue(origin_url.spec()));
RequestPermission(BROWSER_PLUGIN_PERMISSION_TYPE_JAVASCRIPT_DIALOG, RequestPermission(BROWSER_PLUGIN_PERMISSION_TYPE_JAVASCRIPT_DIALOG,
new JavaScriptDialogRequest(callback), new JavaScriptDialogRequest(weak_ptr_factory_.GetWeakPtr(),
callback),
request_info); request_info);
} }
...@@ -1853,7 +1874,8 @@ void BrowserPluginGuest::DidRetrieveDownloadURLFromRequestId( ...@@ -1853,7 +1874,8 @@ void BrowserPluginGuest::DidRetrieveDownloadURLFromRequestId(
request_info.Set(browser_plugin::kURL, base::Value::CreateStringValue(url)); request_info.Set(browser_plugin::kURL, base::Value::CreateStringValue(url));
RequestPermission(BROWSER_PLUGIN_PERMISSION_TYPE_DOWNLOAD, RequestPermission(BROWSER_PLUGIN_PERMISSION_TYPE_DOWNLOAD,
new DownloadRequest(callback), new DownloadRequest(weak_ptr_factory_.GetWeakPtr(),
callback),
request_info); request_info);
} }
......
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