diff --git a/README.md b/README.md index 58028a8..bf394f9 100644 --- a/README.md +++ b/README.md @@ -244,6 +244,12 @@ used in Firefox. ## Release Notes +### 0.8.5 + +* If debug assertions are enabled but fuzzing isn't enabled, lossy conversions + to Latin1 in the `mem` module assert that the input is in the range + U+0000...U+00FF (inclusive). + ### 0.8.4 * Fix SSE2-specific, `simd-accel`-specific memory corruption introduced in diff --git a/src/mem.rs b/src/mem.rs index 81c5b6e..e069612 100644 --- a/src/mem.rs +++ b/src/mem.rs @@ -31,6 +31,10 @@ use super::EncoderResult; use ascii::*; use utf_8::*; +macro_rules! non_fuzz_debug_assert { + ($($arg:tt)*) => (if !cfg!(fuzzing) { debug_assert!($($arg)*); }) +} + cfg_if!{ if #[cfg(feature = "simd-accel")] { use ::std::intrinsics::unlikely; @@ -1718,6 +1722,7 @@ pub fn convert_latin1_to_str(src: &[u8], dst: &mut str) -> usize { /// each output byte. /// /// If the input does not fulfill the condition stated above, this function +/// panics if debug assertions are enabled (and fuzzing isn't) and otherwise /// does something that is memory-safe without any promises about any /// properties of the output. In particular, callers shouldn't assume the /// output to be the same across crate versions or CPU architectures and @@ -1731,12 +1736,16 @@ pub fn convert_latin1_to_str(src: &[u8], dst: &mut str) -> usize { /// # Panics /// /// Panics if the destination buffer is shorter than stated above. +/// +/// If debug assertions are enabled (and not fuzzing) and the input is +/// not in the range U+0000 to U+00FF, inclusive. #[inline] pub fn convert_utf8_to_latin1_lossy(src: &[u8], dst: &mut [u8]) -> usize { assert!( dst.len() >= src.len(), "Destination must not be shorter than the source." ); + non_fuzz_debug_assert!(is_utf8_latin1(src)); let src_len = src.len(); let src_ptr = src.as_ptr(); let dst_ptr = dst.as_mut_ptr(); @@ -1777,10 +1786,11 @@ pub fn convert_utf8_to_latin1_lossy(src: &[u8], dst: &mut [u8]) -> usize { /// each output byte. /// /// If the input does not fulfill the condition stated above, this function +/// panics if debug assertions are enabled (and fuzzing isn't) and otherwise /// does something that is memory-safe without any promises about any /// properties of the output. In particular, callers shouldn't assume the /// output to be the same across crate versions or CPU architectures and -/// should not assume that non-Basic Latin input can't map to ASCII output. +/// should not assume that non-ASCII input can't map to ASCII output. /// /// The length of the destination buffer must be at least the length of the /// source buffer. @@ -1790,12 +1800,16 @@ pub fn convert_utf8_to_latin1_lossy(src: &[u8], dst: &mut [u8]) -> usize { /// # Panics /// /// Panics if the destination buffer is shorter than stated above. +/// +/// If debug assertions are enabled (and not fuzzing) and the input is +/// not in the range U+0000 to U+00FF, inclusive. #[inline] pub fn convert_utf16_to_latin1_lossy(src: &[u16], dst: &mut [u8]) { assert!( dst.len() >= src.len(), "Destination must not be shorter than the source." ); + non_fuzz_debug_assert!(is_utf16_latin1(src)); unsafe { pack_latin1(src.as_ptr(), dst.as_mut_ptr(), src.len()); } @@ -2163,6 +2177,13 @@ mod tests { assert_eq!(dst, reference); } + #[test] + #[should_panic] + fn test_convert_utf8_to_latin1_lossy_panics() { + let mut dst = [0u8; 16]; + let _ = convert_utf8_to_latin1_lossy("\u{100}".as_bytes(), &mut dst[..]); + } + #[test] fn test_convert_utf16_to_latin1_lossy() { let mut src: Vec = Vec::with_capacity(256); @@ -2179,6 +2200,13 @@ mod tests { assert_eq!(dst, reference); } + #[test] + #[should_panic] + fn test_convert_utf16_to_latin1_lossy_panics() { + let mut dst = [0u8; 16]; + let _ = convert_utf16_to_latin1_lossy(&[0x0100u16], &mut dst[..]); + } + #[test] fn test_utf16_valid_up_to() { let valid = vec![