Commit e6af5cab authored by Kevin Marshall's avatar Kevin Marshall Committed by Commit Bot

Reland "Fix thread safety issues with safe browsing DB."

This is a reland of 0ac8d89e
Original change's description:
> Fix thread safety issues with safe browsing DB.
> 
> Fix thread safety issues with safe browsing DB.
> The Safe Browsing DB has some sequence consistency issues, which
> is caught by the stricter WeakPtr semantics detailed on CL 2908073007.
> 
> * Delete SafeBrowsingDatabaseManager on IO thread.
> * Remove illegal dereferencing of IO-thread WeakPtr from DB thread in
>   V4Database::VerifyChecksumOnTaskRunner
> * Remove non-threadsafe accesses of |io_thread_| resident members from
>   the DB thread in V4LocalDatabaseManager.
> * Add IO-thread runloops to unit tests to handle database teardown.
> 
> 
> R=nparker@chromium.org
> CC=wez@chromium.org
> 
> Bug: 729716
> Change-Id: I1bc620d42aca2f1cc99e482b7776a628d783d390
> Reviewed-on: https://chromium-review.googlesource.com/523983
> Commit-Queue: Kevin Marshall <kmarshall@chromium.org>
> Reviewed-by: Charlie Harrison <csharrison@chromium.org>
> Reviewed-by: Greg Thompson <grt@chromium.org>
> Reviewed-by: Varun Khaneja <vakh@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#481735}

TBR=csharrison@chromium.org, grt@chromium.org, vakh@chromium.org

