Skip to content

heap-buffer-overflow in img_bmp.c

High
a1batross published GHSA-3p7j-2339-g5mm Nov 17, 2025

Package

Continuous master Build (xash3d-fwgs)

Affected versions

=< 3879

Patched versions

> 3879

Description

Summary

The function Image_LoadBMP would only stop processing after the image width had been reached, causing it to write one extra pixel beyond the allocated buffer for certain 1-bit BMPs.

Details

case 1:
      alpha = *buf_p++;
      column--;  // decrement to compensate for outer loop
      for (c = 0, k = 128; c < 8; c++, k >>= 1) {
          red = green = blue = (!!(alpha & k) == 1 ? 0xFF : 0x00);
          *pixbuf++ = red;    // writes
          *pixbuf++ = green;
          *pixbuf++ = blue;
          *pixbuf++ = 0x00;
          if (++column == columns)  // only checks bounds after write
              break;
      }
      break;

For a 1x1 image with 4 bytes allocated, the first iteration writes 4 bytes at column 0 (check fails: 0 != 1), but since the check is at the end, it will iterate a second time which will write another 4 bytes (this is the overflow) before the check succeeds and breaks.

PoC

  1. Build with ASAN using CC=clang CXX=clang++ ./waf configure -T sanitize --enable-fuzzer --enable-engine-tests and ./waf build
  2. Eun ASAN_OPTIONS="detect_leaks=0" LD_LIBRARY_PATH="build/engine" build/utils/run-fuzzer/run-fuzzer-Image_LoadBMP poc.bmp against poc.bmp

Impact

Any multiplayer users of the engine. According to engine/custom/custom.c, bmp files can be loaded as decals which are transmitted over the network https://steamcommunity.com/sharedfiles/filedetails/?id=3017564644:

static rgbdata_t *CustomDecal_LoadImage( const char *path, void *raw, int size )
{
	const char *testname;

	// this way we limit file types
	if( !Q_stricmp( COM_FileExtension( path ), "png" ))
		testname = "#logo.png";
	else if( !Q_stricmp( COM_FileExtension( path ), "wad" ))
		testname = "#logo.wad";
	else testname = "#logo.bmp";

	Image_SetForceFlags( IL_LOAD_PLAYER_DECAL );

	return FS_LoadImage( testname, raw, size );
}

Severity

High

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Network
Attack complexity
Low
Privileges required
None
User interaction
None
Scope
Unchanged
Confidentiality
Low
Integrity
Low
Availability
Low

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:L

CVE ID

No known CVE

Weaknesses

Heap-based Buffer Overflow

A heap overflow condition is a buffer overflow, where the buffer that can be overwritten is allocated in the heap portion of memory, generally meaning that the buffer was allocated using a routine such as malloc(). Learn more on MITRE.

Credits