Skip to content

TextureCache: fix two host-memory overruns from GE texture state - #22190

Draft
hrydgard wants to merge 1 commit into
masterfrom
texture-bounds-fixes
Draft

TextureCache: fix two host-memory overruns from GE texture state#22190
hrydgard wants to merge 1 commit into
masterfrom
texture-bounds-fixes

Conversation

@hrydgard

Copy link
Copy Markdown
Owner

PrepareBuildTexture's mip scan stopped at the first level with a dimension of 1 before running the mip size check for that level, so a level like 1x256 under a 256x256 level 0 was accepted as a valid mip. The backend then sized the level from the halved level-0 dimensions (128x128) while LoadTextureLevel re-read the real height (256) from the GE state, writing past the allocation. Do the check first.

That check was also gated on GPU_USE_SAMPLER_LOD_CONTROL, so backends without it did no mip dimension validation at all - there's no reason for the guard, the result only feeds badMipSizes, so drop it.

Separately, the non-DXT decode paths read w texels per row from a source whose stride, range check and unswizzle buffer are all computed from bufw. w and bufw are independent GE registers, so w > bufw is reachable and read past the end of both the validated guest range and the temp buffer. Clamp w to bufw in DecodeTextureLevel, which is what the DXT paths have always done via minw.

Note: ungating the mip check means backends without SAMPLER_LOD_CONTROL can now set badMipSizes where they previously didn't, which collapses such textures to a single level. That's the intended behavior, but it is a rendering-visible change on those backends.

Claude-Session: https://claude.ai/code/session_01DCPmm7FoQUoqrbMdhfqhQ2

PrepareBuildTexture's mip scan stopped at the first level with a dimension of 1
*before* running the mip size check for that level, so a level like 1x256 under a
256x256 level 0 was accepted as a valid mip. The backend then sized the level from
the halved level-0 dimensions (128x128) while LoadTextureLevel re-read the real
height (256) from the GE state, writing past the allocation. Do the check first.

That check was also gated on GPU_USE_SAMPLER_LOD_CONTROL, so backends without it
did no mip dimension validation at all - there's no reason for the guard, the
result only feeds badMipSizes, so drop it.

Separately, the non-DXT decode paths read w texels per row from a source whose
stride, range check and unswizzle buffer are all computed from bufw. w and bufw
are independent GE registers, so w > bufw is reachable and read past the end of
both the validated guest range and the temp buffer. Clamp w to bufw in
DecodeTextureLevel, which is what the DXT paths have always done via minw.

Note: ungating the mip check means backends without SAMPLER_LOD_CONTROL can now
set badMipSizes where they previously didn't, which collapses such textures to a
single level. That's the intended behavior, but it is a rendering-visible change
on those backends.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DCPmm7FoQUoqrbMdhfqhQ2
@hrydgard

Copy link
Copy Markdown
Owner Author

Hm, I'm a bit dubious that such clamping is correct in all cases.

@hrydgard
hrydgard marked this pull request as draft August 31, 2026 14:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant