|
| 1 | +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
| 2 | +From: Rouslan Solomakhin <rouslan@chromium.org> |
| 3 | +Date: Wed, 15 Apr 2020 23:03:07 +0000 |
| 4 | +Subject: Browser context owned callback. |
| 5 | + |
| 6 | +Before this patch, an unowned function pointer would be invoked |
| 7 | +asynchronously with a reference to the possibly freed reference to the |
| 8 | +browser context, which could cause use after free in certain |
| 9 | +circumstances. |
| 10 | + |
| 11 | +This patch makes the browser context own the callback and binds the |
| 12 | +function with a weak pointer, so freeing the browser context invalidates |
| 13 | +the weak pointer, which cancels the callback execution. |
| 14 | + |
| 15 | +After this patch, freeing the browser context aborts the asynchronous |
| 16 | +callback that dereferences the browser context, so the use after free |
| 17 | +is prevented. |
| 18 | + |
| 19 | +TBR=rouslan@chromium.org |
| 20 | + |
| 21 | +(cherry picked from commit 2d0aad1e7602a7076d86772cc159b891cf2cf03b) |
| 22 | + |
| 23 | +Bug: 1065298 |
| 24 | +Change-Id: Id6de3099a55c4505e94a8a6d21fb25d6d2b34c6c |
| 25 | +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2144311 |
| 26 | +Reviewed-by: Danyao Wang <danyao@chromium.org> |
| 27 | +Commit-Queue: Rouslan Solomakhin <rouslan@chromium.org> |
| 28 | +Cr-Original-Commit-Position: refs/heads/master@{#758404} |
| 29 | +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2151474 |
| 30 | +Reviewed-by: Rouslan Solomakhin <rouslan@chromium.org> |
| 31 | +Cr-Commit-Position: refs/branch-heads/4044@{#942} |
| 32 | +Cr-Branched-From: a6d9daf149a473ceea37f629c41d4527bf2055bd-refs/heads/master@{#737173} |
| 33 | + |
| 34 | +diff --git a/content/browser/payments/payment_app_provider_impl.cc b/content/browser/payments/payment_app_provider_impl.cc |
| 35 | +index 9a84066912f0cef926a941a69090af3749d09288..694d7f57ac3814cb8d99ceecd4d90477a4bdd26f 100644 |
| 36 | +--- a/content/browser/payments/payment_app_provider_impl.cc |
| 37 | ++++ b/content/browser/payments/payment_app_provider_impl.cc |
| 38 | +@@ -15,6 +15,7 @@ |
| 39 | + #include "base/metrics/histogram_macros.h" |
| 40 | + #include "base/strings/string_number_conversions.h" |
| 41 | + #include "base/strings/string_util.h" |
| 42 | ++#include "base/supports_user_data.h" |
| 43 | + #include "base/task/post_task.h" |
| 44 | + #include "base/token.h" |
| 45 | + #include "content/browser/payments/payment_app_context_impl.h" |
| 46 | +@@ -425,28 +426,65 @@ void OnInstallPaymentApp( |
| 47 | + } |
| 48 | + } |
| 49 | + |
| 50 | +-void CheckPermissionForPaymentApps( |
| 51 | +- BrowserContext* browser_context, |
| 52 | +- PaymentAppProvider::GetAllPaymentAppsCallback callback, |
| 53 | +- PaymentAppProvider::PaymentApps apps) { |
| 54 | +- DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 55 | ++// Callbacks for checking permissions asynchronously. Owned by the browser |
| 56 | ++// context to avoid using the browser context after it has been freed. Deleted |
| 57 | ++// after the callback is invoked. |
| 58 | ++// Sample usage: |
| 59 | ++// PostTask(&PermissionChecker::CheckPermissionForPaymentApps, |
| 60 | ++// PermissionChecker::Create(browser_context), std::move(callback)); |
| 61 | ++class PermissionChecker : public base::SupportsUserData::Data { |
| 62 | ++ public: |
| 63 | ++ static base::WeakPtr<PermissionChecker> Create( |
| 64 | ++ BrowserContext* browser_context) { |
| 65 | ++ auto owned = std::make_unique<PermissionChecker>(browser_context); |
| 66 | ++ auto weak_pointer_result = owned->weak_ptr_factory_.GetWeakPtr(); |
| 67 | ++ void* key = owned.get(); |
| 68 | ++ browser_context->SetUserData(key, std::move(owned)); |
| 69 | ++ return weak_pointer_result; |
| 70 | ++ } |
| 71 | ++ |
| 72 | ++ // Do not use this method directly! Use the static PermissionChecker::Create() |
| 73 | ++ // method instead. (The constructor must be public for std::make_unique<> in |
| 74 | ++ // the Create() method.) |
| 75 | ++ explicit PermissionChecker(BrowserContext* browser_context) |
| 76 | ++ : browser_context_(browser_context) {} |
| 77 | ++ ~PermissionChecker() override = default; |
| 78 | ++ |
| 79 | ++ // Disallow copy and assign. |
| 80 | ++ PermissionChecker(const PermissionChecker& other) = delete; |
| 81 | ++ PermissionChecker& operator=(const PermissionChecker& other) = delete; |
| 82 | ++ |
| 83 | ++ void CheckPermissionForPaymentApps( |
| 84 | ++ PaymentAppProvider::GetAllPaymentAppsCallback callback, |
| 85 | ++ PaymentAppProvider::PaymentApps apps) { |
| 86 | ++ DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 87 | + |
| 88 | +- PermissionController* permission_controller = |
| 89 | +- BrowserContext::GetPermissionController(browser_context); |
| 90 | +- DCHECK(permission_controller); |
| 91 | +- |
| 92 | +- PaymentAppProvider::PaymentApps permitted_apps; |
| 93 | +- for (auto& app : apps) { |
| 94 | +- GURL origin = app.second->scope.GetOrigin(); |
| 95 | +- if (permission_controller->GetPermissionStatus( |
| 96 | +- PermissionType::PAYMENT_HANDLER, origin, origin) == |
| 97 | +- blink::mojom::PermissionStatus::GRANTED) { |
| 98 | +- permitted_apps[app.first] = std::move(app.second); |
| 99 | ++ PermissionController* permission_controller = |
| 100 | ++ BrowserContext::GetPermissionController(browser_context_); |
| 101 | ++ DCHECK(permission_controller); |
| 102 | ++ |
| 103 | ++ PaymentAppProvider::PaymentApps permitted_apps; |
| 104 | ++ for (auto& app : apps) { |
| 105 | ++ GURL origin = app.second->scope.GetOrigin(); |
| 106 | ++ if (permission_controller->GetPermissionStatus( |
| 107 | ++ PermissionType::PAYMENT_HANDLER, origin, origin) == |
| 108 | ++ blink::mojom::PermissionStatus::GRANTED) { |
| 109 | ++ permitted_apps[app.first] = std::move(app.second); |
| 110 | ++ } |
| 111 | + } |
| 112 | ++ |
| 113 | ++ std::move(callback).Run(std::move(permitted_apps)); |
| 114 | ++ |
| 115 | ++ // Deletes this PermissionChecker object. |
| 116 | ++ browser_context_->RemoveUserData(/*key=*/this); |
| 117 | + } |
| 118 | + |
| 119 | +- std::move(callback).Run(std::move(permitted_apps)); |
| 120 | +-} |
| 121 | ++ private: |
| 122 | ++ // Owns this PermissionChecker object, so it's always valid. |
| 123 | ++ BrowserContext* browser_context_; |
| 124 | ++ |
| 125 | ++ base::WeakPtrFactory<PermissionChecker> weak_ptr_factory_{this}; |
| 126 | ++}; |
| 127 | + |
| 128 | + void AbortInvokePaymentApp(BrowserContext* browser_context, |
| 129 | + PaymentEventResponseType reason) { |
| 130 | +@@ -599,9 +637,11 @@ void PaymentAppProviderImpl::GetAllPaymentApps( |
| 131 | + |
| 132 | + RunOrPostTaskOnThread( |
| 133 | + FROM_HERE, ServiceWorkerContext::GetCoreThreadId(), |
| 134 | +- base::BindOnce(&GetAllPaymentAppsOnCoreThread, payment_app_context, |
| 135 | +- base::BindOnce(&CheckPermissionForPaymentApps, |
| 136 | +- browser_context, std::move(callback)))); |
| 137 | ++ base::BindOnce( |
| 138 | ++ &GetAllPaymentAppsOnCoreThread, payment_app_context, |
| 139 | ++ base::BindOnce(&PermissionChecker::CheckPermissionForPaymentApps, |
| 140 | ++ PermissionChecker::Create(browser_context), |
| 141 | ++ std::move(callback)))); |
| 142 | + } |
| 143 | + |
| 144 | + void PaymentAppProviderImpl::InvokePaymentApp( |
0 commit comments