Skip to content

Commit bcb1562

Browse files
ArthurSonzogniChromium LUCI CQ
authored andcommitted
Fix dangling pointers in DBus during Shutdown.
@AliMariam reported the dangling pointer detector found a new dangling pointer when running tests on linux Workstation. The error is: ``` The memory was freed at: chromium#3 allocator_shim::internal::PartitionFree() chromium#4 bluez::BluezDBusThreadManager::~BluezDBusThreadManager() chromium#5 bluez::BluezDBusThreadManager::Shutdown() chromium#6 ChromeBrowserMainPartsLinux::PostDestroyThreads() chromium#7 content::BrowserMainLoop::ShutdownThreadsAndCleanUp() chromium#8 content::BrowserMainRunnerImpl::Shutdown() chromium#9 content::BrowserMain() chromium#10 content::RunBrowserProcessMain() chromium#11 content::ContentMainRunnerImpl::RunBrowser() chromium#12 content::ContentMainRunnerImpl::Run() chromium#13 content::RunContentProcess() chromium#14 content::ContentMain() chromium#15 ChromeMain The dangling raw_ptr was released at: chromium#3 base::internal::RawPtrBackupRefImpl<>::ReleaseInternal() chromium#4 dbus::ObjectManager::~ObjectManager() chromium#5 std::__Cr::__tuple_impl<>::~__tuple_impl() chromium#6 base::internal::BindState<>::Destroy() chromium#7 base::[...]::LazilyDeallocatedDeque<>::Ring::~Ring() chromium#8 base::[...]::TaskQueueImpl::UnregisterTaskQueue() chromium#9 base::[...]::SequenceManagerImpl::UnregisterTaskQueueImpl() chromium#10 base::sequence_manager::TaskQueue::ShutdownTaskQueue() chromium#11 content::BrowserTaskQueues::~BrowserTaskQueues() chromium#12 content::BrowserUIThreadScheduler::~BrowserUIThreadScheduler() chromium#13 content::BrowserTaskExecutor::[...]::~UIThreadExecutor() chromium#14 content::BrowserTaskExecutor::[...]::~UIThreadExecutor() chromium#15 content::BrowserTaskExecutor::Shutdown() chromium#16 content::ContentMainRunnerImpl::Shutdown() chromium#17 content::RunContentProcess() chromium#18 content::ContentMain() chromium#19 ChromeMain ``` Diagnostic: - `bluez::BluezDBusThreadManager` owns a `dbus::Bus` as `system_bus`. - `dbus::Bus` owns: - The set of `dbus::ObjectManager` as `object_manager_table_`. - The DBus task runner as `dbus_task_runner_`. - The `dbus::ObjectManager` references `dbus::Bus` via `bus_`. So far so good, the ownership is clear. The problem happens when calling `dbus::Bus::RemoveObjectManager`. Indeed this moves the ObjectManager out of `dbus::Bus` toward a callback to a new thread. This still works transitively, because the dbus::Bus owns the thread. The problem happens after a second transfer back to the original thread. Indeed, there is a race condition possible: Behavior without problems: ----------------------------------- ┌─────────────┐ ┌───────────┐ │Origin thread│ │DBus thread│ └──────┬──────┘ └─────┬─────┘ RemoveObjectManager() │ │────────────────────────────────>│ │ RemoveObjectManagerInternal() │<────────────────────────────────│ RemoveObjectManagerInternalHelper() │ ~ObjectManager() │ │ ┌─────┴─────┐ Shutdown DBus Thread ─────────────>│DBus thread│ Shutdown DBus Thread <─────────────│DBus thread│ │ └───────────┘ ~Bus ┌──────┴──────┐ │Origin thread│ └─────────────┘ Behavior with problems: ---------------------------------------- ┌─────────────┐ ┌───────────┐ │Origin thread│ │DBus thread│ └──────┬──────┘ └─────┬─────┘ RemoveObjectManager() │ │────────────────────────────────>│ │ RemoveObjectManagerInternal() │ ┌────────────│ │ │ ┌─────┴─────┐ Shutdown DBus Thread ─────────────>│DBus thread│ Shutdown DBus Thread <─────────────│DBus thread│ │ │ └───────────┘ ~Bus() │ │ │ │<───────────────────┘ RemoveObjectManagerInternalHelper() ~ObjectManager() ┌──────┴──────┐ │Origin thread│ └─────────────┘ ----------------------------------------------------------------- In the second case: ~Bus() is called before ~ObjectManager(). The fix is a use `ObjectManager::Cleanup()` to cleanup the raw_ptr while the object is still transitively owned by the object it referenced. Bug: chromium:1478759 Fixed: chromium:1478759 Change-Id: I4ac04d449ab8a7b860256c490f8ac878c1c5c7c5 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4839496 Reviewed-by: Ryo Hashimoto <[email protected]> Commit-Queue: Arthur Sonzogni <[email protected]> Cr-Commit-Position: refs/heads/main@{#1192343}
1 parent faed507 commit bcb1562

File tree

2 files changed

+18
-14
lines changed

2 files changed

+18
-14
lines changed

dbus/object_manager.cc

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,7 @@ scoped_refptr<ObjectManager> ObjectManager::Create(
5050
ObjectManager::ObjectManager(Bus* bus,
5151
const std::string& service_name,
5252
const ObjectPath& object_path)
53-
: bus_(bus),
54-
service_name_(service_name),
55-
object_path_(object_path),
56-
setup_success_(false),
57-
cleanup_called_(false) {
53+
: bus_(bus), service_name_(service_name), object_path_(object_path) {
5854
LOG_IF(FATAL, !object_path_.IsValid()) << object_path_.value();
5955
DVLOG(1) << "Creating ObjectManager for " << service_name_
6056
<< " " << object_path_.value();
@@ -175,15 +171,20 @@ void ObjectManager::CleanUp() {
175171
}
176172

177173
match_rule_.clear();
174+
// After Cleanup(), the Bus doesn't own `this` anymore and might be deleted
175+
// before `this`.
176+
bus_ = nullptr;
178177
}
179178

180179
bool ObjectManager::SetupMatchRuleAndFilter() {
181-
DCHECK(bus_);
182180
DCHECK(!setup_success_);
183-
bus_->AssertOnDBusThread();
184181

185-
if (cleanup_called_)
182+
if (cleanup_called_) {
186183
return false;
184+
}
185+
186+
DCHECK(bus_);
187+
bus_->AssertOnDBusThread();
187188

188189
if (!bus_->Connect() || !bus_->SetUpAsyncOperations())
189190
return false;
@@ -225,16 +226,17 @@ void ObjectManager::OnSetupMatchRuleAndFilterComplete(bool success) {
225226
<< ": Failed to set up match rule.";
226227
return;
227228
}
228-
229-
DCHECK(bus_);
230229
DCHECK(object_proxy_);
231230
DCHECK(setup_success_);
232-
bus_->AssertOnOriginThread();
233231

234232
// |object_proxy_| is no longer valid if the Bus was shut down before this
235233
// call. Don't initiate any other action from the origin thread.
236-
if (cleanup_called_)
234+
if (cleanup_called_) {
237235
return;
236+
}
237+
238+
DCHECK(bus_);
239+
bus_->AssertOnOriginThread();
238240

239241
object_proxy_->ConnectToSignal(
240242
kObjectManagerInterface, kObjectManagerInterfacesAdded,

dbus/object_manager.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -328,14 +328,16 @@ class CHROME_DBUS_EXPORT ObjectManager final
328328
// |service_name_owner_|.
329329
void UpdateServiceNameOwner(const std::string& new_owner);
330330

331+
// Valid in between the constructor and `CleanUp()`.
332+
// After Cleanup(), `this` lifetime might exceed Bus's one.
331333
raw_ptr<Bus> bus_;
332334
std::string service_name_;
333335
std::string service_name_owner_;
334336
std::string match_rule_;
335337
ObjectPath object_path_;
336338
raw_ptr<ObjectProxy, AcrossTasksDanglingUntriaged> object_proxy_;
337-
bool setup_success_;
338-
bool cleanup_called_;
339+
bool setup_success_ = false;
340+
bool cleanup_called_ = false;
339341

340342
// Maps the name of an interface to the implementation class used for
341343
// instantiating PropertySet structures for that interface's properties.

0 commit comments

Comments
 (0)