ITADN
pdf-rs/pdf/Issues

Panic (multiply overflow) in `SampledFunction::apply`

#289OpenMinghuaWang 创建于 2026-06-12
M
MinghuaWangcommented
### Describe the bug `SampledFunction::apply` panics with `attempt to multiply with overflow` when evaluating a 1-D Type 0 (sampled) function whose `/Encode` array contains values that overflow `f32` to infinity. The panic occurs at `src/object/function.rs:375`, where `let idx = i * n_out` overflows when `i = usize::MAX`. The overflow to `usize::MAX` happens via this chain: 1. PDF `/Encode` array contains a decimal number large enough to overflow `f32` (e.g., 38 nines: `99999999999999999999999999999999999999.`) 2. `str::parse::<f32>()` produces `f32::INFINITY` 3. `SampledFunctionInput::map` (line 260) computes `y = x.mul_add(Inf, Inf) = Inf` 4. `y.floor() as usize` (line 261) saturates to `usize::MAX` (Rust >= 1.45 saturating float-to-int cast) 5. `usize::MAX * n_out` (line 313) panics with integer multiplication overflow when `n_out >= 2` . Panic info: Stack trace (relevant frames): ``` pdf::object::function::SampledFunctionInput::map -> x.mul_add(Inf, Inf) // Inf from overflowing Encode values -> Inf.floor() as usize // saturates to usize::MAX pdf::object::function::SampledFunction::apply -> let idx = i * n_out; // usize::MAX * 2 → PANIC (overflow) ``` Full stack trace: ``` thread 'main' panicked at pdf/src/object/function.rs:375:31: attempt to multiply with overflow 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_const::panic_const_mul_overflow at /rustc/ec7c02612527d185c379900b613311bc1dcbf7dc/library/core/src/panicking.rs:175:17 3: pdf::object::function::SampledFunction::apply at ./pdf/src/object/function.rs:375:31 4: pdf::object::function::Function::apply at ./pdf/src/object/function.rs:111:49 5: poc_nan_inf_panic::main at ./pdf/examples/poc_nan_inf_panic.rs:20:18 ``` ### Root cause In `src/object/function.rs`, the constructor stores `/Encode` values without validation: ```rust impl Object for Function { fn from_primitive(...) { // ... // lines 254-259 -- no validation that encode values are finite: .map(|(c, e, &s)| SampledFunctionInput { domain: (c[0], c[1]), encode_offset: e[0], // may be Inf/NaN encode_scale: e[1], // may be Inf/NaN size: s as usize, }) //... } } ``` Then `SampledFunctionInput::map` uses these values in arithmetic that produces Inf, which is then cast to `usize`: ```rust // lines 259-261: fn map(&self, x: f32) -> (usize, usize, f32) { let x = x.clamp(self.domain.0, self.domain.1); let y = x.mul_add(self.encode_scale, self.encode_offset); // Inf if scale/offset overflow (y.floor() as usize, self.size, y.fract()) // usize::MAX, _, NaN } ``` Then in `apply`, the index is multiplied by the output dimension count: ```rust // line 313: let idx = i * n_out; // ← usize::MAX * n_out → overflow panic when n_out >= 2 ``` ### To reproduce ```rust fn main() { use pdf::file::FileOptions; use pdf::object::ColorSpace; let pdf_path = concat!( env!("CARGO_MANIFEST_DIR"), "/examples/crash_nan_inf.pdf" ); let file = FileOptions::cached() .open(pdf_path) .expect("failed to open PDF"); let page = file.pages().next().unwrap().expect("failed to get page"); let resources = page.resources().expect("failed to get resources"); let cs = resources.color_spaces.get("CS1").expect("CS1 not found"); match cs { ColorSpace::Separation(_name, _alt, func) => { let mut out = [0.0f32; 2]; func.apply(&[0.5], &mut out).unwrap(); } _ => {} } } ``` Crash PDF: [crash_nan_inf.pdf](https://github.com/user-attachments/files/28861338/crash_nan_inf.pdf) ### Test environment - Version: pdf master branch - OS: Ubuntu 24.04, 64-bit - Rustc version: rustc 1.91.0-nightly (ec7c02612 2025-08-05) ### Possible fix Validate Encode values at construction: ```rust // In Object::from_primitive for Function, Type 0 branch: fn from_primitive(...) { // ... 0 => { // ... .map(|(c, e, &s)| { if !e[0].is_finite() || !e[1].is_finite() { bail!("Invalid sampled function encode: [{}, {}]", e[0], e[1]); } Ok(SampledFunctionInput { domain: (c[0], c[1]), encode_offset: e[0], encode_scale: e[1], size: s as usize, }) }) } } ```
0 条评论