diff --git a/src/libcore/fmt/float.rs b/src/libcore/fmt/float.rs index b52b56b1bdbc2..fe96ae9bb6e70 100644 --- a/src/libcore/fmt/float.rs +++ b/src/libcore/fmt/float.rs @@ -2,8 +2,6 @@ use crate::fmt::{Formatter, Result, LowerExp, UpperExp, Display, Debug}; use crate::mem::MaybeUninit; use crate::num::flt2dec; -// ignore-tidy-undocumented-unsafe - // Don't inline this so callers don't use the stack space this function // requires unless they have to. #[inline(never)] @@ -11,6 +9,7 @@ fn float_to_decimal_common_exact(fmt: &mut Formatter<'_>, num: &T, sign: flt2dec::Sign, precision: usize) -> Result where T: flt2dec::DecodableFloat { + // SAFETY: possible undefined behavior, see comment unsafe { let mut buf = MaybeUninit::<[u8; 1024]>::uninit(); // enough for f32 and f64 let mut parts = MaybeUninit::<[flt2dec::Part<'_>; 4]>::uninit(); @@ -33,6 +32,7 @@ fn float_to_decimal_common_shortest(fmt: &mut Formatter<'_>, num: &T, sign: flt2dec::Sign, precision: usize) -> Result where T: flt2dec::DecodableFloat { + // SAFETY: possible undefined behavior, see comment unsafe { // enough for f32 and f64 let mut buf = MaybeUninit::<[u8; flt2dec::MAX_SIG_DIGITS]>::uninit(); @@ -73,6 +73,7 @@ fn float_to_exponential_common_exact(fmt: &mut Formatter<'_>, num: &T, upper: bool) -> Result where T: flt2dec::DecodableFloat { + // SAFETY: possible undefined behavior, see comment unsafe { let mut buf = MaybeUninit::<[u8; 1024]>::uninit(); // enough for f32 and f64 let mut parts = MaybeUninit::<[flt2dec::Part<'_>; 6]>::uninit(); @@ -92,6 +93,7 @@ fn float_to_exponential_common_shortest(fmt: &mut Formatter<'_>, upper: bool) -> Result where T: flt2dec::DecodableFloat { + // SAFETY: possible undefined behavior, see comment unsafe { // enough for f32 and f64 let mut buf = MaybeUninit::<[u8; flt2dec::MAX_SIG_DIGITS]>::uninit(); diff --git a/src/libcore/fmt/mod.rs b/src/libcore/fmt/mod.rs index 5a039144f667f..74d47010fb747 100644 --- a/src/libcore/fmt/mod.rs +++ b/src/libcore/fmt/mod.rs @@ -1,7 +1,5 @@ //! Utilities for formatting and printing strings. -// ignore-tidy-undocumented-unsafe - #![stable(feature = "rust1", since = "1.0.0")] use crate::cell::{UnsafeCell, Cell, RefCell, Ref, RefMut}; @@ -279,6 +277,7 @@ impl<'a> ArgumentV1<'a> { issue = "0")] pub fn new<'b, T>(x: &'b T, f: fn(&T, &mut Formatter<'_>) -> Result) -> ArgumentV1<'b> { + // SAFETY: relies on T being sized to avoid undefined behavior unsafe { ArgumentV1 { formatter: mem::transmute(f), @@ -296,6 +295,7 @@ impl<'a> ArgumentV1<'a> { fn as_usize(&self) -> Option { if self.formatter as usize == ArgumentV1::show_usize as usize { + // SAFETY: if the formatter is show_usize, it means it came in as &usize Some(unsafe { *(self.value as *const _ as *const usize) }) } else { None @@ -1355,6 +1355,8 @@ impl<'a> Formatter<'a> { let mut align = old_align; if self.sign_aware_zero_pad() { // a sign always goes first + // SAFETY: formatted.sign is always generated from determine_sign which is + // valid utf8 let sign = unsafe { str::from_utf8_unchecked(formatted.sign) }; self.buf.write_str(sign)?; @@ -1386,6 +1388,8 @@ impl<'a> Formatter<'a> { fn write_formatted_parts(&mut self, formatted: &flt2dec::Formatted<'_>) -> Result { fn write_bytes(buf: &mut dyn Write, s: &[u8]) -> Result { + // SAFETY: formatted.sign is always generated from determine_sign which is + // valid utf8 buf.write_str(unsafe { str::from_utf8_unchecked(s) }) } diff --git a/src/libcore/fmt/num.rs b/src/libcore/fmt/num.rs index 3c7aefc090f8e..0e3f7e3db3411 100644 --- a/src/libcore/fmt/num.rs +++ b/src/libcore/fmt/num.rs @@ -1,8 +1,5 @@ //! Integer and floating-point number formatting -// ignore-tidy-undocumented-unsafe - - use crate::fmt; use crate::ops::{Div, Rem, Sub}; use crate::str; @@ -83,6 +80,7 @@ trait GenericRadix { } } let buf = &buf[curr..]; + // SAFETY: only chars in buf are created by Self::digit which are asuumed to be valid utf8 let buf = unsafe { str::from_utf8_unchecked(slice::from_raw_parts( MaybeUninit::first_ptr(buf), buf.len() @@ -191,49 +189,62 @@ static DEC_DIGITS_LUT: &[u8; 200] = macro_rules! impl_Display { ($($t:ident),* as $u:ident via $conv_fn:ident named $name:ident) => { fn $name(mut n: $u, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // 2^128 is is about 3*10^38, so 39 gives an extra byte of space let mut buf = [MaybeUninit::::uninit(); 39]; let mut curr = buf.len() as isize; let buf_ptr = MaybeUninit::first_ptr_mut(&mut buf); let lut_ptr = DEC_DIGITS_LUT.as_ptr(); - unsafe { - // need at least 16 bits for the 4-characters-at-a-time to work. - assert!(crate::mem::size_of::<$u>() >= 2); + // need at least 16 bits for the 4-characters-at-a-time to work. + assert!(crate::mem::size_of::<$u>() >= 2); - // eagerly decode 4 characters at a time - while n >= 10000 { - let rem = (n % 10000) as isize; - n /= 10000; + // eagerly decode 4 characters at a time + while n >= 10000 { + let rem = (n % 10000) as isize; + n /= 10000; - let d1 = (rem / 100) << 1; - let d2 = (rem % 100) << 1; - curr -= 4; + let d1 = (rem / 100) << 1; + let d2 = (rem % 100) << 1; + curr -= 4; + // SAFETY: d1, d2 are each max 198, so buf_ptr[d1..d1 + 1] is safe to access + unsafe { ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2); ptr::copy_nonoverlapping(lut_ptr.offset(d2), buf_ptr.offset(curr + 2), 2); } + } - // if we reach here numbers are <= 9999, so at most 4 chars long - let mut n = n as isize; // possibly reduce 64bit math + // if we reach here numbers are <= 9999, so at most 4 chars long + let mut n = n as isize; // possibly reduce 64bit math - // decode 2 more chars, if > 2 chars - if n >= 100 { - let d1 = (n % 100) << 1; - n /= 100; - curr -= 2; + // decode 2 more chars, if > 2 chars + if n >= 100 { + let d1 = (n % 100) << 1; + n /= 100; + curr -= 2; + // SAFETY: d1 is max 198, so buf_ptr[d1..d1 + 1] is safe to access + unsafe { ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2); } + } - // decode last 1 or 2 chars - if n < 10 { - curr -= 1; + // decode last 1 or 2 chars + if n < 10 { + curr -= 1; + // SAFETY: curr is still less than buf.len() and since n < 10, n + '0' is valid utf8 + unsafe { *buf_ptr.offset(curr) = (n as u8) + b'0'; - } else { - let d1 = n << 1; - curr -= 2; + } + } else { + let d1 = n << 1; + curr -= 2; + // SAFETY: d1 is max 18, so buf_ptr[d1..d1 + 1] is safe to access + unsafe { ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2); } } + // SAFETY: curr > 0 (since we made buf large enough), and all the chars are valid utf8 + // since DEC_DIGITS_LUT is let buf_slice = unsafe { str::from_utf8_unchecked( slice::from_raw_parts(buf_ptr.offset(curr), buf.len() - curr as usize))