about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--library/core/src/fmt/mod.rs28
-rw-r--r--src/test/run-make/fmt-write-bloat/Makefile25
-rw-r--r--src/test/run-make/fmt-write-bloat/main.rs32
3 files changed, 78 insertions, 7 deletions
diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs
index 23e8d1d856a..4ed62a620c4 100644
--- a/library/core/src/fmt/mod.rs
+++ b/library/core/src/fmt/mod.rs
@@ -1084,7 +1084,9 @@ pub fn write(output: &mut dyn Write, args: Arguments<'_>) -> Result {
             // a string piece.
             for (arg, piece) in fmt.iter().zip(args.pieces.iter()) {
                 formatter.buf.write_str(*piece)?;
-                run(&mut formatter, arg, &args.args)?;
+                // SAFETY: arg and args.args come from the same Arguments,
+                // which guarantees the indexes are always within bounds.
+                unsafe { run(&mut formatter, arg, &args.args) }?;
                 idx += 1;
             }
         }
@@ -1098,25 +1100,37 @@ pub fn write(output: &mut dyn Write, args: Arguments<'_>) -> Result {
     Ok(())
 }
 
-fn run(fmt: &mut Formatter<'_>, arg: &rt::v1::Argument, args: &[ArgumentV1<'_>]) -> Result {
+unsafe fn run(fmt: &mut Formatter<'_>, arg: &rt::v1::Argument, args: &[ArgumentV1<'_>]) -> Result {
     fmt.fill = arg.format.fill;
     fmt.align = arg.format.align;
     fmt.flags = arg.format.flags;
-    fmt.width = getcount(args, &arg.format.width);
-    fmt.precision = getcount(args, &arg.format.precision);
+    // SAFETY: arg and args come from the same Arguments,
+    // which guarantees the indexes are always within bounds.
+    unsafe {
+        fmt.width = getcount(args, &arg.format.width);
+        fmt.precision = getcount(args, &arg.format.precision);
+    }
 
     // Extract the correct argument
-    let value = args[arg.position];
+    debug_assert!(arg.position < args.len());
+    // SAFETY: arg and args come from the same Arguments,
+    // which guarantees its index is always within bounds.
+    let value = unsafe { args.get_unchecked(arg.position) };
 
     // Then actually do some printing
     (value.formatter)(value.value, fmt)
 }
 
-fn getcount(args: &[ArgumentV1<'_>], cnt: &rt::v1::Count) -> Option<usize> {
+unsafe fn getcount(args: &[ArgumentV1<'_>], cnt: &rt::v1::Count) -> Option<usize> {
     match *cnt {
         rt::v1::Count::Is(n) => Some(n),
         rt::v1::Count::Implied => None,
-        rt::v1::Count::Param(i) => args[i].as_usize(),
+        rt::v1::Count::Param(i) => {
+            debug_assert!(i < args.len());
+            // SAFETY: cnt and args come from the same Arguments,
+            // which guarantees this index is always within bounds.
+            unsafe { args.get_unchecked(i).as_usize() }
+        }
     }
 }
 
diff --git a/src/test/run-make/fmt-write-bloat/Makefile b/src/test/run-make/fmt-write-bloat/Makefile
new file mode 100644
index 00000000000..26e08086a72
--- /dev/null
+++ b/src/test/run-make/fmt-write-bloat/Makefile
@@ -0,0 +1,25 @@
+-include ../../run-make-fulldeps/tools.mk
+
+# ignore-windows
+
+ifeq ($(shell $(RUSTC) -vV | grep 'host: $(TARGET)'),)
+
+# Don't run this test when cross compiling.
+all:
+
+else
+
+NM = nm
+
+PANIC_SYMS = panic_bounds_check pad_integral Display Debug
+
+# Allow for debug_assert!() in debug builds of std.
+ifdef NO_DEBUG_ASSERTIONS
+PANIC_SYMS += panicking panic_fmt
+endif
+
+all: main.rs
+	$(RUSTC) $< -O
+	$(NM) $(call RUN_BINFILE,main) | $(CGREP) -v $(PANIC_SYMS)
+
+endif
diff --git a/src/test/run-make/fmt-write-bloat/main.rs b/src/test/run-make/fmt-write-bloat/main.rs
new file mode 100644
index 00000000000..e86c48014c3
--- /dev/null
+++ b/src/test/run-make/fmt-write-bloat/main.rs
@@ -0,0 +1,32 @@
+#![feature(lang_items)]
+#![feature(start)]
+#![no_std]
+
+use core::fmt;
+use core::fmt::Write;
+
+#[link(name = "c")]
+extern "C" {}
+
+struct Dummy;
+
+impl fmt::Write for Dummy {
+    #[inline(never)]
+    fn write_str(&mut self, _: &str) -> fmt::Result {
+        Ok(())
+    }
+}
+
+#[start]
+fn main(_: isize, _: *const *const u8) -> isize {
+    let _ = writeln!(Dummy, "Hello World");
+    0
+}
+
+#[lang = "eh_personality"]
+fn eh_personality() {}
+
+#[panic_handler]
+fn panic(_: &core::panic::PanicInfo) -> ! {
+    loop {}
+}