Skip to content

P1: Refuse to reload a bitmap slot with a file of a different size or format - #7885

Open
dustinspace217 wants to merge 1 commit into
scp-fs2open:masterfrom
dustinspace217:bmpman/eff-stream-frame-size
Open

dustinspace217 wants to merge 1 commit into
scp-fs2open:masterfrom
dustinspace217:bmpman/eff-stream-frame-size

Conversation

@dustinspace217

@dustinspace217 dustinspace217 commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #7883

What?

bm_reload refuses a file whose size or format differs from the bitmap slot it is being loaded into, so a streamed EFF frame that is bigger than the frames before it no longer corrupts memory.

Why?

Main hall animations, ship and weapon selection icons and other streamed EFF animations crash the game when a later frame is bigger than the first ones (a 32-bit DDS after 24-bit ones, or larger dimensions), on OpenGL and Vulkan alike.

How?

Streaming reuses one bitmap slot for every frame. bm_reload only swapped the filename, and the lock functions then read the new file into a buffer sized for the slot's first file. bm_reload now opens the new file with the slot's own extension, reads its header and refuses it unless width, height, bit depth, compression and mipmap count match the slot; the caller then keeps the last frame that fit. It doesn't compare mem_taken, because bm_lock_pcx rewrites that to the size it locked at. bm_reload has no other callers.

Screenshots?

Mismatched EFF frames, master and this PR

Testing?

Retail FS2 data, a test mod replacing the main hall's 2_maincrane EFF, 1024x768, Linux Debug builds, master against this branch on OpenGL and Vulkan:

  • Later frames bigger (32-bit DDS after 24-bit; JPG frames at twice the size): master crashed in both cases on both renderers; this branch plays up to the last matching frame and holds it.
  • Matching frames in eight formats (DDS, JPG, PNG and TGA at 24 and 32 bits, and 8-bit PCX): pixel-identical to master on OpenGL; on Vulkan identical for the 32-bit formats and PCX (the 24-bit formats are broken on master's Vulkan regardless, see P2: Pass each streamed EFF frame's own bit depth to gr_update_texture #7882, and differ only in the over-read noise).
  • No frame was refused in any matching-frame run, including PCX and two looping EFFs played from a script. The retail main hall and SM1-01 behave as on master.

Not covered: KTX frames (no tool here to make them; same header path as DDS).

Anything Else?

A first frame bigger than the rest is a different path (the stream's texture is made from frame 0); #7886, which gives EFF streams their own buffer, handles it. The two are meant to land together: this guard is still needed alongside #7886 because the overflow happens in the lock, before the stream sees the frame. The check costs one header read per streamed frame, next to the full decode that follows. A refused frame logs a line each time it comes around in a loop. Written with Claude Code; the investigation, review and testing ran on my machine.

Streamed EFF animations reuse one bitmap slot for every frame: bm_reload
only swapped the filename, and the lock functions then read the new file
into a buffer sized for the slot's first file. A later frame that was
bigger (a 32-bit DDS after a 24-bit one, or larger dimensions) wrote past
that buffer and corrupted the heap.

bm_reload now opens the new file with the slot's own extension, reads its
header and refuses it unless width, height, bit depth, compression and
mipmap count match the slot, so the caller keeps the last frame that fit.
@BMagnu BMagnu added the LLM-authored For PR's mainly created through LLMs and NOT submitted by badged developers label Oct 19, 2026
@dustinspace217 dustinspace217 changed the title Refuse to reload a bitmap slot with a file of a different size or format P1: Refuse to reload a bitmap slot with a file of a different size or format Oct 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

LLM-authored For PR's mainly created through LLMs and NOT submitted by badged developers

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Streamed EFF with a frame bigger than the first frame corrupts the heap

2 participants