Bug: 729716, 742596
Change-Id: I4d67aa201c54be7a1e75a7375ac9861a29f1d87f
Reviewed-on: https://chromium-review.googlesource.com/571684Reviewed-by: default avatarKevin Marshall <kmarshall@chromium.org>
Commit-Queue: Kevin Marshall <kmarshall@chromium.org>
Cr-Commit-Position: refs/heads/master@{#486946}
parent f554301a
...@@ -81,8 +81,15 @@ class ResourceRequestDetectorTest : public testing::Test { ...@@ -81,8 +81,15 @@ class ResourceRequestDetectorTest : public testing::Test {
new StrictMock<safe_browsing::MockIncidentReceiver>()), new StrictMock<safe_browsing::MockIncidentReceiver>()),
mock_database_manager_(new StrictMock<MockSafeBrowsingDatabaseManager>), mock_database_manager_(new StrictMock<MockSafeBrowsingDatabaseManager>),
fake_resource_request_detector_( fake_resource_request_detector_(
mock_database_manager_, base::MakeUnique<FakeResourceRequestDetector>(
base::WrapUnique(mock_incident_receiver_)) {} mock_database_manager_,
base::WrapUnique(mock_incident_receiver_))) {}
void TearDown() override {
fake_resource_request_detector_.reset();
mock_database_manager_ = nullptr;
base::RunLoop().RunUntilIdle();
}
std::unique_ptr<net::URLRequest> GetTestURLRequest( std::unique_ptr<net::URLRequest> GetTestURLRequest(
const std::string& url, const std::string& url,
...@@ -135,7 +142,7 @@ class ResourceRequestDetectorTest : public testing::Test { ...@@ -135,7 +142,7 @@ class ResourceRequestDetectorTest : public testing::Test {
ResourceRequestInfo info = ResourceRequestInfo info =
ResourceRequestDetector::GetRequestInfo(request.get()); ResourceRequestDetector::GetRequestInfo(request.get());
fake_resource_request_detector_.ProcessResourceRequest(&info); fake_resource_request_detector_->ProcessResourceRequest(&info);
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
} }
...@@ -152,7 +159,7 @@ class ResourceRequestDetectorTest : public testing::Test { ...@@ -152,7 +159,7 @@ class ResourceRequestDetectorTest : public testing::Test {
ResourceRequestInfo info = ResourceRequestInfo info =
ResourceRequestDetector::GetRequestInfo(request.get()); ResourceRequestDetector::GetRequestInfo(request.get());
fake_resource_request_detector_.ProcessResourceRequest(&info); fake_resource_request_detector_->ProcessResourceRequest(&info);
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
ASSERT_TRUE(incident); ASSERT_TRUE(incident);
...@@ -168,7 +175,7 @@ class ResourceRequestDetectorTest : public testing::Test { ...@@ -168,7 +175,7 @@ class ResourceRequestDetectorTest : public testing::Test {
StrictMock<safe_browsing::MockIncidentReceiver>* mock_incident_receiver_; StrictMock<safe_browsing::MockIncidentReceiver>* mock_incident_receiver_;
scoped_refptr<MockSafeBrowsingDatabaseManager> mock_database_manager_; scoped_refptr<MockSafeBrowsingDatabaseManager> mock_database_manager_;
FakeResourceRequestDetector fake_resource_request_detector_; std::unique_ptr<FakeResourceRequestDetector> fake_resource_request_detector_;
private: private:
// UrlRequest requires a message loop. This provides one. // UrlRequest requires a message loop. This provides one.
......
...@@ -500,6 +500,7 @@ source_set("unit_tests_mobile") { ...@@ -500,6 +500,7 @@ source_set("unit_tests_mobile") {
":v4_test_util", ":v4_test_util",
"//base", "//base",
"//components/variations", "//components/variations",
"//content/test:test_support",
"//testing/gtest", "//testing/gtest",
"//url", "//url",
] ]
...@@ -531,6 +532,8 @@ source_set("whitelist_checker_client_unittest") { ...@@ -531,6 +532,8 @@ source_set("whitelist_checker_client_unittest") {
":whitelist_checker_client", ":whitelist_checker_client",
"//base:base", "//base:base",
"//base/test:test_support", "//base/test:test_support",
"//content/public/browser",
"//content/test:test_support",
"//testing/gmock:gmock", "//testing/gmock:gmock",
"//testing/gtest:gtest", "//testing/gtest:gtest",
] ]
......
...@@ -15,7 +15,11 @@ using content::BrowserThread; ...@@ -15,7 +15,11 @@ using content::BrowserThread;
namespace safe_browsing { namespace safe_browsing {
SafeBrowsingDatabaseManager::SafeBrowsingDatabaseManager() : enabled_(false) {} SafeBrowsingDatabaseManager::SafeBrowsingDatabaseManager()
: base::RefCountedDeleteOnSequence<SafeBrowsingDatabaseManager>(
content::BrowserThread::GetTaskRunnerForThread(
content::BrowserThread::IO)),
enabled_(false) {}
SafeBrowsingDatabaseManager::~SafeBrowsingDatabaseManager() { SafeBrowsingDatabaseManager::~SafeBrowsingDatabaseManager() {
DCHECK(!v4_get_hash_protocol_manager_); DCHECK(!v4_get_hash_protocol_manager_);
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
#include "base/gtest_prod_util.h" #include "base/gtest_prod_util.h"
#include "base/macros.h" #include "base/macros.h"
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted_delete_on_sequence.h"
#include "components/safe_browsing_db/hit_report.h" #include "components/safe_browsing_db/hit_report.h"
#include "components/safe_browsing_db/util.h" #include "components/safe_browsing_db/util.h"
#include "components/safe_browsing_db/v4_protocol_manager_util.h" #include "components/safe_browsing_db/v4_protocol_manager_util.h"
...@@ -41,7 +41,7 @@ class V4GetHashProtocolManager; ...@@ -41,7 +41,7 @@ class V4GetHashProtocolManager;
// Base class to either the locally-managed or a remotely-managed database. // Base class to either the locally-managed or a remotely-managed database.
class SafeBrowsingDatabaseManager class SafeBrowsingDatabaseManager
: public base::RefCountedThreadSafe<SafeBrowsingDatabaseManager> { : public base::RefCountedDeleteOnSequence<SafeBrowsingDatabaseManager> {
public: public:
// Callers requesting a result should derive from this class. // Callers requesting a result should derive from this class.
// The destructor should call db_manager->CancelCheck(client) if a // The destructor should call db_manager->CancelCheck(client) if a
...@@ -264,7 +264,8 @@ class SafeBrowsingDatabaseManager ...@@ -264,7 +264,8 @@ class SafeBrowsingDatabaseManager
virtual ~SafeBrowsingDatabaseManager(); virtual ~SafeBrowsingDatabaseManager();
friend class base::RefCountedThreadSafe<SafeBrowsingDatabaseManager>; friend class base::RefCountedDeleteOnSequence<SafeBrowsingDatabaseManager>;
friend class base::DeleteHelper<SafeBrowsingDatabaseManager>;
FRIEND_TEST_ALL_PREFIXES(SafeBrowsingDatabaseManagerTest, FRIEND_TEST_ALL_PREFIXES(SafeBrowsingDatabaseManagerTest,
CheckApiBlacklistUrlPrefixes); CheckApiBlacklistUrlPrefixes);
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "base/run_loop.h" #include "base/run_loop.h"
#include "base/single_thread_task_runner.h" #include "base/single_thread_task_runner.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/thread_task_runner_handle.h" #include "base/threading/thread_task_runner_handle.h"
#include "components/safe_browsing_db/test_database_manager.h" #include "components/safe_browsing_db/test_database_manager.h"
#include "components/safe_browsing_db/v4_protocol_manager_util.h" #include "components/safe_browsing_db/v4_protocol_manager_util.h"
...@@ -68,8 +69,9 @@ class SafeBrowsingDatabaseManagerTest : public testing::Test { ...@@ -68,8 +69,9 @@ class SafeBrowsingDatabaseManagerTest : public testing::Test {
} }
void TearDown() override { void TearDown() override {
base::RunLoop().RunUntilIdle();
db_manager_->StopOnIOThread(false); db_manager_->StopOnIOThread(false);
db_manager_ = nullptr;
base::RunLoop().RunUntilIdle();
} }
std::string GetStockV4GetHashResponse() { std::string GetStockV4GetHashResponse() {
......
...@@ -8,10 +8,12 @@ ...@@ -8,10 +8,12 @@
#include "base/logging.h" #include "base/logging.h"
#include "base/metrics/field_trial.h" #include "base/metrics/field_trial.h"
#include "base/run_loop.h"
#include "base/strings/stringprintf.h" #include "base/strings/stringprintf.h"
#include "base/time/time.h" #include "base/time/time.h"
#include "components/safe_browsing_db/safe_browsing_api_handler.h" #include "components/safe_browsing_db/safe_browsing_api_handler.h"
#include "components/variations/variations_associated_data.h" #include "components/variations/variations_associated_data.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
namespace safe_browsing { namespace safe_browsing {
...@@ -37,6 +39,11 @@ class RemoteDatabaseManagerTest : public testing::Test { ...@@ -37,6 +39,11 @@ class RemoteDatabaseManagerTest : public testing::Test {
db_ = new RemoteSafeBrowsingDatabaseManager(); db_ = new RemoteSafeBrowsingDatabaseManager();
} }
void TearDown() override {
db_ = nullptr;
base::RunLoop().RunUntilIdle();
}
// Setup the two field trial params. These are read in db_'s ctor. // Setup the two field trial params. These are read in db_'s ctor.
void SetFieldTrialParams(const std::string types_to_check_val) { void SetFieldTrialParams(const std::string types_to_check_val) {
// Destroy the existing FieldTrialList before creating a new one to avoid // Destroy the existing FieldTrialList before creating a new one to avoid
...@@ -59,6 +66,7 @@ class RemoteDatabaseManagerTest : public testing::Test { ...@@ -59,6 +66,7 @@ class RemoteDatabaseManagerTest : public testing::Test {
group_name, params)); group_name, params));
} }
content::TestBrowserThreadBundle thread_bundle_;
std::unique_ptr<base::FieldTrialList> field_trials_; std::unique_ptr<base::FieldTrialList> field_trials_;
TestSafeBrowsingApiHandler api_handler_; TestSafeBrowsingApiHandler api_handler_;
scoped_refptr<RemoteSafeBrowsingDatabaseManager> db_; scoped_refptr<RemoteSafeBrowsingDatabaseManager> db_;
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "base/lazy_instance.h" #include "base/lazy_instance.h"
#include "base/memory/ptr_util.h" #include "base/memory/ptr_util.h"
#include "base/metrics/histogram_macros.h" #include "base/metrics/histogram_macros.h"
#include "base/task_runner_util.h"
#include "base/threading/thread_task_runner_handle.h" #include "base/threading/thread_task_runner_handle.h"
#include "components/safe_browsing_db/v4_database.h" #include "components/safe_browsing_db/v4_database.h"
#include "content/public/browser/browser_thread.h" #include "content/public/browser/browser_thread.h"
...@@ -30,6 +31,19 @@ base::LazyInstance<std::unique_ptr<V4DatabaseFactory>>::Leaky g_db_factory = ...@@ -30,6 +31,19 @@ base::LazyInstance<std::unique_ptr<V4DatabaseFactory>>::Leaky g_db_factory =
base::LazyInstance<std::unique_ptr<V4StoreFactory>>::Leaky g_store_factory = base::LazyInstance<std::unique_ptr<V4StoreFactory>>::Leaky g_store_factory =
LAZY_INSTANCE_INITIALIZER; LAZY_INSTANCE_INITIALIZER;
// Verifies the checksums on a collection of stores.
// Returns the IDs of stores whose checksums failed to verify.
std::vector<ListIdentifier> VerifyChecksums(
std::vector<std::pair<ListIdentifier, V4Store*>> stores) {
std::vector<ListIdentifier> stores_to_reset;
for (const auto& store_map_iter : stores) {
if (!store_map_iter.second->VerifyChecksum()) {
stores_to_reset.push_back(store_map_iter.first);
}
}
return stores_to_reset;
}
} // namespace } // namespace
std::unique_ptr<V4Database> V4DatabaseFactory::Create( std::unique_ptr<V4Database> V4DatabaseFactory::Create(
...@@ -254,28 +268,19 @@ void V4Database::ResetStores( ...@@ -254,28 +268,19 @@ void V4Database::ResetStores(
void V4Database::VerifyChecksum( void V4Database::VerifyChecksum(
DatabaseReadyForUpdatesCallback db_ready_for_updates_callback) { DatabaseReadyForUpdatesCallback db_ready_for_updates_callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO); DCHECK_CURRENTLY_ON(BrowserThread::IO);
// TODO(vakh): Consider using PostTaskAndReply instead.
const scoped_refptr<base::SingleThreadTaskRunner> callback_task_runner =
base::ThreadTaskRunnerHandle::Get();
db_task_runner_->PostTask(
FROM_HERE,
base::Bind(&V4Database::VerifyChecksumOnTaskRunner,
weak_factory_on_io_.GetWeakPtr(), callback_task_runner,
db_ready_for_updates_callback));
}
void V4Database::VerifyChecksumOnTaskRunner( // Make a threadsafe copy of store_map_ w/raw pointers that we can hand to
const scoped_refptr<base::SingleThreadTaskRunner>& callback_task_runner, // the DB thread. The V4Stores ptrs are guaranteed to be valid because their
DatabaseReadyForUpdatesCallback db_ready_for_updates_callback) { // deletion would be sequenced on the DB thread, after this posted task is
std::vector<ListIdentifier> stores_to_reset; // serviced.
for (const auto& store_map_iter : *store_map_) { std::vector<std::pair<ListIdentifier, V4Store*>> stores;
if (!store_map_iter.second->VerifyChecksum()) { for (const auto& next_store : *store_map_) {
stores_to_reset.push_back(store_map_iter.first); stores.push_back(std::make_pair(next_store.first, next_store.second.get()));
}
} }
callback_task_runner->PostTask( base::PostTaskAndReplyWithResult(db_task_runner_.get(), FROM_HERE,
FROM_HERE, base::Bind(db_ready_for_updates_callback, stores_to_reset)); base::Bind(&VerifyChecksums, stores),
db_ready_for_updates_callback);
} }
bool V4Database::IsStoreAvailable(const ListIdentifier& identifier) const { bool V4Database::IsStoreAvailable(const ListIdentifier& identifier) const {
......
...@@ -167,7 +167,11 @@ class V4Database { ...@@ -167,7 +167,11 @@ class V4Database {
V4Database(const scoped_refptr<base::SequencedTaskRunner>& db_task_runner, V4Database(const scoped_refptr<base::SequencedTaskRunner>& db_task_runner,
std::unique_ptr<StoreMap> store_map); std::unique_ptr<StoreMap> store_map);
// Map of ListIdentifier to the V4Store. // The collection of V4Stores, keyed by ListIdentifier.
// The map itself lives on the V4Database's parent thread, but its V4Store
// objects live on the db_task_runner_thread.
// TODO(vakh): Consider writing a container object which encapsulates or
// harmonizes thread affinity for the associative container and the data.
const std::unique_ptr<StoreMap> store_map_; const std::unique_ptr<StoreMap> store_map_;
private: private:
......
...@@ -334,7 +334,6 @@ class V4LocalDatabaseManager : public SafeBrowsingDatabaseManager { ...@@ -334,7 +334,6 @@ class V4LocalDatabaseManager : public SafeBrowsingDatabaseManager {
base::WeakPtrFactory<V4LocalDatabaseManager> weak_factory_; base::WeakPtrFactory<V4LocalDatabaseManager> weak_factory_;
friend class base::RefCountedThreadSafe<V4LocalDatabaseManager>;
DISALLOW_COPY_AND_ASSIGN(V4LocalDatabaseManager); DISALLOW_COPY_AND_ASSIGN(V4LocalDatabaseManager);
}; // class V4LocalDatabaseManager }; // class V4LocalDatabaseManager
......
...@@ -10,6 +10,8 @@ ...@@ -10,6 +10,8 @@
#include "base/test/test_mock_time_task_runner.h" #include "base/test/test_mock_time_task_runner.h"
#include "base/threading/thread_task_runner_handle.h" #include "base/threading/thread_task_runner_handle.h"
#include "components/safe_browsing_db/test_database_manager.h" #include "components/safe_browsing_db/test_database_manager.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/test/test_browser_thread.h"
#include "testing/gmock/include/gmock/gmock.h" #include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
...@@ -39,17 +41,22 @@ class MockSafeBrowsingDatabaseManager : public TestSafeBrowsingDatabaseManager { ...@@ -39,17 +41,22 @@ class MockSafeBrowsingDatabaseManager : public TestSafeBrowsingDatabaseManager {
class WhitelistCheckerClientTest : public testing::Test { class WhitelistCheckerClientTest : public testing::Test {
public: public:
WhitelistCheckerClientTest() : target_url_("http://foo.bar"){}; WhitelistCheckerClientTest() : target_url_("http://foo.bar") {}
void SetUp() override { void SetUp() override {
database_manager_ = new MockSafeBrowsingDatabaseManager; database_manager_ = new MockSafeBrowsingDatabaseManager;
task_runner_ = new base::TestMockTimeTaskRunner(base::Time::Now(), task_runner_ = new base::TestMockTimeTaskRunner(base::Time::Now(),
base::TimeTicks::Now()); base::TimeTicks::Now());
message_loop_.reset(new base::MessageLoop); message_loop_.reset(new base::MessageLoop);
io_thread_ = base::MakeUnique<content::TestBrowserThread>(
content::BrowserThread::IO, base::MessageLoop::current());
message_loop_->SetTaskRunner(task_runner_); message_loop_->SetTaskRunner(task_runner_);
} }
void TearDown() override { void TearDown() override {
database_manager_ = nullptr;
base::RunLoop().RunUntilIdle();
// Verify no callback is remaining. // Verify no callback is remaining.
// TODO(nparker): We should somehow EXPECT that no entry is remaining, // TODO(nparker): We should somehow EXPECT that no entry is remaining,
// rather than just invoking it. // rather than just invoking it.
...@@ -60,6 +67,9 @@ class WhitelistCheckerClientTest : public testing::Test { ...@@ -60,6 +67,9 @@ class WhitelistCheckerClientTest : public testing::Test {
GURL target_url_; GURL target_url_;
scoped_refptr<MockSafeBrowsingDatabaseManager> database_manager_; scoped_refptr<MockSafeBrowsingDatabaseManager> database_manager_;
// Needed for |database_manager_| teardown tasks.
std::unique_ptr<content::TestBrowserThread> io_thread_;
std::unique_ptr<base::MessageLoop> message_loop_; std::unique_ptr<base::MessageLoop> message_loop_;
scoped_refptr<base::TestMockTimeTaskRunner> task_runner_; scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
}; };
......
...@@ -193,7 +193,17 @@ class SubresourceFilterSafeBrowsingActivationThrottleTest ...@@ -193,7 +193,17 @@ class SubresourceFilterSafeBrowsingActivationThrottleTest
void TearDown() override { void TearDown() override {
client_.reset(); client_.reset();
// RunUntilIdle() must be called multiple times to flush any outstanding
// cross-thread interactions.
// TODO(csharrison): Clean up test teardown logic.
RunUntilIdle();
RunUntilIdle();
// RunUntilIdle() called once more, to delete the database on the IO thread.
fake_safe_browsing_database_ = nullptr;
RunUntilIdle(); RunUntilIdle();
content::RenderViewHostTestHarness::TearDown(); content::RenderViewHostTestHarness::TearDown();
} }
...@@ -316,8 +326,8 @@ class SubresourceFilterSafeBrowsingActivationThrottleTest ...@@ -316,8 +326,8 @@ class SubresourceFilterSafeBrowsingActivationThrottleTest
void UsePassThroughThrottle() { fake_safe_browsing_database_ = nullptr; } void UsePassThroughThrottle() { fake_safe_browsing_database_ = nullptr; }
void RunUntilIdle() { void RunUntilIdle() {
test_io_task_runner_->RunUntilIdle();
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
test_io_task_runner_->RunUntilIdle();
} }
content::NavigationSimulator* navigation_simulator() { content::NavigationSimulator* navigation_simulator() {
......
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