diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index 72e0401e7ab77..c4b23fd191955 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -2701,9 +2701,14 @@ pub fn get_item_val(ccx: &CrateContext, id: ast::NodeId) -> ValueRef { unsafe { let llty = llvm::LLVMTypeOf(v); - let g = sym.as_slice().with_c_str(|buf| { + let g = match sym.as_slice().with_c_str_opt(|buf| { llvm::LLVMAddGlobal(ccx.llmod(), llty, buf) - }); + }) { + Some(x) => x, + None => ccx.sess().fatal( + format!("Internal null byte in export_name value: `{}`", + sym).as_slice()) + }; // Apply the `unnamed_addr` attribute if // requested @@ -2765,9 +2770,14 @@ pub fn get_item_val(ccx: &CrateContext, id: ast::NodeId) -> ValueRef { match attr::first_attr_value_str_by_name(i.attrs.as_slice(), "link_section") { Some(sect) => unsafe { - sect.get().with_c_str(|buf| { + match sect.get().with_c_str_opt(|buf| { llvm::LLVMSetSection(v, buf); - }) + }) { + Some(_) => (), + None => ccx.sess().fatal( + format!("Internal null byte in link_section value: `{}`", + sect.get()).as_slice()) + } }, None => () } diff --git a/src/librustrt/c_str.rs b/src/librustrt/c_str.rs index 04a4e96ecc40e..05f67ac2cef9d 100644 --- a/src/librustrt/c_str.rs +++ b/src/librustrt/c_str.rs @@ -326,6 +326,9 @@ pub trait ToCStr { /// Fails the task if the receiver has an interior null. fn to_c_str(&self) -> CString; + /// Variant of to_c_str that returns None if the receiver has an interior null. + fn to_c_str_opt(&self) -> Option; + /// Unsafe variant of `to_c_str()` that doesn't check for nulls. unsafe fn to_c_str_unchecked(&self) -> CString; @@ -353,6 +356,12 @@ pub trait ToCStr { f(c_str.as_ptr()) } + /// Variant of with_c_str that returns None if the receiver has an interior null. + #[inline] + fn with_c_str_opt(&self, f: |*const libc::c_char| -> T) -> Option { + self.to_c_str_opt().map( |c_str| f(c_str.as_ptr()) ) + } + /// Unsafe variant of `with_c_str()` that doesn't check for nulls. #[inline] unsafe fn with_c_str_unchecked(&self, f: |*const libc::c_char| -> T) -> T { @@ -375,6 +384,11 @@ impl<'a> ToCStr for &'a str { self.as_bytes().to_c_str() } + #[inline] + fn to_c_str_opt(&self) -> Option { + self.as_bytes().to_c_str_opt() + } + #[inline] unsafe fn to_c_str_unchecked(&self) -> CString { self.as_bytes().to_c_str_unchecked() @@ -385,6 +399,11 @@ impl<'a> ToCStr for &'a str { self.as_bytes().with_c_str(f) } + #[inline] + fn with_c_str_opt(&self, f: |*const libc::c_char| -> T) -> Option { + self.as_bytes().with_c_str_opt(f) + } + #[inline] unsafe fn with_c_str_unchecked(&self, f: |*const libc::c_char| -> T) -> T { self.as_bytes().with_c_str_unchecked(f) @@ -397,6 +416,11 @@ impl ToCStr for String { self.as_bytes().to_c_str() } + #[inline] + fn to_c_str_opt(&self) -> Option { + self.as_bytes().to_c_str_opt() + } + #[inline] unsafe fn to_c_str_unchecked(&self) -> CString { self.as_bytes().to_c_str_unchecked() @@ -407,6 +431,11 @@ impl ToCStr for String { self.as_bytes().with_c_str(f) } + #[inline] + fn with_c_str_opt(&self, f: |*const libc::c_char| -> T) -> Option { + self.as_bytes().with_c_str_opt(f) + } + #[inline] unsafe fn with_c_str_unchecked(&self, f: |*const libc::c_char| -> T) -> T { self.as_bytes().with_c_str_unchecked(f) @@ -418,9 +447,19 @@ static BUF_LEN: uint = 128; impl<'a> ToCStr for &'a [u8] { fn to_c_str(&self) -> CString { + match self.to_c_str_opt() { + Some(c) => c, + None=> fail!( "{} contains an internal NULL byte", self) + } + } + + fn to_c_str_opt(&self) -> Option { let mut cs = unsafe { self.to_c_str_unchecked() }; - check_for_null(*self, cs.as_mut_ptr()); - cs + if check_for_null(*self, cs.as_mut_ptr()) { + None + } else { + Some(cs) + } } unsafe fn to_c_str_unchecked(&self) -> CString { @@ -434,45 +473,61 @@ impl<'a> ToCStr for &'a [u8] { } fn with_c_str(&self, f: |*const libc::c_char| -> T) -> T { - unsafe { with_c_str(*self, true, f) } + unsafe { + match with_c_str(*self, true, f) { + Some(l) => l, + None => fail!("{} contains an internal NULL byte.\n", self) + } + } + } + + fn with_c_str_opt(&self, f: |*const libc::c_char| -> T) -> Option { + unsafe { + with_c_str(*self, true, f ) + } } unsafe fn with_c_str_unchecked(&self, f: |*const libc::c_char| -> T) -> T { - with_c_str(*self, false, f) + with_c_str(*self, false, f).unwrap() //with_c_str never returns none if checked is false } } // Unsafe function that handles possibly copying the &[u8] into a stack array. +// Returns None if checked is true and and v contains a null byte, and Some(T) otherwise. unsafe fn with_c_str(v: &[u8], checked: bool, - f: |*const libc::c_char| -> T) -> T { - let c_str = if v.len() < BUF_LEN { + f: |*const libc::c_char| -> T) -> Option { + if v.len() < BUF_LEN { let mut buf: [u8, .. BUF_LEN] = mem::uninitialized(); slice::bytes::copy_memory(buf, v); buf[v.len()] = 0; let buf = buf.as_mut_ptr(); - if checked { - check_for_null(v, buf as *mut libc::c_char); + if checked && check_for_null(v, buf as *mut libc::c_char) { + None + } else { + Some(f(buf as *const libc::c_char)) } - - return f(buf as *const libc::c_char) - } else if checked { - v.to_c_str() } else { - v.to_c_str_unchecked() - }; - - f(c_str.as_ptr()) + let c_str_opt = if checked { + v.to_c_str_opt() + } else { + Some(v.to_c_str_unchecked()) + }; + c_str_opt.map( |c_str| f(c_str.as_ptr())) + } } #[inline] -fn check_for_null(v: &[u8], buf: *mut libc::c_char) { +fn check_for_null(v: &[u8], buf: *mut libc::c_char) -> bool { for i in range(0, v.len()) { unsafe { let p = buf.offset(i as int); - assert!(*p != 0); + if *p == 0 { + return true + } } } + false } /// External iterator for a CString's bytes. diff --git a/src/libstd/path/posix.rs b/src/libstd/path/posix.rs index c654d3a668a7c..39d3f64650b1b 100644 --- a/src/libstd/path/posix.rs +++ b/src/libstd/path/posix.rs @@ -100,6 +100,12 @@ impl ToCStr for Path { unsafe { self.to_c_str_unchecked() } } + #[inline] + fn to_c_str_opt(&self) -> Option { + // The Path impl guarantees no internal NUL + Some( unsafe { self.to_c_str_unchecked() } ) + } + #[inline] unsafe fn to_c_str_unchecked(&self) -> CString { self.as_vec().to_c_str_unchecked() @@ -112,6 +118,11 @@ impl<'a> ToCStr for &'a Path { (*self).to_c_str() } + #[inline] + fn to_c_str_opt(&self) -> Option { + (*self).to_c_str_opt() + } + #[inline] unsafe fn to_c_str_unchecked(&self) -> CString { (*self).to_c_str_unchecked() diff --git a/src/libstd/path/windows.rs b/src/libstd/path/windows.rs index e68c8bdb07d20..cc209f5dd673d 100644 --- a/src/libstd/path/windows.rs +++ b/src/libstd/path/windows.rs @@ -124,6 +124,12 @@ impl ToCStr for Path { unsafe { self.to_c_str_unchecked() } } + #[inline] + fn to_c_str_opt(&self) -> Option { + // The Path impl guarantees no internal NUL + Some(unsafe { self.to_c_str_unchecked() }) + } + #[inline] unsafe fn to_c_str_unchecked(&self) -> CString { self.as_vec().to_c_str_unchecked() @@ -136,6 +142,11 @@ impl<'a> ToCStr for &'a Path { (*self).to_c_str() } + #[inline] + fn to_c_str_opt(&self) -> Option { + (*self).to_c_str_opt() + } + #[inline] unsafe fn to_c_str_unchecked(&self) -> CString { (*self).to_c_str_unchecked()