diff --git a/src/libstd/ffi/c_str.rs b/src/libstd/ffi/c_str.rs index 1c449712e1f7a..ab9a4e0c2847b 100644 --- a/src/libstd/ffi/c_str.rs +++ b/src/libstd/ffi/c_str.rs @@ -313,7 +313,7 @@ impl CString { #[stable(feature = "cstring_drop", since = "1.13.0")] impl Drop for CString { fn drop(&mut self) { - unsafe { *self.inner.get_unchecked_mut(0) = 0; } + unsafe { ptr::write_volatile(self.inner.as_mut_ptr(), 0u8) } } } diff --git a/src/test/run-pass/auxiliary/allocator-leaking.rs b/src/test/run-pass/auxiliary/allocator-leaking.rs new file mode 100644 index 0000000000000..48575bd634a26 --- /dev/null +++ b/src/test/run-pass/auxiliary/allocator-leaking.rs @@ -0,0 +1,49 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// no-prefer-dynamic + +#![feature(allocator, core_intrinsics, libc)] +#![allocator] +#![crate_type = "rlib"] +#![no_std] + +extern crate libc; + +#[no_mangle] +pub extern fn __rust_allocate(size: usize, align: usize) -> *mut u8 { + unsafe { + libc::malloc(size as libc::size_t) as *mut u8 + } +} + +#[no_mangle] +pub extern fn __rust_deallocate(ptr: *mut u8, old_size: usize, align: usize) { + // Do nothing at all. +} + +#[no_mangle] +pub extern fn __rust_reallocate(ptr: *mut u8, old_size: usize, size: usize, + align: usize) -> *mut u8 { + unsafe { + libc::realloc(ptr as *mut _, size as libc::size_t) as *mut u8 + } +} + +#[no_mangle] +pub extern fn __rust_reallocate_inplace(ptr: *mut u8, old_size: usize, + size: usize, align: usize) -> usize { + unsafe { core::intrinsics::abort() } +} + +#[no_mangle] +pub extern fn __rust_usable_size(size: usize, align: usize) -> usize { + unsafe { core::intrinsics::abort() } +} diff --git a/src/test/run-pass/cstring-drop.rs b/src/test/run-pass/cstring-drop.rs index 960391bb8deac..f56ddcc4dd162 100644 --- a/src/test/run-pass/cstring-drop.rs +++ b/src/test/run-pass/cstring-drop.rs @@ -8,42 +8,25 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// ignore-emscripten +// no-prefer-dynamic +// aux-build:allocator-leaking.rs // Test that `CString::new("hello").unwrap().as_ptr()` pattern // leads to failure. -use std::env; -use std::ffi::{CString, CStr}; -use std::os::raw::c_char; -use std::process::{Command, Stdio}; +extern crate allocator_leaking; -fn main() { - let args: Vec = env::args().collect(); - if args.len() > 1 && args[1] == "child" { - // Repeat several times to be more confident that - // it is `Drop` for `CString` that does the cleanup, - // and not just some lucky UB. - let xs = vec![CString::new("Hello").unwrap(); 10]; - let ys = xs.iter().map(|s| s.as_ptr()).collect::>(); - drop(xs); - assert!(ys.into_iter().any(is_hello)); - return; - } - - let output = Command::new(&args[0]).arg("child").output().unwrap(); - assert!(!output.status.success()); -} +use std::ffi::CString; +use std::ptr; -fn is_hello(s: *const c_char) -> bool { - // `s` is a dangling pointer and reading it is technically +fn main() { + let ptr = CString::new("Hello").unwrap().as_ptr(); + // `ptr` is a dangling pointer and reading it is almost always // undefined behavior. But we want to prevent the most diabolical // kind of UB (apart from nasal demons): reading a value that was - // previously written. - // - // Segfaulting or reading an empty string is Ok, - // reading "Hello" is bad. - let s = unsafe { CStr::from_ptr(s) }; - let hello = CString::new("Hello").unwrap(); - s == hello.as_ref() + // previously written. So we make sure that CString zeros the + // first byte in the `Drop`. + // To make the test itself UB-free we use a custom allocator + // which always leaks memory. + assert_eq!(unsafe { ptr::read(ptr as *const [u8; 6]) } , *b"\0ello\0"); }