Skip to content

Commit cf2879c

Browse files
committed
UB free test for CString Drop
1 parent 2c2552b commit cf2879c

File tree

3 files changed

+63
-31
lines changed

3 files changed

+63
-31
lines changed

src/libstd/ffi/c_str.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,7 @@ impl CString {
313313
#[stable(feature = "cstring_drop", since = "1.13.0")]
314314
impl Drop for CString {
315315
fn drop(&mut self) {
316-
unsafe { *self.inner.get_unchecked_mut(0) = 0; }
316+
unsafe { ptr::write_volatile(self.inner.as_mut_ptr(), 0u8) }
317317
}
318318
}
319319

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// no-prefer-dynamic
12+
13+
#![feature(allocator, core_intrinsics, libc)]
14+
#![allocator]
15+
#![crate_type = "rlib"]
16+
#![no_std]
17+
18+
extern crate libc;
19+
20+
#[no_mangle]
21+
pub extern fn __rust_allocate(size: usize, align: usize) -> *mut u8 {
22+
unsafe {
23+
libc::malloc(size as libc::size_t) as *mut u8
24+
}
25+
}
26+
27+
#[no_mangle]
28+
pub extern fn __rust_deallocate(ptr: *mut u8, old_size: usize, align: usize) {
29+
// Do nothing at all.
30+
}
31+
32+
#[no_mangle]
33+
pub extern fn __rust_reallocate(ptr: *mut u8, old_size: usize, size: usize,
34+
align: usize) -> *mut u8 {
35+
unsafe {
36+
libc::realloc(ptr as *mut _, size as libc::size_t) as *mut u8
37+
}
38+
}
39+
40+
#[no_mangle]
41+
pub extern fn __rust_reallocate_inplace(ptr: *mut u8, old_size: usize,
42+
size: usize, align: usize) -> usize {
43+
unsafe { core::intrinsics::abort() }
44+
}
45+
46+
#[no_mangle]
47+
pub extern fn __rust_usable_size(size: usize, align: usize) -> usize {
48+
unsafe { core::intrinsics::abort() }
49+
}

src/test/run-pass/cstring-drop.rs

+13-30
Original file line numberDiff line numberDiff line change
@@ -8,42 +8,25 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
// ignore-emscripten
11+
// no-prefer-dynamic
12+
// aux-build:allocator-leaking.rs
1213

1314
// Test that `CString::new("hello").unwrap().as_ptr()` pattern
1415
// leads to failure.
1516

16-
use std::env;
17-
use std::ffi::{CString, CStr};
18-
use std::os::raw::c_char;
19-
use std::process::{Command, Stdio};
17+
extern crate allocator_leaking;
2018

21-
fn main() {
22-
let args: Vec<String> = env::args().collect();
23-
if args.len() > 1 && args[1] == "child" {
24-
// Repeat several times to be more confident that
25-
// it is `Drop` for `CString` that does the cleanup,
26-
// and not just some lucky UB.
27-
let xs = vec![CString::new("Hello").unwrap(); 10];
28-
let ys = xs.iter().map(|s| s.as_ptr()).collect::<Vec<_>>();
29-
drop(xs);
30-
assert!(ys.into_iter().any(is_hello));
31-
return;
32-
}
33-
34-
let output = Command::new(&args[0]).arg("child").output().unwrap();
35-
assert!(!output.status.success());
36-
}
19+
use std::ffi::CString;
20+
use std::ptr;
3721

38-
fn is_hello(s: *const c_char) -> bool {
39-
// `s` is a dangling pointer and reading it is technically
22+
fn main() {
23+
let ptr = CString::new("Hello").unwrap().as_ptr();
24+
// `ptr` is a dangling pointer and reading it is almost always
4025
// undefined behavior. But we want to prevent the most diabolical
4126
// kind of UB (apart from nasal demons): reading a value that was
42-
// previously written.
43-
//
44-
// Segfaulting or reading an empty string is Ok,
45-
// reading "Hello" is bad.
46-
let s = unsafe { CStr::from_ptr(s) };
47-
let hello = CString::new("Hello").unwrap();
48-
s == hello.as_ref()
27+
// previously written. So we make sure that CString zeros the
28+
// first byte in the `Drop`.
29+
// To make the test itself UB-free we use a custom allocator
30+
// which always leaks memory.
31+
assert_eq!(unsafe { ptr::read(ptr as *const [u8; 6]) } , *b"\0ello\0");
4932
}

0 commit comments

Comments
 (0)