CVE-2026-20889
A heap-based buffer overflow vulnerability exists in the x3f_thumb_loader functionality of LibRaw Commit d20315b. A specially crafted malicious file can lead to a heap buffer overflow. An attacker can provide a malicious file to trigger this vulnerability.
The versions below were either tested or verified to be vulnerable by Talos or confirmed to be vulnerable by the vendor.
LibRaw Commit d20315b
LibRaw - https://github.com/LibRaw/LibRaw.git
9.8 - CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
CWE-190 - Integer Overflow or Wraparound
LibRaw is an open-source C/C++ library for reading, decoding, and processing RAW image files from multiple camera manufacturers.
LibRaw is a widely-used library for reading RAW image files from digital cameras. The library includes support for Sigma/Foveon X3F files, which contain both RAW sensor data and embedded thumbnails. The thumbnail extraction routine is vulnerable to a heap buffer overflow because the size calculation for the thumbnail buffer uses 32-bit arithmetic that can overflow when processing attacker-controlled dimension values.
The vulnerable function LibRaw::x3f_thumb_loader() in src/x3f/x3f_parse_process.cpp handles extraction of bitmap thumbnails from X3F files:
void LibRaw::x3f_thumb_loader()
{
[...]
x3f_directory_entry_header_t *DEH = &DE->header;
x3f_image_data_t *ID = &DEH->data_subsection.image_data;
[...]
else if (imgdata.thumbnail.tformat == LIBRAW_THUMBNAIL_BITMAP)
{
[1] imgdata.thumbnail.tlength = ID->columns * ID->rows * 3;
[2] imgdata.thumbnail.thumb = (char *)malloc(ID->columns * ID->rows * 3);
char *src0 = (char *)ID->data;
for (int row = 0; row < (int)ID->rows; row++)
{
[3] int offset = row * ID->row_stride;
[4] if (offset + ID->columns * 3 > ID->data_size)
break;
[5] char *dest = &imgdata.thumbnail.thumb[row * ID->columns * 3];
char *src = &src0[offset];
[6] memmove(dest, src, ID->columns * 3);
}
}
[...]
}
The ID->columns, ID->rows, and ID->row_stride values are read directly from the X3F thumbnail section header in the image file and are attacker-controlled. These fields are defined as uint32_t in the x3f_image_data_t structure. At [1] and [2], the code calculates the buffer size as columns * rows * 3 (3 bytes per RGB pixel). Since all operands are uint32_t, the multiplication is performed using 32-bit arithmetic, which overflows when the product exceeds UINT32_MAX (4,294,967,295). The overflowed result is then passed to malloc().
At [2], malloc() receives the overflowed (truncated) value, allocating a buffer much smaller than intended. The loop then iterates based on the original (large) rows value. At [5], the destination offset is calculated as row * columns * 3, which uses the original dimensions and quickly exceeds the allocated buffer size. At [6], memmove() writes data beyond the buffer boundary.
For example, with columns = 50000 and rows = 30000:
- Intended size: 50000 * 30000 * 3 = 4,500,000,000 bytes (~4.2 GB)
- Overflowed value: 4,500,000,000 mod 2^32 = 205,032,704 bytes (~195 MB)
- malloc() allocates only ~195 MB
- The loop writes ~4.2 GB, overflowing by ~4 GB
The bounds check at [4] prevents reading past the source data in the file, but this is easily satisfied by providing sufficient pixel data or using a small row_stride value. With row_stride = 1, the source offset grows slowly (0, 1, 2, 3…) while the destination offset grows rapidly (0, 150000, 300000…), allowing the overflow to trigger with minimal file size.
Any application that calls unpack_thumb() on untrusted X3F files is vulnerable to this heap buffer overflow, which can result in heap corruption and potential code execution. Note that X3F support requires LibRaw to be compiled with -DUSE_X3FTOOLS, which is not enabled by default.
2026-02-12 - Initial Vendor Contact
2026-02-12 - Vendor Disclosure
2026-04-06 - Vendor Patch Release
2026-04-07 - Public Release
Discovered by Francesco Benvenuto of Cisco Talos.