Skip to content

TextureCache: fix use-after-free in CleanTimer() - #27972

Merged
thexai merged 1 commit into
xbmc:masterfrom
sampsonc:fix/texturecache-uaf-cleantimer
Mar 15, 2026
Merged

thexai merged 1 commit into
xbmc:masterfrom
sampsonc:fix/texturecache-uaf-cleantimer

Conversation

@sampsonc

@sampsonc sampsonc commented Mar 9, 2026

Copy link
Copy Markdown
Contributor

Closes #27971

What

CTextureCache::CleanTimer() was submitting a [this] lambda to the global job manager (CServiceBroker::GetJobManager()). Deinitialize() only called CancelJobs(), which clears the local CJobQueue — it has no effect on jobs already handed to the global manager. Additionally, m_cleanTimer was never stopped in Deinitialize(), so the timer could fire and enqueue a new global job after teardown had begun.

After CTextureCache is destroyed, the pending lambda runs on the global job manager's worker thread and accesses freed m_cleaningInProgress and m_cleanTimer members (heap-use-after-free).

Fix

  1. Stop the timer first: call m_cleanTimer.Stop(true) at the top of Deinitialize() so no further CleanTimer() invocations can be enqueued after teardown starts.
  2. Use the local job queue: change CleanTimer() to submit via Submit() (the local CJobQueue) instead of CServiceBroker::GetJobManager(), so CancelJobs() and the CJobQueue destructor properly govern the lambda's lifetime — the [this] capture cannot outlive *this.

Testing

Confirmed with AddressSanitizer: heap-use-after-free on m_cleaningInProgress reproduced reliably before the fix, and is absent after.

CleanTimer() was submitting a [this] lambda to the global job manager
(CServiceBroker::GetJobManager()). Deinitialize() only called CancelJobs()
which clears the local CJobQueue — it has no effect on jobs already handed
to the global manager. Additionally, m_cleanTimer was never stopped in
Deinitialize(), so the timer could fire and enqueue a new global job after
teardown had begun.

Fix by:
- Stopping m_cleanTimer in Deinitialize() before CancelJobs(), preventing
  any further CleanTimer() invocations after teardown starts.
- Submitting through the local CJobQueue (Submit()) instead of the global
  job manager, so CancelJobs() and the CJobQueue destructor properly govern
  the job lifetime and the [this] lambda cannot outlive *this.

Confirmed with AddressSanitizer: heap-use-after-free on m_cleaningInProgress
when the global job worker thread runs the lambda after CTextureCache is freed.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
@kodiai

kodiai Bot commented Mar 9, 2026

Copy link
Copy Markdown
Contributor
Review Details
  • Files reviewed: 1
  • Lines changed: +3 -3
  • Profile: strict (auto, lines changed: 6)
  • Author: first-time (adapted tone)
  • Findings: 0 critical, 0 major, 0 medium, 0 minor
  • Review completed: 2026-03-09T19:37:45.632Z
  • Keyword parsing: No keywords detected

@neo1973 neo1973 added Type: Fix non-breaking change which fixes an issue v22 Piers labels Mar 10, 2026
@neo1973 neo1973 added this to the Piers 22.0 Alpha 3 milestone Mar 10, 2026

@neo1973 neo1973 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you, I think this makes sense.

@thexai
thexai merged commit 686b789 into xbmc:master Mar 15, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Type: Fix non-breaking change which fixes an issue v22 Piers

Projects

None yet

Development

Successfully merging this pull request may close these issues.

TextureCache: use-after-free in CleanTimer() via global job manager

3 participants