Skip to content

Commit 04b776e

Browse files
committed
Zero first byte of CString on drop
This should prevent code like ``` let ptr = CString::new("hello").unwrap().as_ptr(); ``` from working by accident.
1 parent 1d04201 commit 04b776e

File tree

1 file changed

+25
-3
lines changed

1 file changed

+25
-3
lines changed

src/libstd/ffi/c_str.rs

+25-3
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use mem;
1919
use memchr;
2020
use ops;
2121
use os::raw::c_char;
22+
use ptr;
2223
use slice;
2324
use str::{self, Utf8Error};
2425

@@ -68,6 +69,9 @@ use str::{self, Utf8Error};
6869
#[derive(PartialEq, PartialOrd, Eq, Ord, Hash, Clone)]
6970
#[stable(feature = "rust1", since = "1.0.0")]
7071
pub struct CString {
72+
// Invariant 1: the slice ends with a zero byte and has a length of at least one.
73+
// Invariant 2: the slice contains only one zero byte.
74+
// Improper usage of unsafe function can break Invariant 2, but not Invariant 1.
7175
inner: Box<[u8]>,
7276
}
7377

@@ -244,7 +248,7 @@ impl CString {
244248
/// Failure to call `from_raw` will lead to a memory leak.
245249
#[stable(feature = "cstr_memory", since = "1.4.0")]
246250
pub fn into_raw(self) -> *mut c_char {
247-
Box::into_raw(self.inner) as *mut c_char
251+
Box::into_raw(self.into_inner()) as *mut c_char
248252
}
249253

250254
/// Converts the `CString` into a `String` if it contains valid Unicode data.
@@ -265,7 +269,7 @@ impl CString {
265269
/// it is guaranteed to not have any interior nul bytes.
266270
#[stable(feature = "cstring_into", since = "1.7.0")]
267271
pub fn into_bytes(self) -> Vec<u8> {
268-
let mut vec = self.inner.into_vec();
272+
let mut vec = self.into_inner().into_vec();
269273
let _nul = vec.pop();
270274
debug_assert_eq!(_nul, Some(0u8));
271275
vec
@@ -275,7 +279,7 @@ impl CString {
275279
/// includes the trailing nul byte.
276280
#[stable(feature = "cstring_into", since = "1.7.0")]
277281
pub fn into_bytes_with_nul(self) -> Vec<u8> {
278-
self.inner.into_vec()
282+
self.into_inner().into_vec()
279283
}
280284

281285
/// Returns the contents of this `CString` as a slice of bytes.
@@ -293,6 +297,24 @@ impl CString {
293297
pub fn as_bytes_with_nul(&self) -> &[u8] {
294298
&self.inner
295299
}
300+
301+
// Bypass "move out of struct which implements `Drop` trait" restriction.
302+
fn into_inner(self) -> Box<[u8]> {
303+
unsafe {
304+
let result = ptr::read(&self.inner);
305+
mem::forget(self);
306+
result
307+
}
308+
}
309+
}
310+
311+
// Turns this `CString` into an empty string to prevent
312+
// memory unsafe code from working by accident.
313+
#[stable(feature = "cstring_drop", since = "1.13.0")]
314+
impl Drop for CString {
315+
fn drop(&mut self) {
316+
unsafe { *self.inner.get_unchecked_mut(0) = 0; }
317+
}
296318
}
297319

298320
#[stable(feature = "rust1", since = "1.0.0")]

0 commit comments

Comments
 (0)