Commit 31391ba3 authored by Robbie McElrath's avatar Robbie McElrath Committed by Commit Bot

Fix race condition in net::SerialWorker.

net::SerialWorker is a RefCountedThreadSafe object that also uses
WeakPtrFactory to create WeakPtrs of itself. To run whatever work it's
supposed to run, it PostTaskWithTraitsAndReply's to another sequence to
do the work there, and then gets called back when the work is complete.
The work callback holds a (refcounted) reference to the SerialWorker,
and the reply callback is bound to a WeakPtr to ensure the SerialWorker
will still be deleted if posting the reply fails. WeakPtr enforces that
all dereferences to it, as well as its invalidation, must happen on the
same sequence to avoid races. When the reply callback is being called,
it dereferences the SerialWorker WeakPtr on the original sequence,
which binds that WeakPtr to that sequence. It's possible for the
SerialWorker’s owner to release it after the WeakPtr has been checked,
but before the worker function has returned, in which case the worker
function will have the last reference to the SerialWorker. In that case,
when the worker function finally does return, it will delete the
SerialWorker, which will delete its WeakPtrFactory, which will
invalidate existing WeakPtrs. However, the WeakPtr was previously
dereferenced on the SerialWorker sequence, and now it’s being
invalidated on the worker sequence, which is invalid.

To fix this, this CL makes SerialWorker a RefCountedDeleteOnSequence to
ensure the deletion always happens in the right sequence.

Bug: 882610
Change-Id: If738d3724384ffd5ac210130415bce7262932feb
Reviewed-on: https://chromium-review.googlesource.com/c/1336066
Commit-Queue: Robbie McElrath <rmcelrath@chromium.org>
Reviewed-by: default avatarEric Roman <eroman@chromium.org>
Reviewed-by: default avatarEric Orth <ericorth@chromium.org>
Cr-Commit-Position: refs/heads/master@{#608223}
parent 342d16b4
...@@ -189,6 +189,7 @@ TEST(DnsConfigServicePosixTest, DestroyWhileJobsWorking) { ...@@ -189,6 +189,7 @@ TEST(DnsConfigServicePosixTest, DestroyWhileJobsWorking) {
new internal::DnsConfigServicePosix()); new internal::DnsConfigServicePosix());
service->ReadConfig(base::Bind(&DummyConfigCallback)); service->ReadConfig(base::Bind(&DummyConfigCallback));
service.reset(); service.reset();
scoped_task_environment.RunUntilIdle();
base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(1000)); base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(1000));
} }
...@@ -215,6 +216,7 @@ class DnsConfigServicePosixTest : public testing::Test { ...@@ -215,6 +216,7 @@ class DnsConfigServicePosixTest : public testing::Test {
void TearDown() override { ASSERT_TRUE(base::DeleteFile(temp_file_, false)); } void TearDown() override { ASSERT_TRUE(base::DeleteFile(temp_file_, false)); }
base::test::ScopedTaskEnvironment scoped_task_environment_;
bool seen_config_; bool seen_config_;
base::FilePath temp_file_; base::FilePath temp_file_;
std::unique_ptr<DnsConfigServicePosix> service_; std::unique_ptr<DnsConfigServicePosix> service_;
...@@ -223,11 +225,9 @@ class DnsConfigServicePosixTest : public testing::Test { ...@@ -223,11 +225,9 @@ class DnsConfigServicePosixTest : public testing::Test {
// Regression test for https://crbug.com/704662. // Regression test for https://crbug.com/704662.
TEST_F(DnsConfigServicePosixTest, ChangeConfigMultipleTimes) { TEST_F(DnsConfigServicePosixTest, ChangeConfigMultipleTimes) {
base::test::ScopedTaskEnvironment scoped_task_environment;
service_->WatchConfig(base::Bind(&DnsConfigServicePosixTest::OnConfigChanged, service_->WatchConfig(base::Bind(&DnsConfigServicePosixTest::OnConfigChanged,
base::Unretained(this))); base::Unretained(this)));
scoped_task_environment.RunUntilIdle(); scoped_task_environment_.RunUntilIdle();
for (int i = 0; i < 5; i++) { for (int i = 0; i < 5; i++) {
service_->OnConfigChanged(true); service_->OnConfigChanged(true);
...@@ -235,7 +235,7 @@ TEST_F(DnsConfigServicePosixTest, ChangeConfigMultipleTimes) { ...@@ -235,7 +235,7 @@ TEST_F(DnsConfigServicePosixTest, ChangeConfigMultipleTimes) {
// called if the new config is different from the old one, so this can't be // called if the new config is different from the old one, so this can't be
// ExpectChange(). // ExpectChange().
base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(50)); base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(50));
scoped_task_environment.RunUntilIdle(); scoped_task_environment_.RunUntilIdle();
} }
// There should never be more than 4 nameservers in a real config. // There should never be more than 4 nameservers in a real config.
......
...@@ -7,11 +7,16 @@ ...@@ -7,11 +7,16 @@
#include "base/bind.h" #include "base/bind.h"
#include "base/location.h" #include "base/location.h"
#include "base/task/post_task.h" #include "base/task/post_task.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/threading/thread_task_runner_handle.h" #include "base/threading/thread_task_runner_handle.h"
namespace net { namespace net {
SerialWorker::SerialWorker() : state_(IDLE), weak_factory_(this) {} SerialWorker::SerialWorker()
: base::RefCountedDeleteOnSequence<SerialWorker>(
base::SequencedTaskRunnerHandle::Get()),
state_(IDLE),
weak_factory_(this) {}
SerialWorker::~SerialWorker() = default; SerialWorker::~SerialWorker() = default;
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
#include "base/compiler_specific.h" #include "base/compiler_specific.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 "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h" #include "base/sequence_checker.h"
#include "base/task/task_traits.h" #include "base/task/task_traits.h"
...@@ -34,7 +34,7 @@ namespace net { ...@@ -34,7 +34,7 @@ namespace net {
// This implementation avoids locking by using the |state_| member to ensure // This implementation avoids locking by using the |state_| member to ensure
// that |DoWork| and |OnWorkFinished| cannot execute in parallel. // that |DoWork| and |OnWorkFinished| cannot execute in parallel.
class NET_EXPORT_PRIVATE SerialWorker class NET_EXPORT_PRIVATE SerialWorker
: public base::RefCountedThreadSafe<SerialWorker> { : public base::RefCountedDeleteOnSequence<SerialWorker> {
public: public:
SerialWorker(); SerialWorker();
...@@ -48,7 +48,8 @@ class NET_EXPORT_PRIVATE SerialWorker ...@@ -48,7 +48,8 @@ class NET_EXPORT_PRIVATE SerialWorker
bool IsCancelled() const { return state_ == CANCELLED; } bool IsCancelled() const { return state_ == CANCELLED; }
protected: protected:
friend class base::RefCountedThreadSafe<SerialWorker>; friend class base::DeleteHelper<SerialWorker>;
friend class base::RefCountedDeleteOnSequence<SerialWorker>;
// protected to allow sub-classing, but prevent deleting // protected to allow sub-classing, but prevent deleting
virtual ~SerialWorker(); virtual ~SerialWorker();
......
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