Skip to content

Panic in RW2 decoder (decode_panasonic) on newer Panasonic compression variants #59

Description

@akilegaspi

Summary

Decoding Panasonic RW2 files from newer cameras (S5 II / S1R II / G9 II generation) panics in decode_panasonic with an out-of-bounds slice index.

Panic

thread '<unnamed>' panicked at rawloader-0.37.2/src/decoders/rw2.rs:94:21:
range start index 26329088 out of range for slice of length 24175632

Reproducible on both 0.37.2 (crates.io) and current master — the code at the panic site is identical:

let skip = ((width * row * 9) + (width/14 * 2 * row)) / 8;
let blocks = skip / 0x4000;
let src = &buf[blocks*0x4000..];   // <- line 94: unguarded slice

Analysis

The failing file's raw section is ~1.0 byte/pixel (24,175,632 bytes for a ~23.6 MP image), while decode_panasonic assumes the classic 9/8 bytes-per-pixel encoding. The branch selection in Rw2Decoder::image doesn't catch this variant:

  • src.len() >= width*height*2 → false
  • src.len() >= width*height*3/2 → false
  • falls through to decode_panasonic(src, width, height, false, …), which computes block offsets from the assumed 9/8 packing and overruns the buffer near the bottom rows.

Newer Panasonic bodies write RW2 with a different compression (~8-bit/pixel), which this decoder path was never written for.

Expected behavior

Return an Err (unsupported encoding) instead of panicking — a panic on a rayon worker thread aborts the whole decode and is unfriendly to downstream FFI consumers. At minimum a bounds check before the slice; ideally support for the newer compression.

Environment

  • rawloader 0.37.2 (also reproduced against master)
  • macOS arm64, called via FFI (panic crosses catch_unwind only because rayon re-raises on the join)

Activity

  1. akilegaspi commented on Sep 30, 2026

    @akilegaspi
    Author

    Reproduction with a modern camera file

    Confirmed with a Panasonic DC-S5M2X (Lumix S5 II X, 2023) RW2 file: 6000×4000 px, 29,769,728 bytes total.

    Minimal repro:

    fn main() {
        let path = std::env::args().nth(1).unwrap();
        match rawloader::decode_file(&path) {
            Ok(img) => println!("decoded OK: {}x{} cpp={}", img.width, img.height, img.cpp),
            Err(e) => println!("decode error (no panic): {}", e),
        }
    }

    with rawloader = "=0.37.2":

    thread '<unnamed>' panicked at rawloader-0.37.2/src/decoders/rw2.rs:94:21:
    range start index 26673152 out of range for slice of length 25587216
    thread '<unnamed>' panicked at rawloader-0.37.2/src/decoders/rw2.rs:94:21:
    range start index 26869760 out of range for slice of length 25587216
    decode error (no panic): RawLoaderError: "Caught a panic while decoding.
    Please file a bug with a sample file at https://github.com/pedrocr/rawloader/issues/new"
    

    (Multiple rayon worker threads panic on successive row groups before the internal catch_unwind turns it into an Err.)

    Raw section is 25,587,216 bytes ≈ 1.07 bytes/pixel for a 24 MP frame — confirming this is the newer ~8-bit/pixel Panasonic compression, which decode_panasonic's 9/8-packing offset math can't address. The decoder selection in Rw2Decoder::image has no branch for it, so it falls through to the panicking path.

    Environment: macOS 26 arm64, rustc stable, rawloader 0.37.2 (also verified identical code on master).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions