Repository navigation
v0.2.1 — i32 stride OOB class fix
⚠️ YANKED — this version has been pulled from crates.io. It contains a use-after-free inStreamingDecoder::update(fixed in 0.3.4) plus other soundness issues fixed across the 0.2.x / 0.3.x series. Use 0.3.4 or later — or, recommended for new projects, switch tozenwebp, which is#![forbid(unsafe_code)]and has no FFI exposure.
Patch release closing a class of OOB issues found in a second-pass audit.
Want pure-Rust WebP? For new projects,
zenwebpis the recommended choice — a pure-Rust port of libwebp built with#![forbid(unsafe_code)], with full feature parity, comparable runtime performance and compression, and no FFI surface area.webpxis maintained for existing users.
Soundness fixes
Encoder & streaming stride values cast to i32 without an upper-bound check. A caller-supplied u32 / usize stride >= 2^31 wraps to a negative i32; libwebp's row pointer arithmetic then walks backwards through process memory. Reachable from safe Rust without any unsafe block at the call site by constructing a &[u8] of >= 2 GiB on 64-bit and passing a stride above i32::MAX. Fixed at every site:
Encoder::new_rgba_stride/new_bgra_stride/new_rgb_stride/new_bgr_stride(viavalidate_buffer_size_stride)Encoder::new_argb/new_argb_strideEncoder::new_yuv(y_stride,uv_stride,a_stride— viavalidate_yuv_planes)- The same paths in
import_pixelsforencode_owned StreamingDecoder::with_buffer
Encoder::from_img and Encoder::from_pixels_stride truncated (stride * bpp) as u32 when the source usize stride exceeded u32::MAX / bpp. Truncation produced a stride smaller than the actual byte stride; validate_buffer_size_stride couldn't catch it and libwebp encoded with the wrong row layout — silent wrong-output bug, not OOB on the input slice. Fixed with a saturating cast so oversized strides hit the new i32::MAX upper-bound check.
Latent UB
All MaybeUninit::<libwebp_sys::*>::uninit() sites switched to MaybeUninit::<libwebp_sys::*>::zeroed(). libwebp's *Init* functions (WebPMemoryWriterInit, WebPAnimDecoderOptionsInit, WebPAnimEncoderOptionsInit, etc.) only set their documented fields; the pad: [u32; 1] / padding: [u32; 7] / padding: [u32; 4] arrays they leave alone are now initialized to zero rather than uninitialized. Per Rust's UB rules, reading uninit bytes during assume_init is technically UB even when those bytes are never observed.
Deprecations
compat::webpandcompat::webp_animationmodules and every public type carry#[deprecated(since = "0.2.1")]. Both shims swallow error variants the main API surfaces and don't exposeLimitsfor untrusted-input decoding. They're retained for at least one minor release.
Documentation
Encoder::new_argbrustdoc now spells out the little-endian-only assumption: the0xAARRGGBBnumeric layout matches libwebp's expected[B, G, R, A]byte order on every CI target. Big-endian callers should treat this path as unsupported.
Regression tests
tests/soundness.rs gains a stride_overflow module with four regression tests covering ARGB, RGBA, YUV, and streaming with_buffer stride bounds. Total soundness regression count: 8 → 12. Full suite: 264 tests.
See CHANGELOG.md for the full diff summary.