SF: Fix UAF due to outliving idle timer
On Scheduler destruction, stop the timer and clear the callbacks, since
the RefreshRateConfigs may outlive the Scheduler.
On RefreshRateConfigs destruction, ensure the timer is destroyed before
the mutex/callbacks. Invoke the callback under lock.
Remove TestableSurfaceFlinger's subtle precondition that setupScheduler
must be called prior to FakeDisplayDeviceInjector for them to share
RefreshRateConfigs, which was not the case in DisplayModeSwitchingTest.
TestableScheduler is deleted through a Scheduler pointer, so ~Scheduler
should be virtual. Although ~TestableScheduler is trivial, this invoked
undefined behavior.
Bug: 213688734
Test: libsurfaceflinger_unittest --gtest_repeat=1000
Change-Id: Id23fafaf3d7071a5e28e275de386dd731a726006
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index 71d5631..a94952f 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -714,20 +714,19 @@
void RefreshRateConfigs::initializeIdleTimer() {
if (mConfig.idleTimerTimeoutMs > 0) {
- const auto getCallback = [this]() -> std::optional<IdleTimerCallbacks::Callbacks> {
- std::scoped_lock lock(mIdleTimerCallbacksMutex);
- if (!mIdleTimerCallbacks.has_value()) return {};
- return mConfig.supportKernelIdleTimer ? mIdleTimerCallbacks->kernel
- : mIdleTimerCallbacks->platform;
- };
-
mIdleTimer.emplace(
"IdleTimer", std::chrono::milliseconds(mConfig.idleTimerTimeoutMs),
- [getCallback] {
- if (const auto callback = getCallback()) callback->onReset();
+ [this] {
+ std::scoped_lock lock(mIdleTimerCallbacksMutex);
+ if (const auto callbacks = getIdleTimerCallbacks()) {
+ callbacks->onReset();
+ }
},
- [getCallback] {
- if (const auto callback = getCallback()) callback->onExpired();
+ [this] {
+ std::scoped_lock lock(mIdleTimerCallbacksMutex);
+ if (const auto callbacks = getIdleTimerCallbacks()) {
+ callbacks->onExpired();
+ }
});
}
}