Skip to content

Panic (unwrap() on None) in color_no_convert #289

@MinghuaWang

Description

@MinghuaWang

Describe the bug

color_no_convert panics with called Option::unwrap() on a None value when decoding a crafted jpeg. The panic occurs at the unchecked output_iter.next().unwrap() in src/decoder.rs:1481. The function iterates all bytes from every component's upsampled line buffer and writes them to the output line. When the internal line buffer is larger than the output length, it would cause the output iterator to be exhausted before all input bytes are consumed, leading to panic on unwrap().

Panic info:

thread 'main' panicked at src/decoder.rs:1481:34:
called `Option::unwrap()` on a `None` value

Stack trace (relevant frames):

jpeg_decoder::decoder::color_no_convert
  → output_iter.next().unwrap()     //  panic
jpeg_decoder::upsampler::Upsampler::upsample_and_interleave_row
jpeg_decoder::worker::compute_image_parallel
jpeg_decoder::decoder::compute_image
jpeg_decoder::decoder::Decoder<R>::decode_planes
jpeg_decoder::decoder::Decoder<R>::decode_internal
jpeg_decoder::decoder::Decoder<R>::decode

Full stack trace:

thread 'main' panicked at src/decoder.rs:1481:34:
called `Option::unwrap()` on a `None` value
stack backtrace:
   0: __rustc::rust_begin_unwind
             at /rustc/ec7c02612527d185c379900b613311bc1dcbf7dc/library/std/src/panicking.rs:697:5
   1: core::panicking::panic_fmt
             at /rustc/ec7c02612527d185c379900b613311bc1dcbf7dc/library/core/src/panicking.rs:75:14
   2: core::panicking::panic
             at /rustc/ec7c02612527d185c379900b613311bc1dcbf7dc/library/core/src/panicking.rs:145:5
   3: core::option::unwrap_failed
             at /rustc/ec7c02612527d185c379900b613311bc1dcbf7dc/library/core/src/option.rs:2130:5
   4: core::option::Option<T>::unwrap
             at /home/tony/.rustup/toolchains/nightly-2025-08-06-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/option.rs:1009:21
   5: jpeg_decoder::decoder::color_no_convert
             at ./src/decoder.rs:1481:34
   6: jpeg_decoder::upsampler::Upsampler::upsample_and_interleave_row
             at ./src/upsampler.rs:62:9
   7: jpeg_decoder::worker::compute_image_parallel
             at ./src/worker/mod.rs:117:23
   8: jpeg_decoder::decoder::compute_image
             at ./src/decoder.rs:1334:9
   9: jpeg_decoder::decoder::Decoder<R>::decode_planes
             at ./src/decoder.rs:689:13
  10: jpeg_decoder::decoder::Decoder<R>::decode_internal::{{closure}}
             at ./src/decoder.rs:613:18
  11: jpeg_decoder::worker::WorkerScope::get_or_init_worker
             at ./src/worker/mod.rs:84:9
  12: jpeg_decoder::decoder::Decoder<R>::decode_internal
             at ./src/decoder.rs:612:22
  13: jpeg_decoder::decoder::Decoder<R>::decode::{{closure}}
             at ./src/decoder.rs:294:41
  14: jpeg_decoder::worker::WorkerScope::with
             at ./src/worker/mod.rs:61:9
  15: jpeg_decoder::decoder::Decoder<R>::decode
             at ./src/decoder.rs:294:9
  16: poc_color_no_convert_panic::main
             at ./examples/poc_color_no_convert_panic.rs:7:21

This was obtained by running with RUST_BACKTRACE=1 cargo run --example poc_color_no_convert_panic --no-default-features --features platform_independent

Root cause

In src/decoder.rs, color_no_convert iterates all bytes from every line buffer. When the line buffer is larger than output, it would cause the output iterator to be exhausted before all input bytes are consumed, which leads to the panic on unwrap().

// lines 1476-1484 — PANICS on subsampled images:
fn color_no_convert(data: &[Vec<u8>], output: &mut [u8]) {
    let mut output_iter = output.iter_mut();

    for pixel in data {
        for d in pixel {
            *(output_iter.next().unwrap()) = *d;  // output exhausted, panic
        }
    }
}

To reproduce

// examples/poc_color_no_convert_panic.rs
use jpeg_decoder::{Decoder, ColorTransform};

fn main() {
    let jpeg = std::fs::read("poc_1.jpeg").unwrap();

    let mut decoder = Decoder::new(jpeg.as_slice());
    decoder.set_color_transform(ColorTransform::None);
    // This panics:
    let _pixels = decoder.decode();
}

JPEG: poc_1.jpeg.zip (unzip it to get poc_1.jpeg)

Test environment

  • Version: jpeg-decoder master (commit eb2d7c0)
  • OS: Ubuntu 24.04 (x86_64)
  • Rustc version: rustc 1.91.0-nightly (ec7c02612 2025-08-05)

Suggested fix

fn color_no_convert(data: &[Vec<u8>], output: &mut [u8]) {
    let ncomp = data.len();
    for (i, chunk) in output.chunks_exact_mut(ncomp).enumerate() {
        for (j, component) in data.iter().enumerate() {
            if let Some(&val) = component.get(i) {
                chunk[j] = val;
            }
        }
    }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions