Skip to content

Commit 48265b7

Browse files
committed
Check for interior nulls in .to_c_str()
Previous dicussions about CString suggested that interior nulls should throw an error. This was never implemented. Add this now, using a condition (named null_byte) to allow for recovery. Add method .to_c_str_unchecked() that skips this check.
1 parent 1e4f13f commit 48265b7

File tree

2 files changed

+107
-12
lines changed

2 files changed

+107
-12
lines changed

src/libstd/c_str.rs

Lines changed: 99 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,28 @@
99
// except according to those terms.
1010

1111
use cast;
12-
use iterator::Iterator;
12+
use iterator::{Iterator,range};
1313
use libc;
1414
use ops::Drop;
1515
use option::{Option, Some, None};
1616
use ptr::RawPtr;
1717
use ptr;
1818
use str::StrSlice;
19-
use vec::ImmutableVector;
19+
use vec::{ImmutableVector,CopyableVector};
20+
use container::Container;
21+
22+
/// Resolution options for the `null_byte` condition
23+
pub enum NullByteResolution {
24+
/// Truncate at the null byte
25+
Truncate,
26+
/// Use a replacement byte
27+
ReplaceWith(libc::c_char)
28+
}
29+
30+
condition! {
31+
// this should be &[u8] but there's a lifetime issue
32+
null_byte: (~[u8]) -> super::NullByteResolution;
33+
}
2034

2135
/// The representation of a C String.
2236
///
@@ -110,31 +124,59 @@ impl Drop for CString {
110124

111125
/// A generic trait for converting a value to a CString.
112126
pub trait ToCStr {
113-
/// Create a C String.
127+
/// Copy the receiver into a CString.
128+
///
129+
/// # Failure
130+
///
131+
/// Raises the `null_byte` condition if the receiver has an interior null.
114132
fn to_c_str(&self) -> CString;
133+
134+
/// Unsafe variant of `to_c_str()` that doesn't check for nulls.
135+
unsafe fn to_c_str_unchecked(&self) -> CString;
115136
}
116137

117138
impl<'self> ToCStr for &'self str {
118139
#[inline]
119140
fn to_c_str(&self) -> CString {
120141
self.as_bytes().to_c_str()
121142
}
143+
144+
#[inline]
145+
unsafe fn to_c_str_unchecked(&self) -> CString {
146+
self.as_bytes().to_c_str_unchecked()
147+
}
122148
}
123149

124150
impl<'self> ToCStr for &'self [u8] {
125151
fn to_c_str(&self) -> CString {
126-
do self.as_imm_buf |self_buf, self_len| {
127-
unsafe {
128-
let buf = libc::malloc(self_len as libc::size_t + 1) as *mut u8;
129-
if buf.is_null() {
130-
fail!("failed to allocate memory!");
152+
let mut cs = unsafe { self.to_c_str_unchecked() };
153+
do cs.with_mut_ref |buf| {
154+
for i in range(0, self.len()) {
155+
unsafe {
156+
let p = buf.offset_inbounds(i as int);
157+
if *p == 0 {
158+
match null_byte::cond.raise(self.to_owned()) {
159+
Truncate => break,
160+
ReplaceWith(c) => *p = c
161+
}
162+
}
131163
}
164+
}
165+
}
166+
cs
167+
}
132168

133-
ptr::copy_memory(buf, self_buf, self_len);
134-
*ptr::mut_offset(buf, self_len as int) = 0;
135-
136-
CString::new(buf as *libc::c_char, true)
169+
unsafe fn to_c_str_unchecked(&self) -> CString {
170+
do self.as_imm_buf |self_buf, self_len| {
171+
let buf = libc::malloc(self_len as libc::size_t + 1) as *mut u8;
172+
if buf.is_null() {
173+
fail!("failed to allocate memory!");
137174
}
175+
176+
ptr::copy_memory(buf, self_buf, self_len);
177+
*ptr::mut_offset(buf, self_len as int) = 0;
178+
179+
CString::new(buf as *libc::c_char, true)
138180
}
139181
}
140182
}
@@ -231,4 +273,49 @@ mod tests {
231273
assert_eq!(iter.next(), Some('o' as libc::c_char));
232274
assert_eq!(iter.next(), None);
233275
}
276+
277+
#[test]
278+
#[ignore(cfg(windows))]
279+
fn test_to_c_str_fail() {
280+
use c_str::null_byte::cond;
281+
282+
let mut error_happened = false;
283+
do cond.trap(|err| {
284+
assert_eq!(err, bytes!("he", 0, "llo").to_owned())
285+
error_happened = true;
286+
Truncate
287+
}).inside {
288+
"he\x00llo".to_c_str()
289+
};
290+
assert!(error_happened);
291+
292+
do cond.trap(|_| {
293+
ReplaceWith('?' as libc::c_char)
294+
}).inside(|| "he\x00llo".to_c_str()).with_ref |buf| {
295+
unsafe {
296+
assert_eq!(*buf.offset(0), 'h' as libc::c_char);
297+
assert_eq!(*buf.offset(1), 'e' as libc::c_char);
298+
assert_eq!(*buf.offset(2), '?' as libc::c_char);
299+
assert_eq!(*buf.offset(3), 'l' as libc::c_char);
300+
assert_eq!(*buf.offset(4), 'l' as libc::c_char);
301+
assert_eq!(*buf.offset(5), 'o' as libc::c_char);
302+
assert_eq!(*buf.offset(6), 0);
303+
}
304+
}
305+
}
306+
307+
#[test]
308+
fn test_to_c_str_unchecked() {
309+
unsafe {
310+
do "he\x00llo".to_c_str_unchecked().with_ref |buf| {
311+
assert_eq!(*buf.offset(0), 'h' as libc::c_char);
312+
assert_eq!(*buf.offset(1), 'e' as libc::c_char);
313+
assert_eq!(*buf.offset(2), 0);
314+
assert_eq!(*buf.offset(3), 'l' as libc::c_char);
315+
assert_eq!(*buf.offset(4), 'l' as libc::c_char);
316+
assert_eq!(*buf.offset(5), 'o' as libc::c_char);
317+
assert_eq!(*buf.offset(6), 0);
318+
}
319+
}
320+
}
234321
}

src/libstd/path.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -569,6 +569,10 @@ impl ToCStr for PosixPath {
569569
fn to_c_str(&self) -> c_str::CString {
570570
self.to_str().to_c_str()
571571
}
572+
573+
unsafe fn to_c_str_unchecked(&self) -> c_str::CString {
574+
self.to_str().to_c_str_unchecked()
575+
}
572576
}
573577

574578
// FIXME (#3227): when default methods in traits are working, de-duplicate
@@ -781,6 +785,10 @@ impl c_str::ToCStr for WindowsPath {
781785
fn to_c_str(&self) -> c_str::CString {
782786
self.to_str().to_c_str()
783787
}
788+
789+
unsafe fn to_c_str_unchecked(&self) -> c_str::CString {
790+
self.to_str().to_c_str_unchecked()
791+
}
784792
}
785793

786794
impl GenericPath for WindowsPath {

0 commit comments

Comments
 (0)