From 3b3e59185dc1e9a319d8ce20ac19c30a966a5a9c Mon Sep 17 00:00:00 2001 From: Alec Mouri Date: Mon, 26 Sep 2022 21:37:01 +0000 Subject: Fix use-after-free in SurfaceFlinger::doDump SurfaceFlinger::doDump previously contained two layer traversals, one on the main thread and one off the main thread. During concurrent dumpsys commands, the layer traversals may race with each other, which causes shared ownership of the underlying storage of a SortedVector containing the z-ordered list of layers. Because the implementation of SortedVector's STL iterators assumes that the underlying storage may be edited, this can cause the storage to be copied whenever SortedVector::begin and SortedVector::end are called, which means that SortedVector::begin() + SortedVector::size() == SortedVector::end() is not always true, which causes invalid iteration. In general, this use-after-free can happen as long as the off-main thread traversal exists in doDump(), because the traversal can run in parallel with any workload on the main thread that executes a layer traversal. So, this patch moves the traversal for dumping out the list of composition layers into the main thread. A future patch could explore either fixing SortedVector to fix judicious iterator invalidation, or building LayerVector on top of std::set, but either option is an invasive data structure change. Bug: 237291506 Test: Test script that calls dumpsys SurfaceFlinger on many threads Change-Id: I0748396519c924dc1b84113d44259f22d0d7ebd6 (cherry picked from commit 6761733ad1dd775f011588c59d5a6d210175c546) Merged-In: I0748396519c924dc1b84113d44259f22d0d7ebd6 --- services/surfaceflinger/SurfaceFlinger.cpp | 37 +++++++++++++++++++----------- services/surfaceflinger/SurfaceFlinger.h | 3 ++- 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index db2447c40b..0e1acb4154 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -5005,6 +5005,25 @@ status_t SurfaceFlinger::doDump(int fd, const DumpArgs& args, bool asProto) { const auto flag = args.empty() ? ""s : std::string(String8(args[0])); + // Traversal of drawing state must happen on the main thread. + // Otherwise, SortedVector may have shared ownership during concurrent + // traversals, which can result in use-after-frees. + std::string compositionLayers; + mScheduler + ->schedule([&] { + StringAppendF(&compositionLayers, "Composition layers\n"); + mDrawingState.traverseInZOrder([&](Layer* layer) { + auto* compositionState = layer->getCompositionState(); + if (!compositionState || !compositionState->isVisible) return; + + android::base::StringAppendF(&compositionLayers, "* Layer %p (%s)\n", layer, + layer->getDebugName() ? layer->getDebugName() + : ""); + compositionState->dump(compositionLayers); + }); + }) + .get(); + bool dumpLayers = true; { TimedLock lock(mStateLock, s2ns(1), __func__); @@ -5017,7 +5036,7 @@ status_t SurfaceFlinger::doDump(int fd, const DumpArgs& args, bool asProto) { (it->second)(args, asProto, result); dumpLayers = false; } else if (!asProto) { - dumpAllLocked(args, result); + dumpAllLocked(args, compositionLayers, result); } } @@ -5316,7 +5335,8 @@ void SurfaceFlinger::dumpOffscreenLayers(std::string& result) { result.append(future.get()); } -void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, std::string& result) const { +void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, const std::string& compositionLayers, + std::string& result) const { const bool colorize = !args.empty() && args[0] == String16("--color"); Colorizer colorizer(colorize); @@ -5367,18 +5387,7 @@ void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, std::string& result) co StringAppendF(&result, "Visible layers (count = %zu)\n", mNumLayers.load()); colorizer.reset(result); - { - StringAppendF(&result, "Composition layers\n"); - mDrawingState.traverseInZOrder([&](Layer* layer) { - auto* compositionState = layer->getCompositionState(); - if (!compositionState || !compositionState->isVisible) return; - - android::base::StringAppendF(&result, "* Layer %p (%s)\n", layer, - layer->getDebugName() ? layer->getDebugName() - : ""); - compositionState->dump(result); - }); - } + result.append(compositionLayers); colorizer.bold(result); StringAppendF(&result, "Displays (%zu entries)\n", mDisplays.size()); diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 83134a2ebc..9e0cee8fd4 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -1085,7 +1085,8 @@ private: /* * Debugging & dumpsys */ - void dumpAllLocked(const DumpArgs& args, std::string& result) const REQUIRES(mStateLock); + void dumpAllLocked(const DumpArgs& args, const std::string& compositionLayers, + std::string& result) const REQUIRES(mStateLock); void appendSfConfigString(std::string& result) const; void listLayersLocked(std::string& result) const; -- cgit v1.2.3-59-g8ed1b