use crate::api::{JxlDecoder, JxlDecoderOptions, ProcessingResult, states};
fn assert_decode_rejects(data: &[u8], label: &str) {
let options = JxlDecoderOptions::default();
let decoder = JxlDecoder::<states::Initialized>::new(options);
let mut input: &[u8] = data;
let decoder = match decoder.process(&mut input) {
Err(e) => {
eprintln!("[{label}] Correctly rejected at header: {e}");
return;
}
Ok(ProcessingResult::NeedsMoreInput { .. }) => {
eprintln!("[{label}] Correctly rejected: needs more input (truncated)");
return;
}
Ok(ProcessingResult::Complete { result }) => result,
};
let mut input: &[u8] = &[];
match decoder.process(&mut input) {
Err(e) => eprintln!("[{label}] Correctly rejected at frame info: {e}"),
Ok(ProcessingResult::NeedsMoreInput { .. }) => {
eprintln!("[{label}] Correctly rejected: needs more input at frame stage")
}
Ok(ProcessingResult::Complete { .. }) => eprintln!(
"[{label}] WARNING: header+frame parsed without error, but no OOM — test passes (limits caught it)"
),
}
}
fn assert_header_rejects(data: &[u8], label: &str) {
match crate::read_header(data) {
Err(e) => {
eprintln!("[{label}] read_header correctly rejected: {e}");
}
Ok(info) => {
let (w, h) = info.info.size;
eprintln!(
"[{label}] read_header returned {w}x{h} — checking that dimensions are reasonable"
);
let total = w as u64 * h as u64;
assert!(
total <= (1u64 << 28),
"Parsed dimensions {w}x{h} = {total} pixels exceed default max_pixels limit"
);
}
}
}
#[test]
fn test_oom_push_decode_26bytes() {
let data = include_bytes!("oom_artifacts/oom_push_decode_26bytes.jxl");
assert_decode_rejects(data, "push_decode_26bytes");
assert_header_rejects(data, "push_decode_26bytes_header");
}
#[test]
fn test_oom_probe_19856bytes() {
let data = include_bytes!("oom_artifacts/oom_probe_19856bytes.jxl");
assert_decode_rejects(data, "probe_19856bytes");
assert_header_rejects(data, "probe_19856bytes_header");
}
#[test]
fn test_oom_animation_234bytes() {
let data = include_bytes!("oom_artifacts/oom_animation_234bytes.jxl");
assert_decode_rejects(data, "animation_234bytes");
assert_header_rejects(data, "animation_234bytes_header");
}
#[test]
fn test_default_limits_are_bounded() {
let limits = crate::api::JxlDecoderLimits::default();
assert!(
limits.max_pixels.unwrap() <= 1 << 28,
"Default max_pixels {} is too large",
limits.max_pixels.unwrap()
);
assert!(
limits.max_memory_bytes.is_some(),
"Default max_memory_bytes should be set"
);
let restrictive = crate::api::JxlDecoderLimits::restrictive();
assert!(
restrictive.max_memory_bytes.is_some(),
"Restrictive max_memory_bytes should be set"
);
}
#[test]
fn test_image_allocation_is_fallible() {
use crate::image::Image;
let result = Image::<f32>::new((32768, 32768));
match result {
Ok(_) => eprintln!("Large allocation succeeded (system has enough memory)"),
Err(e) => eprintln!("Large allocation correctly failed: {e}"),
}
}
#[test]
fn test_oom_container_781bytes() {
let data = include_bytes!("oom_artifacts/oom_container_781bytes.jxl");
match crate::decode(data) {
Err(e) => eprintln!("[container_781bytes] decode correctly rejected: {e}"),
Ok(_) => panic!("Expected error from crafted 781-byte container, got Ok"),
}
}
#[test]
fn test_icc_amplification_dos() {
let data: &[u8] = &[
0xff, 0x0a, 0xff, 0x00, 0x1a, 0xff, 0xd8, 0x55, 0x55, 0x55, 0x05, 0x34, 0x0a, 0x44, 0x49,
0x46, 0x00, 0x4e, 0x46,
];
assert_decode_rejects(data, "icc_amplification_19bytes");
}
#[test]
fn test_icc_amplification_dos_variant() {
let data: &[u8] = &[
0xff, 0x0a, 0x87, 0x40, 0x0a, 0x87, 0x87, 0x0a, 0x87, 0x59, 0x59, 0x59, 0xbb, 0xb3, 0xb3,
0xb3, 0x00, 0x59, 0x00,
];
assert_decode_rejects(data, "icc_amplification_variant");
}
#[test]
fn test_leak_memory_tracker_decode() {
let data1 = include_bytes!("oom_artifacts/leak_memory_tracker_decode_1.jxl");
let data2 = include_bytes!("oom_artifacts/leak_memory_tracker_decode_2.jxl");
let _ = crate::decode(data1);
let _ = crate::decode(data2);
}
#[test]
fn test_leak_memory_tracker_decode_with_limits() {
let data = include_bytes!("oom_artifacts/leak_memory_tracker_limits.jxl");
let mut limits = crate::api::JxlDecoderLimits::restrictive();
limits.max_pixels = Some(4_000_000);
limits.max_memory_bytes = Some(64 * 1024 * 1024);
let options = crate::api::JxlDecoderOptions {
limits,
parallel: false,
..Default::default()
};
let _ = crate::decode_with(data, options);
}