OpenImageIO PSD reader: a crafted layer with a missing color channel drives a NULL pointer dereference when the layer subimage is read

已关闭
#5,658 1 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

维护者通常 2 天内回复

还没有人认领这个 Issue。

评估

难度
3/5
预计耗时
1-2 天
新手友好度
45/100
Issue 类型
缺陷
描述清晰度
描述清楚
活跃度
活跃
技术栈
cpp
领域
backend, security

调研方向

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.

由索引模型根据 Issue 内容生成。

描述

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.

主要语言
C++
星标
2.4k
派生
718
平均合并
2 天 18 小时
30 天内合并 PR
85

环境准备

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

AcademySoftwareFoundation/OpenImageIO 的其他 Issue

查看 AcademySoftwareFoundation/OpenImageIO 的全部 Issue

相似的 Issue

更多 C++ Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。