Each TextDrawerAndroid took a global ref to the TextRenderer class and
never released it (the release had been commented out as crashing), plus
leaked the local ref it was made from. A TextDrawer is created every time
graphics come up, so that was one of each per resume.
Look the class up once and keep it. Also initialize the method IDs, which
IsReady() read uninitialized if the class was missing, and don't walk into
a null array if renderText throws.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
registerCallbacks took a new global ref to the activity on every onCreate
without releasing the previous one, and unregisterCallbacks only runs on a
real shutdown. Since rotation and resizing recreate the activity, each of
those leaked the old one, view hierarchy and all.
Releasing it means other threads can no longer use the raw global ref,
which had been copied into AndroidStorage and TextDrawerAndroid and was
read from the emu thread and IO threads. They now take a local ref under a
mutex (Android_GetActivity, and the equivalent inside AndroidStorage for
the shortcut activity's sake), so a call in flight keeps its activity
alive while the UI thread swaps in the new one.
The "no activity" bail is a null check on that local ref rather than a
separate HasActivity() test beforehand. Between such a test and the call
the UI thread can swap the activity out, and we'd hand null to a Java
method that wants a Context. The one remaining HasActivity() caller never
passes the activity on, so it has nothing to race with.
The ContentUri class ref and method IDs are now only looked up once,
instead of leaking a class ref per activity.
Headless and the unit test executable link Common but not app-android.cpp, so
they get a stub for this the same way they already do for getEnv and findClass.
Without it the Android CI build fails at link time, where a normal gradle build
is perfectly happy.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01152igCdjTNhT29qvphPyPC
These own GPU objects, memory or refcounts in their destructors (or assert
there that they were torn down), so a copy would double-free. Nothing copies
them today; this keeps it that way. The manager base classes cover every
backend's subclass.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The only guard against writing past the fixed 65536-entry verts_
array was _dbg_assert_msg_, which is compiled out entirely outside
_DEBUG - a large enough single batch (e.g. Circle()/CircleSegment()
with a big segment count, or a large UI list without an intervening
Flush()) silently corrupted the heap in release builds. Added a real
bounds check that drops the vertex instead.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L4QAoxV2KY7ek4PcZw3WvY
TextureLoadTask::Run() only freed the VFS-read file buffer on the
success path - a corrupt/truncated image in a texture pack or
replacement texture directory (valid magic, bad payload) leaked the
whole file's bytes on every load attempt.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L4QAoxV2KY7ek4PcZw3WvY
Our Qt backend has long been left behind and doesn't even support Vulkan
currently. There would be a lot of work to make it viable, and I don't
think anyone is really interested.
ImGui on SDL will soon fulfill the need for a more classic user interface
with a menu bar on Linux, and on Mac we already have a native UI.
pngLoadPtr allocated the decoded buffer directly from attacker-controlled
PNG IHDR dimensions with no upper bound, so browsing a crafted game icon
or savedata could trigger a multi-gigabyte allocation.
- Add maxWidth/maxHeight parameters to pngLoadPtr (default 8192x8192)
and reject images larger than the limits.
- Thread the limits through LoadTextureLevelsFromFileData,
CreateTextureFromFileData, and CreateTextureFromFile.
- Limit game icons to 256x128 in GameInfoCache and IconCache.
The D2D-based text renderer requires a D3D11 context, which confuses
RenderDoc (it doesn't permit multiple 3D APIs in the same process).
So, let's restore the old GDI renderer and use it if RenderDoc is attached.
See #21638