CVE-2023-40115 AI-generated analysis
platform/packages/modules/StatsD, statsd native service (StatsService pushed-event reader thread)
Replaces the detached log-reader thread spawned in the StatsService constructor with a class-member std::thread. The thread is told to stop (an atomic flag plus a wake-up event pushed into the queue) and joined in the StatsService destructor, so readLogs() can no longer run against a destroyed StatsService.
Root cause
In statsd/src/StatsService.cpp the constructor created `std::thread pushedEventThread([this] { readLogs(); })` and immediately called `detach()`. readLogs() loops forever: it calls `mEventQueue->waitPop()` and then passes the event on for processing, so it uses `this` and its members for as long as the thread runs. The destructor was empty and nothing tied the thread to the StatsService object. So, as the commit message puts it, pushedEventThread references class members after detaching, and a destroyed StatsService could still be accessed by the running thread.
How the fix works
- statsd/src/StatsService.h: adds `std::unique_ptr<std::thread> mLogsReaderThread`, `std::atomic<bool> mIsStopRequested = false`, and the private method `stopReadingLogs()`.
- statsd/src/StatsService.cpp:StatsService::StatsService: when mEventQueue != nullptr, the reader thread is now created as `mLogsReaderThread = std::make_unique<std::thread>([this] { readLogs(); })` instead of as a detached local thread.
- statsd/src/StatsService.cpp:StatsService::stopReadingLogs: sets mIsStopRequested = true, then pushes a dummy `LogEvent(uid=0, pid=0)` into mEventQueue via `push()` so that a reader blocked in `waitPop()` wakes up.
- statsd/src/StatsService.cpp:StatsService::readLogs: after `waitPop()` returns, checks `mIsStopRequested` and breaks out of the `while (1)` loop before the event is processed.
- statsd/src/StatsService.cpp:StatsService::~StatsService: when mEventQueue != nullptr, calls stopReadingLogs() and then `mLogsReaderThread->join()`. The destructor body therefore blocks until readLogs() has exited, while all members are still alive (members are destroyed after the destructor body).
Completeness
- Covers the single detached thread created in the StatsService constructor. That thread is now joined in the destructor body, before any member is destroyed.
- The stop path relies on the dummy event push to unblock `waitPop()`. The diff does not use push()'s result, and the material does not show LogEventQueue semantics, so it cannot tell whether the wake-up can be lost (for example, if the queue is full). If it were lost, `join()` would block rather than lead to a use-after-free.
- `mIsStopRequested` is only checked after `waitPop()` returns. This is enough because stopReadingLogs() sets the flag before it pushes the wake-up event.
- This commit changes no other thread creation sites. The android-security-14.0.0_r29 context also shows a call to `onStatsdInitCompletedHandlerTermination()` in the destructor, which is not in this commit. The material does not show where it comes from or why.
Behavior changes
- Destroying a StatsService now blocks until the readLogs thread has seen the stop request and exited (join), instead of returning immediately.
- During shutdown a synthetic LogEvent (uid 0, pid 0) is pushed into the LogEventQueue. readLogs takes it off the queue and then breaks on the stop check, so it is not processed.
- readLogs() is no longer an unconditional infinite loop: it ends once mIsStopRequested is set.
Per major
- Android 14: Same fix: commit f6a9ca981b93 landed in android-security-14.0.0_r2 (a cherry-pick of an internal change, marked Ignore-AOSP-First). At android-security-14.0.0_r29, 6 of the 8 patched-region lines are present. The code shown there includes the member thread, the stop flag, stopReadingLogs() and the destructor join. The material does not identify the 2 differing lines.
- Android 15: Not affected per the bulletin. At android-security-15.0.0_r17, 6 of the 8 patched-region lines are present.
- Android 16: Not affected per the bulletin. At android-security-16.0.0_r8, 6 of the 8 patched-region lines are present.
- Android 17: Not affected per the bulletin. At android-security-17.0.0_r1, 6 of the 8 patched-region lines are present.
Unknowns
- The material does not show LogEventQueue::push()/waitPop() semantics (for example, the return value or behaviour when the queue is full), so it is unknown whether the shutdown wake-up can be missed.
- The material does not show whether any production path in the statsd daemon destroys StatsService while the process keeps running.
- The material does not show how the fix reached Android 11, 12, 12L and 13.
- The material does not show which 2 of the 8 patched-region lines differ in the latest tags of 14 through 17.