bug:Out-of-bounds read in SgiInput::uncompress_rle_channel (bpc==1 RLE "run" branch lacks an input bounds check)

クローズ 初心者向け
#5,505 コメント 1 件 リアクション 0 件 担当者 0 名 GitHub で見る

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

@lgritz がすでに取り組んでいます。

2026年9月28日 から。

  • #5513 @lgritz による — オープン

評価

難易度
2/5
見積もり時間
1〜3時間
初心者へのやさしさ
80/100
issue の種類
バグ
明瞭さ
明確に書かれている
活発さ
活発
技術スタック
cpp
領域
security

調査の方向性

src/sgi.imageio/sgiinput.cpp の SgiInput::uncompress_rle_channel() から始め、bpc==1 の RLE ラン分岐に注目し、それをリテラル分岐の境界チェックと比較します。添付された poc.sgi を ASAN 下で oiiotool に通し、その後、不正な入力が範囲外読み取りなしに拒否され、影響を受ける画像ツールが abort したり sanitizer の失敗を報告したりしなくなったことを確認します。

索引モデルが issue の本文から書いたものです。

説明

Summary

While fuzzing oiiotool with AFL++ + ASAN we found that a malformed SGI
(.sgi) file triggers a span out-of-bounds read inside
SgiInput::uncompress_rle_channel(). In builds with contract assertions
enforced (the OIIO 3.x default) the violation aborts the process; in builds
compiled with hardening NONE + NDEBUG the same access is a 1-byte
heap-buffer-overflow read past the rle_scanline allocation.

Still reproduces on current main (dc87b17ab, 2026-09-27).

Reproducer

SGI file, RLE storage, dimension=3, 100x63, 4 channels, bpc=1: poc.sgi
attached (20 KB).

$ oiiotool -i poc.sgi --printinfo

Any of oiiotool / iconvert / iinfo / maketx reading the file hits it;
the crash is in the reader, independent of the tool.

Output

Build: clang 16, -g -O1 -fno-omit-frame-pointer, ASAN, contract
assertions at default (ENFORCE).

src/include/OpenImageIO/span.h:221 (operator[]): Contract assertion failed: idx < m_size
[abort]

With ASAN_OPTIONS=handle_abort=1, the aborting thread stack:

#5 SgiInput::uncompress_rle_channel(long, long, span<unsigned char>)   src/sgi.imageio/sgiinput.cpp
#6 SgiInput::read_native_scanline(...)          src/sgi.imageio/sgiinput.cpp:211:18
#7 ImageInput::read_native_scanlines(...)       src/libOpenImageIO/imageinput.cpp:521:19

Root cause

Current main, src/sgi.imageio/sgiinput.cpp, bpc==1 branch of
uncompress_rle_channel():

while (i < scanline_len) {
    unsigned char value = rle_scanline[i++];       // count byte (i < scanline_len OK)
    int count           = value & 0x7F;
    if (!count) break;
    if (value & 0x80) {                            // literal copy
        while (count--) {
            if (i >= scanline_len || limit <= 0) {  // <-- input bound IS checked here (line 286)
                errorfmt("Corrupt RLE data"); return false;
            }
            outbuf[iout++] = rle_scanline[i++];
            --limit;
        }
    } else {                                       // run: repeat next value
        value = rle_scanline[i++];                 // <-- line 296: NO `i >= scanline_len` check!
        while (count--) { ... }
    }
}

If the RLE count byte is the last byte of the scanline chunk
(i == scanline_len - 1 at loop top), consuming it advances i to
scanline_len, and the "run" branch then reads rle_scanline[i] one past
the end. The literal branch checks i >= scanline_len (line 286) but the
run branch was missed. The final i != scanline_len sanity check at the end
of the function would have rejected this data — but the OOB read happens
before it.

A minimal fix is mirroring the literal-branch check before line 296:

if (i >= scanline_len) { errorfmt("Corrupt RLE data"); return false; }
value = rle_scanline[i++];

Impact

  • Contract assertions enforced (OIIO 3.x default): process abort on a
    malformed file — denial of service for CLI tools (oiiotool, iconvert,
    iinfo, maketx) and any app using the SGI reader on untrusted input.
  • OIIO_HARDENING_NONE + NDEBUG builds: 1-byte heap-buffer-overflow read
    past the new unsigned char[scanline_len] allocation.

Related work

This is in the same function as GHSA-jg3q-vm3q-2j35 / CVE-2026-43903
(fixed in 3.0.18.0 / 3.1.13.0) and #5321 (16-bit odd byte counts), but the
access above is still reachable on current main — it is an
incomplete fix, leaving a remaining unchecked read in the bpc==1 run branch.

Meta

Found by a coverage-guided AFL++ fuzzing campaign (569-file multi-format
seed corpus from the OIIO testsuite) run during verification of #4552 /
CVE-2024-55194. 19 of the campaign's saved crash inputs reproduce this one
bug; the attached PoC is a readily-reproducible one.

Happy to send additional crash inputs or a minimized PoC if useful.

poc.zip

主要言語
C++
スター
2.4k
フォーク
718
平均マージ
2日 18時間
マージ済み PR(30日)
85

環境構築

はじめの一歩

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

AcademySoftwareFoundation/OpenImageIO のほかの issue

AcademySoftwareFoundation/OpenImageIO の issue をすべて見る

似ている issue

C++ の issue をもっと見る

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

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