bug:Out-of-bounds read in SgiInput::uncompress_rle_channel (bpc==1 RLE "run" branch lacks an input bounds check)
メンテナーはふだん 2 日以内に返信
評価
調査の方向性
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+NDEBUGbuilds: 1-byte heap-buffer-overflow read
past thenew 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.
- 主要言語
- C++
- スター
- 2.4k
- フォーク
- 718
- 平均マージ
- 2日 18時間
- マージ済み PR(30日)
- 85
環境構築
- Dockerfile・Docker Compose ファイルなし
- プルリクエストのテンプレートあり
- コントリビューションガイドを読む
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
AcademySoftwareFoundation/OpenImageIO のほかの issue
-
bug: get_image_info returns true after copying an uninitialised buffer into the caller's output when every UDIM tile is unreadable対応中かも @lgritz が 1 日前に担当しました。 オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 76/100
AcademySoftwareFoundation/OpenImageIO#5662 · コメント 1 件 · 担当者 1 名 ·
メンテナーはふだん 2 日以内に返信
-
bug: copy() writes destination channels the source does not have, taking the values from past the source's pixel対応中かも @lgritz が 1 日前に担当しました。 オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 35/100
AcademySoftwareFoundation/OpenImageIO#5661 · コメント 1 件 · 担当者 1 名 ·
メンテナーはふだん 2 日以内に返信
-
bug: a malformed filename throws std::regex_error out of ImageCache, and catching it deadlocks the file cache対応中かも @lgritz が 1 日前に担当しました。 オープン
AcademySoftwareFoundation/OpenImageIO#5660 · 担当者 1 名 ·
メンテナーはふだん 2 日以内に返信
-
bug: deep paste wraps into the next scanline instead of clipping, and ignores chbegin対応中かも @lgritz が 1 日前に担当しました。 オープン
難易度 4/5 3〜5日 初心者へのやさしさ 45/100
AcademySoftwareFoundation/OpenImageIO#5649 · コメント 1 件 · 担当者 1 名 ·
メンテナーはふだん 2 日以内に返信
-
bug: make_kernel("binomial", ...) indexes its tap arrays past their allocation for even or fractional extents対応中かも @lgritz が 3 日前に担当しました。 オープン
難易度 3/5 半日 初心者へのやさしさ 62/100
AcademySoftwareFoundation/OpenImageIO#5648 ·
メンテナーはふだん 2 日以内に返信
AcademySoftwareFoundation/OpenImageIO の issue をすべて見る
似ている issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 66/100
xiaoyifang/goldendict-ng#3148 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 76/100
メンテナーはふだん 4 日以内に返信
-
documentation
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
-
[Bug]: Launching Space Calibrator causes Bluetooth mouse lag due to a 100% duty-cycle BLE scan対応中かも @Yuuki204 が今日担当しました。 オープンbug
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
-
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
メンテナーはふだん 2 日以内に返信