OpenImageIO PSD reader: a crafted layer with a missing color channel drives a NULL pointer dereference when the layer subimage is read
Maintainer thường phản hồi trong vòng 2 ngày
Chưa có ai nhận issue này.
Đánh giá
- Độ khó
- 3/5
- Thời gian dự kiến
- 1-2 ngày
- Mức phù hợp với người mới
- 45/100
Hướng nghiên cứu
Start with src/psd.imageio/psdinput.cpp, especially PSDInput::setup around the layer channel lookups and read_native_scanline around the reported dereference. Trace how layer channel IDs are parsed and compare that path with composite channel validation. Done means a crafted layer missing an expected color channel is rejected or safely handled, with a regression test covering reading its subimage.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
Hello, and thank you for maintaining this project.
I found a likely memory-safety issue by static source review (no runtime exploit was developed). The technical details are inline below so this report is self-contained, with a full write-up linked within.
Affected component: OpenImageIO, the Photoshop PSD/PSB image reader (src/psd.imageio/psdinput.cpp).
Tested version(s): v3.2.1.2 at commit 5c4408f545f1da5f30102c5f1c7151dbd9faf522 (latest release, 2026-10-15, annotated tag object bd94ea79d183f2a5043b6a68a1331ad861629cad). Default branch main HEAD 07e3c3e0373551c57e7d8e01353deaa4b3646613 (2026-10-17) was also inspected and carries the same code. Earlier releases that expose PSD layers as subimages are affected as well.
Details:
NULL pointer dereference (CWE-476) in the OpenImageIO PSD reader, reachable under the default build from a crafted PSD when a consumer reads a layer subimage. The PSD reader exposes the merged composite as subimage 0 and each Photoshop layer as a further subimage (m_subimage_count = m_layers.size() + 1, psdinput.cpp:655).
Root cause. For each layer, PSDInput::setup builds the per-channel pointer list m_channels[subimage] by requesting the color channel ids 0..mode_channel_count-1 from layer.channel_id_map using std::map::operator[] (psdinput.cpp:2192-2194, and :2197 for the transparency channel). layer.channel_id_map is a std::map<int16_t, ChannelInfo*> populated from the layer record, where each channel id is a signed 16-bit field read straight from the file (load_layer, psdinput.cpp:1640, map populated at :1646). load_layer accepts any channel count and any set of ids and does not require a layer to contain the color channels its color mode implies. std::map::operator[] inserts a value-initialized entry on a miss, and a value-initialized ChannelInfo* is nullptr, so for any expected id the layer omits, channel_id_map[i] returns nullptr and that null is pushed into m_channels with no check. mode_channel_count is 3 for RGB, 4 for CMYK, 1 for Grayscale/Bitmap/Indexed and 3 for Lab (psdinput.cpp:532).
Sink. When the layer subimage is read, PSDInput::read_native_scanline iterates m_channels[subimage] and dereferences each entry: ChannelInfo& channel_info = *channels[c]; channel_buffers[c].resize(channel_info.row_length); (psdinput.cpp:821-822). When channels[c] is nullptr, reading channel_info.row_length is a 32-bit read at offset 0 of a null pointer, which faults (SIGSEGV). The dereference runs before any color-mode branch, so every color mode with a nonzero mode_channel_count reaches it, and it runs whether or not the rawcolor/rawdata option is set.
Attacker model. A crafted PSD with a valid 8BPS header (for example RGB, depth 8, small dimensions) and one layer whose channel record lists ids that omit an expected color id, for example {0, 1, 3} for an RGB layer (gap at id 2), with the present channels carrying small valid raw data blocks. The only precondition is that the consumer reads a layer subimage (index 1 or higher). Reading only the composite (subimage 0) does not trigger it, because the composite channels come from the validated m_image_data.channel_info array (load_image_data rejects a header channel count below the color mode minimum at psdinput.cpp:2070-2082, and setup indexes the real array at :2163-2165).
Impact. Deterministic read through a null pointer and a process crash, a denial of service for any tool or service that reads PSD layers with OpenImageIO (for example Blender, VFX and compositing tools, thumbnailers and asset pipelines). Not established: no out-of-bounds write, no out-of-bounds read of adjacent memory, no information disclosure, no instruction-pointer control and no code execution. The dereferenced value is always exactly nullptr (value-initialized by std::map::operator[]), not an attacker-chosen address, so the primitive is a fixed null read.
Fix. In PSDInput::setup, replace the layer channel_id_map[i] and channel_id_map[ChannelID_Transparency] lookups with find and reject the layer when an expected channel id is missing, instead of storing a null pointer. Add a null guard in read_native_scanline before *channels[c]. As defense in depth, validate each layer's channel id set against its color mode at parse time, the way load_image_data already validates the composite channel count.
Verification. The full chain was confirmed statically against the released source at the pinned commit: psdinput.cpp:1637-1647 (channel ids read and map populated), :532 (mode_channel_count table), :2192-2197 (operator[] lookups that insert null), :821-822 (null dereference sink), and :2070-2082 and :2163-2165 (the guarded composite path). The same lines are present on the main HEAD. No AddressSanitizer run or live deployment was used in this phase, so the crash is inferred from the source path.
Distinct from prior PSD advisories. CVE-2026-63420 (GHSA-x877-h4xx-5m5j, fixed 2026-09-18) is a heap out-of-bounds read in the PSD row-interleaving code for a crafted indexed PSD with transparency, only under the rawcolor/rawdata option. CVE-2026-63635 is an out-of-bounds read of fixed lookup tables after an invalid color mode passes header validation under the same option. CVE-2022-41684 (GHSA-3rmp-xjxw-cprh) is a 2022 heap out-of-bounds read in older PSD code. CVE-2022-43593 and CVE-2022-43603 are null-pointer dereferences in the DPX and Zfile output close paths, not the PSD reader. This finding is a NULL pointer dereference in setup on the normal non-raw layer path, a distinct function and root cause.
Write-up: https://hackmd.io/@haindpwn/openimageio-psd-layer-channel-null-deref
Reported by HaiND (Nguyen Dinh Hai), Post and Telecommunication Institute of Technology, github.com/Haind03.
- Ngôn ngữ chính
- C++
- Star
- 2.4k
- Fork
- 718
- Merge trung bình
- 2 ngày 18 giờ
- Pull request đã merge (30 ngày)
- 85
Chuẩn bị môi trường
- Không có Dockerfile hay tệp Docker Compose
- Có mẫu pull request
- Đọc hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của AcademySoftwareFoundation/OpenImageIO
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 76/100
AcademySoftwareFoundation/OpenImageIO#5662 ·
Maintainer thường phản hồi trong vòng 2 ngày
-
bug: copy() writes destination channels the source does not have, taking the values from past the source's pixelCó thể đã có người làm @lgritz đã nhận hôm nay. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 35/100
AcademySoftwareFoundation/OpenImageIO#5661 ·
Maintainer thường phản hồi trong vòng 2 ngày
-
bug: a malformed filename throws std::regex_error out of ImageCache, and catching it deadlocks the file cacheCó thể đã có người làm @lgritz đã nhận hôm nay. Đang mở
AcademySoftwareFoundation/OpenImageIO#5660 · 1 người được giao ·
Maintainer thường phản hồi trong vòng 2 ngày
-
PSD: layers with a "real user supplied layer mask" (channel -3) stored as Raw fail to load ("unsupported compression")Có thể đã có người làm @linsen458-spec đã nhận hôm nay. Đang mở
Độ khó 3/5 1-2 ngày Mức phù hợp với người mới 72/100
AcademySoftwareFoundation/OpenImageIO#5659 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 2 ngày
-
bug: deep paste wraps into the next scanline instead of clipping, and ignores chbeginCó thể đã có người làm @lgritz đã nhận 2 ngày trước. Đang mở
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 45/100
AcademySoftwareFoundation/OpenImageIO#5649 ·
Maintainer thường phản hồi trong vòng 2 ngày
Tất cả issue của AcademySoftwareFoundation/OpenImageIO
Issue tương tự
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 62/100
mw00/project-maya#877 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 68/100
llvm/offload-test-suite#1703 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
test(tools): test_invite_answer_wiring scans nothing in a checkout whose path contains tests/Đang mởbacklog bug
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 86/100
EchoTools/nevr-runtime#2154 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 62/100
BGI-shenzhen/PopLDdecay#25 ·