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 件 担当者 0 名 GitHub で見る

メンテナーはふだん 1 日以内に返信

まだ誰も着手していません。

評価

難易度
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日 10時間
マージ済み PR(30日)
87

環境構築

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

AcademySoftwareFoundation/OpenImageIO のほかの issue

AcademySoftwareFoundation/OpenImageIO の issue をすべて見る

似ている issue

C++ の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。