Commit be4e3200 authored by Colin Blundell's avatar Colin Blundell Committed by Chromium LUCI CQ

[Safe Browsing] Fix crash in token fetching logic

When SafeBrowsingTokenFetchTracker is destroyed, it runs all of its
outstanding client callbacks. However, when an access token is fetched
SafeBrowsingTokenFetchTracker calls the corresponding client callback
(which is a base::OnceCallback) *before* it removes that callback from
the set of outstanding callbacks. The combination of these facts means
that if the SafeBrowsingTokenFetchTracker is destroyed from within a
client callback, a crash will occur. This indeed occurs in production
(see crashes linked in bug, and note that this is behavior that dates
back from the introduction of this code in SafeBrowsingTokenFetcher).

This CL adds a test that exhibits the problematic flow and fixes the
crash by removing the callback from the set of outstanding callbacks
*before* running that callback on access token fetch. The test crashes
without the fix.

Bug: 1168599
Change-Id: I006bde8e48531b9912fa779a5c6c6a27acaf6fa1
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2640375Reviewed-by: default avatarXinghui Lu <xinghuilu@chromium.org>
Commit-Queue: Colin Blundell <blundell@chromium.org>
Cr-Commit-Position: refs/heads/master@{#846022}
parent 12db6b66
...@@ -57,11 +57,16 @@ void SafeBrowsingTokenFetchTracker::OnTokenFetchTimeout( ...@@ -57,11 +57,16 @@ void SafeBrowsingTokenFetchTracker::OnTokenFetchTimeout(
void SafeBrowsingTokenFetchTracker::Finish(int request_id, void SafeBrowsingTokenFetchTracker::Finish(int request_id,
const std::string& access_token) { const std::string& access_token) {
if (callbacks_.contains(request_id)) { if (!callbacks_.contains(request_id))
std::move(callbacks_[request_id]).Run(access_token); return;
}
// Remove the callback from the map before running it so that this object
// doesn't try to run this callback again if deleted from within the
// callback.
auto callback = std::move(callbacks_[request_id]);
callbacks_.erase(request_id); callbacks_.erase(request_id);
std::move(callback).Run(access_token);
} }
} // namespace safe_browsing } // namespace safe_browsing
...@@ -87,6 +87,28 @@ TEST_F(SafeBrowsingTokenFetchTrackerTest, FetcherDestruction) { ...@@ -87,6 +87,28 @@ TEST_F(SafeBrowsingTokenFetchTrackerTest, FetcherDestruction) {
EXPECT_EQ(access_token2, ""); EXPECT_EQ(access_token2, "");
} }
// Verifies that destruction of a SafeBrowsingTokenFetchTracker instance from
// within the client callback that the token was fetched doesn't cause a crash.
TEST_F(SafeBrowsingTokenFetchTrackerTest,
FetcherDestroyedFromWithinOnTokenFetchedCallback) {
// Destroyed in the token fetch callback.
auto* fetcher = new SafeBrowsingTokenFetchTracker();
std::string access_token;
int request_id = fetcher->StartTrackingTokenFetch(
base::BindOnce(
[](std::string* target_token, SafeBrowsingTokenFetchTracker* fetcher,
const std::string& token) {
*target_token = token;
delete fetcher;
},
&access_token, fetcher),
base::BindOnce([](int request_id) {}));
fetcher->OnTokenFetchComplete(request_id, "token");
EXPECT_EQ(access_token, "token");
}
TEST_F(SafeBrowsingTokenFetchTrackerTest, Timeout) { TEST_F(SafeBrowsingTokenFetchTrackerTest, Timeout) {
SafeBrowsingTokenFetchTracker fetcher; SafeBrowsingTokenFetchTracker fetcher;
std::string access_token1 = "dummy_value1"; std::string access_token1 = "dummy_value1";
......
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