Files
lightcraft/crates/raw/tests/robust.rs
T
radialmonster 2dad18d2ec LR-RAW-NEF: stretch non-square pixels (D1X/D1H) to square
The D1X/D1H raw SubIFD is 4028x1324 with XResolution 600 / YResolution 300:
the pixel is twice as tall as it is wide. Nothing read that, so the
picture came out about 3:1. Add a pixel aspect to RawImage/RawInfo, read
it from the raw IFD resolution ratio (NEF) and from DefaultScale (DNG),
accept only 1.5..4, and resample the rows after demosaic. developed_size()
reports the stretched size, binning is off for non-square pixels, and the
catalog and lens-profile sizing use developed_size().
2026-10-13 06:01:40 -04:00

106 lines
4.0 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! Malformed-input robustness: decoding random or corrupted files must never panic.
use lightcraft_raw::{
BlackLevel, Cfa, ColorData, DngCompression, DngWriteOptions, Method, OpcodeLists, Orientation, RawData, RawFormat, RawImage, Rect,
};
use proptest::prelude::*;
fn sample(comp: DngCompression) -> Vec<u8> {
let (w, h) = (24usize, 16usize);
let data: Vec<u16> = (0..w * h).map(|i| (i * 97 % 4000) as u16).collect();
let raw = RawImage {
format: RawFormat::Dng,
width: w,
height: h,
cpp: 1,
data: RawData::U16(data),
cfa: Some(Cfa::bayer("RGGB").unwrap()),
bits: 12,
black: BlackLevel::uniform(64.0),
white: vec![4095.0],
active_area: Rect::new(0, 0, w, h),
crop: Rect::new(0, 0, w, h),
orientation: Orientation::Normal,
color: ColorData::default(),
wb_multipliers: None,
linearized: false,
pixel_aspect: 1.0,
opcodes: OpcodeLists::default(),
metadata: Default::default(),
};
lightcraft_raw::write_dng(&raw, &DngWriteOptions { compression: comp, ..Default::default() }).unwrap()
}
fn exercise(bytes: &[u8]) {
let _ = lightcraft_raw::probe(bytes);
let _ = lightcraft_raw::embedded_preview(bytes);
if let Ok(img) = lightcraft_raw::decode(bytes)
&& img.width * img.height <= 1 << 16
{
let _ = img.develop(Method::Ahd);
let xy = lightcraft_raw::color::as_shot_white_xy(&img);
let _ = lightcraft_raw::color::camera_transform(&img, xy);
}
}
proptest! {
#![proptest_config(ProptestConfig { cases: 400, .. ProptestConfig::default() })]
#[test]
fn mutated_dngs_never_panic(flips in proptest::collection::vec((any::<usize>(), any::<u8>()), 1..24), cut in any::<usize>(), lj in any::<bool>()) {
let mut data = sample(if lj { DngCompression::Lj92 { tile: 16 } } else { DngCompression::Uncompressed });
for (i, v) in flips {
let n = data.len();
data[i % n] = v;
}
let keep = if cut % 3 == 0 { cut % data.len() } else { data.len() };
exercise(&data[..keep]);
}
#[test]
fn random_tiff_like_bytes_never_panic(mut data in proptest::collection::vec(any::<u8>(), 8..700), kind in 0usize..4) {
let heads: [&[u8]; 4] = [b"II*\0\x08\0\0\0", b"MM\0*\0\0\0\x08", b"II*\0\x10\0\0\0CR\x02\0", b"II+\0\x08\0\0\0"];
let h = heads[kind];
let n = h.len().min(data.len());
data[..n].copy_from_slice(&h[..n]);
exercise(&data);
}
#[test]
fn random_lj92_never_panics(data in proptest::collection::vec(any::<u8>(), 0..400)) {
let mut s = vec![0xff, 0xd8, 0xff, 0xc3, 0, 11, 12, 0, 8, 0, 8, 1, 1, 0x11, 0];
s.extend_from_slice(&data);
let _ = lightcraft_raw::ljpeg::decode(&s, 1 << 20);
}
}
#[test]
fn truncation_at_every_length() {
for comp in [DngCompression::Uncompressed, DngCompression::Lj92 { tile: 16 }] {
let d = sample(comp);
for n in (0..d.len()).step_by(7) {
exercise(&d[..n]);
}
}
}
#[test]
fn hostile_dimensions_are_rejected_quickly() {
// a DNG claiming a 60000 × 60000 16-bit image with 10 bytes of data
use lightcraft_tiff::tags as t;
use lightcraft_tiff::{IfdBuilder, ImageData, TiffWriter, Value};
let mut ifd = IfdBuilder::new();
ifd.set(t::DNG_VERSION, Value::Byte(vec![1, 4, 0, 0]));
ifd.set(t::IMAGE_WIDTH, Value::Long(vec![60000]));
ifd.set(t::IMAGE_LENGTH, Value::Long(vec![60000]));
ifd.set(t::BITS_PER_SAMPLE, Value::Short(vec![16]));
ifd.set(t::PHOTOMETRIC, Value::Short(vec![32803]));
ifd.set(t::CFA_PATTERN_EP, Value::Byte(vec![0, 1, 1, 2]));
ifd.set(t::COMPRESSION, Value::Short(vec![7]));
ifd.set_image(ImageData::Tiles { tile_width: 60000, tile_height: 60000, tiles: vec![vec![0xff, 0xd8, 0xff, 0xd9]] });
let bytes = TiffWriter::default().write(&[ifd]).unwrap();
let t0 = std::time::Instant::now();
assert!(lightcraft_raw::decode(&bytes).is_err());
assert!(t0.elapsed().as_secs_f64() < 2.0);
}