TGA-to-GIF palette split signed overflow causes SIGSEGV
Summary
A tiny truncated TGA can crash OpenImageIO when it is converted to GIF. The PoC declares a 1472 x 12000 RGB TGA image but contains no pixel payload. iinfo -v accepts the image metadata, then oiiotool poc.tga -o out.gif reports the TGA read error and still flushes the pending GIF frame during output close.
The crash reproduces on current main and v3.1.15.0 non-sanitized product binaries as SIGSEGV (rc=139). Sanitizers show the root cause is a signed integer overflow in the GIF palette splitter, followed by a read SEGV.
Impact
An attacker-controlled image can crash applications that use OpenImageIO to transcode this malformed TGA to GIF. Confirmed impact is reliable denial of service with sanitizer-confirmed memory-safety behavior: signed integer overflow and out-of-bounds read.
Precondition: the vulnerable path is reached when writing GIF output. Metadata-only iinfo -v poc.tga does not crash.
Reproduction
Expected original-binary result:
v3_orig_gif_nolimit rc=139 SIGSEGV
v3_orig_gif_vm2g rc=139 SIGSEGV
main_orig_gif_nolimit rc=139 SIGSEGV
main_orig_gif_vm2g rc=139 SIGSEGV
v3_orig_iinfo rc=0 OK, 1472 x 12000 TGA metadata
main_orig_iinfo rc=0 OK, 1472 x 12000 TGA metadata
Optional sanitizer run:
RUN_SANITIZERS=1 bash reproduce.sh
Evidence
- Current
main tested: a9f7a359a32e8cebceed6d689380db2d0f245120.
- Main ASAN: read
SEGV in GifSplitPalette at gif.h:349.
- Main UBSAN: signed integer overflow at
gif.h:343: 17664000 * 127 cannot be represented in int.
- v3.1.15.0 ASAN/UBSAN reports the same source locations.
- No public open PR was found for
GifSplitPalette, GIFOutput, or this GIF palette integer overflow. Open TGA PRs checked were unrelated.
Root cause
During GIF output close, OpenImageIO writes the pending initialized canvas even after the TGA reader reported EOF. GIF palette construction then computes the split size with int arithmetic:
int subPixelsA = numPixels * (splitElt - firstElt) / (lastElt - firstElt);
pal->treeSplit[treeNode] = image[subPixelsA * 4 + splitCom];
For the PoC, numPixels is 17,664,000; multiplying by 127 overflows signed 32-bit int, corrupts subPixelsA, and leads to an out-of-bounds read.
Suggested fix
- Use checked
size_t/uint64_t arithmetic in the GIF palette splitter before converting to int.
- Reject GIF outputs whose total pixel count exceeds the palette builder's safe range.
- Clear the pending GIF frame or skip
finish_subimage() when the input read failed.
PoC archive contents
poc.zip contains exactly these files:
poc.zip
poc/poc.tga — minimized 18-byte trigger.
reproduce.sh — product-binary reproduction script.
evidence/reproduce.log — latest non-sanitized original-binary reproduction output.
evidence/gdb_v3_orig_min_poc_gif.txt — v3 original GDB backtrace.
evidence/gdb_main_orig_min_poc_gif.txt — main original GDB backtrace.
evidence/v3_asan_gif.stderr — full v3 ASAN log.
evidence/v3_ubsan_gif.stderr — full v3 UBSAN log.
evidence/main_asan_gif.stderr — full main ASAN log.
evidence/main_ubsan_gif_vm2g.stderr — full main UBSAN log.
TGA-to-GIF palette split signed overflow causes SIGSEGV
Summary
A tiny truncated TGA can crash OpenImageIO when it is converted to GIF. The PoC declares a
1472 x 12000RGB TGA image but contains no pixel payload.iinfo -vaccepts the image metadata, thenoiiotool poc.tga -o out.gifreports the TGA read error and still flushes the pending GIF frame during output close.The crash reproduces on current
mainandv3.1.15.0non-sanitized product binaries asSIGSEGV(rc=139). Sanitizers show the root cause is a signed integer overflow in the GIF palette splitter, followed by a read SEGV.Impact
An attacker-controlled image can crash applications that use OpenImageIO to transcode this malformed TGA to GIF. Confirmed impact is reliable denial of service with sanitizer-confirmed memory-safety behavior: signed integer overflow and out-of-bounds read.
Precondition: the vulnerable path is reached when writing GIF output. Metadata-only
iinfo -v poc.tgadoes not crash.Reproduction
Expected original-binary result:
Optional sanitizer run:
Evidence
maintested:a9f7a359a32e8cebceed6d689380db2d0f245120.SEGVinGifSplitPaletteatgif.h:349.gif.h:343:17664000 * 127cannot be represented inint.GifSplitPalette,GIFOutput, or this GIF palette integer overflow. Open TGA PRs checked were unrelated.Root cause
During GIF output close, OpenImageIO writes the pending initialized canvas even after the TGA reader reported EOF. GIF palette construction then computes the split size with
intarithmetic:For the PoC,
numPixelsis17,664,000; multiplying by127overflows signed 32-bitint, corruptssubPixelsA, and leads to an out-of-bounds read.Suggested fix
size_t/uint64_tarithmetic in the GIF palette splitter before converting toint.finish_subimage()when the input read failed.PoC archive contents
poc.zipcontains exactly these files:poc.zip
poc/poc.tga— minimized 18-byte trigger.reproduce.sh— product-binary reproduction script.evidence/reproduce.log— latest non-sanitized original-binary reproduction output.evidence/gdb_v3_orig_min_poc_gif.txt— v3 original GDB backtrace.evidence/gdb_main_orig_min_poc_gif.txt— main original GDB backtrace.evidence/v3_asan_gif.stderr— full v3 ASAN log.evidence/v3_ubsan_gif.stderr— full v3 UBSAN log.evidence/main_asan_gif.stderr— full main ASAN log.evidence/main_ubsan_gif_vm2g.stderr— full main UBSAN log.