diff --git a/Cargo.lock b/Cargo.lock index 8675aaf..45e6ba1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,9 +2,9 @@ # It is not intended for manual editing. [[package]] name = "libc" -version = "0.2.71" +version = "0.2.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49" +checksum = "dea0c0405123bba743ee3f91f49b1c7cfb684eef0da0a50110f758ccf24cdff0" [[package]] name = "neco" diff --git a/rustfmt.toml b/rustfmt.toml deleted file mode 100644 index 218e203..0000000 --- a/rustfmt.toml +++ /dev/null @@ -1 +0,0 @@ -hard_tabs = true diff --git a/safe_libc/Cargo.lock b/safe_libc/Cargo.lock new file mode 100644 index 0000000..e659af2 --- /dev/null +++ b/safe_libc/Cargo.lock @@ -0,0 +1,14 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "libc" +version = "0.2.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dea0c0405123bba743ee3f91f49b1c7cfb684eef0da0a50110f758ccf24cdff0" + +[[package]] +name = "safe_libc" +version = "0.1.0" +dependencies = [ + "libc", +] diff --git a/safe_libc/Cargo.toml b/safe_libc/Cargo.toml index 34f8f79..f776d09 100644 --- a/safe_libc/Cargo.toml +++ b/safe_libc/Cargo.toml @@ -7,4 +7,4 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -libc = { version = "0.2.71", default-features = false } +libc = { version = "0.2.68", default-features = false } diff --git a/safe_libc/src/boxed.rs b/safe_libc/src/boxed.rs index a0bbfb4..fa3e45d 100644 --- a/safe_libc/src/boxed.rs +++ b/safe_libc/src/boxed.rs @@ -1,78 +1,150 @@ -use core::{marker, mem, ptr}; +use crate::util; + +use core::{fmt, mem, ptr}; use core::ops::{Deref, DerefMut}; -pub trait Dealloc { - unsafe fn dealloc(p: *mut T); +fn alloc(len: usize) -> *mut T { + if util::zst::(len) { + return ptr::null_mut(); + } + let size = len.checked_mul(mem::size_of::()).expect("allocation overflow"); + let align = mem::align_of::(); + util::must_succeed( + unsafe { + libc::memalign(align as libc::size_t, size as libc::size_t) + } + ).cast() } -#[repr(transparent)] -pub struct BoxLike> { - inner: ptr::NonNull, - _phantom: marker::PhantomData, - _dropper: marker::PhantomData, +#[inline] +const fn dangling() -> *mut T { + mem::align_of::() as *mut T } -impl> BoxLike { - #[inline] - pub unsafe fn from_raw_unchecked(p: *mut T) -> Self { - BoxLike { - inner: ptr::NonNull::new_unchecked(p), - _phantom: marker::PhantomData, - _dropper: marker::PhantomData, - } - } +#[inline] +fn slice_len(p: *const [T]) -> usize { + unsafe { mem::transmute::<_, [usize; 2]>(p)[1] } +} - #[inline] - pub unsafe fn from_raw(p: *mut T) -> Option { - if p.is_null() { - None - } else { - Some(Self::from_raw_unchecked(p)) - } - } +pub trait SafePtr { + fn safe_ptr(p: *mut Self) -> *mut Self; +} +impl SafePtr for T { #[inline] - pub fn into_raw(self) -> *mut T { - let p = self.inner.as_ptr(); - mem::forget(self); + fn safe_ptr(p: *mut T) -> *mut T { + if util::zst::(1) { + return dangling(); + } + + debug_assert!(!p.is_null(), "NULL ptr"); p } } -impl> Drop for BoxLike { +impl SafePtr for [T] { + #[inline] + fn safe_ptr(p: *mut [T]) -> *mut [T] { + let len = slice_len(p); + if util::zst::(len) { + return ptr::slice_from_raw_parts_mut(dangling(), len); + } + + debug_assert!(!p.is_null(), "NULL ptr"); + p + } +} + +pub struct CBox(*mut T); + +impl CBox { + #[inline] + pub unsafe fn from_raw_unchecked(p: *mut T) -> CBox { + CBox(p) + } + + #[inline] + pub fn into_raw(self) -> *mut T { + let p = self.0; + mem::forget(self); + p + } + + #[inline] + pub fn as_ptr(&self) -> *const T { + self.0 + } + + #[inline] + pub fn as_mut_ptr(&mut self) -> *mut T { + self.0 + } +} + +impl CBox { + #[inline] + pub fn new(value: T) -> CBox { + let p = alloc(1); + unsafe { + ptr::write(p, value); + CBox::from_raw_unchecked(p) + } + } + + #[inline] + pub unsafe fn from_raw(p: *mut T) -> CBox { + util::check_ptr(p, 1); + CBox(p) + } + + #[inline] + pub unsafe fn slice_from_raw_parts_unchecked(p: *mut T, len: usize) -> CBox<[T]> { + CBox(ptr::slice_from_raw_parts_mut(p, len)) + } + + #[inline] + pub unsafe fn slice_from_raw_parts(p: *mut T, len: usize) -> CBox<[T]> { + util::check_ptr(p, len); + CBox::slice_from_raw_parts_unchecked(p, len) + } +} + +impl Drop for CBox { #[inline] fn drop(&mut self) { - let p = self.inner.as_ptr(); unsafe { - ptr::drop_in_place(p); - D::dealloc(p); + ptr::drop_in_place(self.0); + libc::free(self.0.cast()); } } } -impl> Deref for BoxLike { +impl Deref for CBox { type Target = T; #[inline] fn deref(&self) -> &T { - unsafe { self.inner.as_ref() } + unsafe { &*T::safe_ptr(self.0) } } } -impl> DerefMut for BoxLike { +impl DerefMut for CBox { #[inline] fn deref_mut(&mut self) -> &mut T { - unsafe { self.inner.as_mut() } + unsafe { &mut *T::safe_ptr(self.0) } } } -pub struct DeallocFree; - -impl Dealloc for DeallocFree { +impl fmt::Debug for CBox { #[inline] - unsafe fn dealloc(p: *mut T) { - libc::free(p as _); + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&**self, f) } } -pub type CBox = BoxLike; +impl fmt::Display for CBox { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&**self, f) + } +} diff --git a/safe_libc/src/errno.rs b/safe_libc/src/errno.rs deleted file mode 100644 index fdb1246..0000000 --- a/safe_libc/src/errno.rs +++ /dev/null @@ -1,34 +0,0 @@ -use crate::string; - -use core::fmt; - -#[derive(Clone, Copy, Debug)] -#[repr(transparent)] -pub struct Errno(pub libc::c_int); - -extern "C" { - #[ffi_const] - pub fn __errno_location() -> *mut libc::c_int; -} - -#[inline] -pub fn errno() -> Errno { - unsafe { Errno(*__errno_location()) } -} - -impl fmt::Display for Errno { - #[inline] - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut buf = [0u8; 1024]; - let cstr = unsafe { - if libc::strerror_r(self.0, buf.as_mut_ptr().cast(), buf.len()) != 0 { - return Err(fmt::Error); - } - string::CStr::from_bytes_with_nul_unchecked(&buf) - }; - match cstr.to_str() { - Err(_) => Err(fmt::Error), - Ok(s) => f.write_str(s), - } - } -} diff --git a/safe_libc/src/lib.rs b/safe_libc/src/lib.rs index cbc4071..0ab0e89 100644 --- a/safe_libc/src/lib.rs +++ b/safe_libc/src/lib.rs @@ -1,20 +1,17 @@ #![no_std] -#![feature(extern_types)] -#![feature(slice_ptr_len)] -#![feature(ffi_const)] - -extern crate alloc; pub use libc::*; mod util; pub mod boxed; -pub mod errno; pub mod stdio; pub mod string; + extern "C" { + pub fn strndup(cs: *const libc::c_char, n: libc::size_t) -> *mut libc::c_char; + // pub static stdin: *mut libc::FILE; pub static stdout: *mut libc::FILE; pub static stderr: *mut libc::FILE; diff --git a/safe_libc/src/stdio.rs b/safe_libc/src/stdio.rs index 2942405..271f023 100644 --- a/safe_libc/src/stdio.rs +++ b/safe_libc/src/stdio.rs @@ -1,104 +1,47 @@ -use crate::{self as libc, errno, string}; +use crate as libc; +use crate::string; use core::fmt; -use core::ops::{Deref, DerefMut}; -pub struct Error { - pub errno: errno::Errno, -} - -impl From for fmt::Error { - fn from(_err: Error) -> fmt::Error { - fmt::Error - } -} - -pub type Result = core::result::Result; - -#[inline] -fn check_io(ok: bool) -> Result<()> { - if ok { - Ok(()) - } else { - Err(Error { - errno: errno::errno(), - }) - } -} - -pub struct BasicOStream(*mut libc::FILE); - -unsafe impl Sync for BasicOStream {} -unsafe impl Send for BasicOStream {} - -pub type Stdout = BasicOStream; -pub type Stderr = BasicOStream; - -#[inline] -pub fn stdout() -> Stdout { - BasicOStream(unsafe { libc::stdout }) +pub struct OStream { + file: *mut libc::FILE } #[inline] -pub fn stderr() -> Stderr { - BasicOStream(unsafe { libc::stderr }) +pub unsafe fn stdout() -> OStream { + OStream { file: libc::stdout } } -impl BasicOStream { - #[inline] - pub fn flush(&mut self) -> Result<()> { - check_io(unsafe { libc::fflush(self.0) } == 0) - } - - #[inline] - pub fn write(&mut self, b: &[u8]) -> Result<()> { - check_io(unsafe { libc::fwrite(b.as_ptr().cast(), 1, b.len(), self.0) } == b.len()) - } - - #[inline] - pub fn puts(&mut self, s: &string::CStr) -> Result<()> { - check_io(unsafe { libc::fputs(s.as_ptr(), self.0) } != libc::EOF) - } +#[inline] +pub unsafe fn stderr() -> OStream { + OStream { file: libc::stderr } } -impl fmt::Write for BasicOStream { - #[inline] - fn write_str(&mut self, s: &str) -> fmt::Result { - self.write(s.as_bytes())?; - Ok(()) - } -} - -pub struct OStream(BasicOStream); - impl OStream { #[inline] - pub unsafe fn from_raw(file: *mut libc::FILE) -> OStream { - OStream(BasicOStream(file)) - } -} - -impl Drop for OStream { - #[inline] - fn drop(&mut self) { + pub fn write(&mut self, b: &[u8]) { unsafe { - libc::fclose((self.0).0); + libc::fwrite( + b.as_ptr().cast(), + 1, + b.len(), + self.file, + ); + } + } + + #[inline] + pub fn puts(&mut self, s: &string::CStr) { + unsafe { + libc::fputs(s.as_ptr(), self.file); } } } -impl Deref for OStream { - type Target = BasicOStream; - +impl fmt::Write for OStream { #[inline] - fn deref(&self) -> &BasicOStream { - &self.0 - } -} - -impl DerefMut for OStream { - #[inline] - fn deref_mut(&mut self) -> &mut BasicOStream { - &mut self.0 + fn write_str(&mut self, s: &str) -> fmt::Result { + self.write(s.as_bytes()); + Ok(()) } } diff --git a/safe_libc/src/string.rs b/safe_libc/src/string.rs index 5483e08..8f973ad 100644 --- a/safe_libc/src/string.rs +++ b/safe_libc/src/string.rs @@ -1,31 +1,40 @@ -use crate::{self as libc, boxed::CBox, util}; +use crate as libc; +use crate::util; +use crate::boxed::CBox; -use core::{mem, slice}; +use core::slice; use core::ops::{Deref, DerefMut}; //pub struct FromBytesWithNulError {} -extern "C" { - type RawCStr; -} - -#[repr(transparent)] -pub struct CStr(RawCStr); +pub struct CStr { inner: libc::c_char } impl CStr { + #[inline] + pub unsafe fn from_ptr_unchecked<'a>(p: *const libc::c_char) -> &'a CStr { + &*p.cast() + } + #[inline] pub unsafe fn from_ptr<'a>(p: *const libc::c_char) -> &'a CStr { - &*(p as *const CStr) + util::check_ptr(p, 1); + CStr::from_ptr_unchecked(p) + } + + #[inline] + pub unsafe fn from_mut_ptr_unchecked<'a>(p: *mut libc::c_char) -> &'a mut CStr { + &mut *p.cast() } #[inline] pub unsafe fn from_mut_ptr<'a>(p: *mut libc::c_char) -> &'a mut CStr { - &mut *(p as *mut CStr) + util::check_ptr(p, 1); + CStr::from_mut_ptr_unchecked(p) } #[inline] pub unsafe fn from_bytes_with_nul_unchecked(bytes: &[u8]) -> &CStr { - CStr::from_ptr(bytes.as_ptr().cast()) + CStr::from_ptr_unchecked(bytes.as_ptr().cast()) } // TODO @@ -34,37 +43,33 @@ impl CStr { #[inline] pub fn len(&self) -> usize { - unsafe { libc::strlen(self.as_ptr()) } + unsafe { libc::strlen(self.as_ptr()) as usize } } #[inline] pub const fn as_ptr(&self) -> *const libc::c_char { - (self as *const CStr).cast() + &self.inner } #[inline] pub fn as_mut_ptr(&mut self) -> *mut libc::c_char { - (self as *mut CStr).cast() + &mut self.inner } #[inline] - pub fn to_bytes(&self) -> &[u8] { - unsafe { slice::from_raw_parts(self.as_ptr().cast(), self.len()) } + pub fn as_bytes(&self) -> &[u8] { + unsafe { slice::from_raw_parts( + self.as_ptr().cast(), + self.len(), + ) } } #[inline] - pub fn to_mut_bytes(&mut self) -> &mut [u8] { - unsafe { slice::from_raw_parts_mut(self.as_mut_ptr().cast(), self.len()) } - } - - #[inline] - pub fn to_str(&self) -> Result<&str, core::str::Utf8Error> { - core::str::from_utf8(self.to_bytes()) - } - - #[inline] - pub fn to_mut_str(&mut self) -> Result<&mut str, core::str::Utf8Error> { - core::str::from_utf8_mut(self.to_mut_bytes()) + pub fn as_mut_bytes(&mut self) -> &mut [u8] { + unsafe { slice::from_raw_parts_mut( + self.as_mut_ptr().cast(), + self.len(), + ) } } #[inline] @@ -75,28 +80,28 @@ impl CStr { #[macro_export] macro_rules! cstr { - ($s:expr) => { + ($s:expr) => ( unsafe { $crate::string::CStr::from_bytes_with_nul_unchecked(concat!($s, "\0").as_bytes()) } - }; + ) } -pub struct CString { - inner: CBox, -} +pub struct CString { inner: CBox } impl CString { #[inline] - pub unsafe fn from_raw(p: *mut libc::c_char) -> CString { - CString { - inner: CBox::from_raw_unchecked(p as *mut CStr) - } + pub unsafe fn from_raw_unchecked(p: *mut libc::c_char) -> CString { + CString { inner: CBox::from_raw_unchecked(p) } } #[inline] - pub fn into_raw(mut self) -> *mut libc::c_char { - let p = self.as_mut_ptr(); - mem::forget(self); - p + pub unsafe fn from_raw(p: *mut libc::c_char) -> CString { + util::check_ptr(p, 1); + CString::from_raw_unchecked(p) + } + + #[inline] + pub fn into_raw(self) -> *mut libc::c_char { + self.inner.into_raw() } } @@ -105,24 +110,26 @@ impl Deref for CString { #[inline] fn deref(&self) -> &CStr { - &*self.inner + unsafe { CStr::from_ptr(&*self.inner) } } } impl DerefMut for CString { #[inline] fn deref_mut(&mut self) -> &mut CStr { - &mut *self.inner + unsafe { CStr::from_mut_ptr(&mut *self.inner) } } } impl From<&[u8]> for CString { fn from(s: &[u8]) -> CString { unsafe { - CString::from_raw(util::must_succeed(libc::strndup( - s.as_ptr().cast(), - s.len(), - ))) + CString::from_raw_unchecked( + util::must_succeed(libc::strndup( + s.as_ptr().cast(), + s.len() as libc::size_t, + )) + ) } } } @@ -137,6 +144,8 @@ impl From<&str> for CString { impl From<&CStr> for CString { #[inline] fn from(s: &CStr) -> CString { - unsafe { CString::from_raw(util::must_succeed(libc::strdup(s.as_ptr()))) } + unsafe { + CString::from_raw_unchecked(util::must_succeed(libc::strdup(s.as_ptr()))) + } } } diff --git a/safe_libc/src/util.rs b/safe_libc/src/util.rs index d37954c..b8a4ed5 100644 --- a/safe_libc/src/util.rs +++ b/safe_libc/src/util.rs @@ -1,9 +1,18 @@ -use alloc::alloc; +use core::mem; + +#[inline] +pub fn zst(len: usize) -> bool { + mem::size_of::() == 0 || len == 0 +} + +#[inline] +pub fn check_ptr(p: *const T, len: usize) { + debug_assert!((p as usize) % mem::align_of::() == 0, "unaligned ptr"); + assert!(zst::(len) || !p.is_null(), "NULL ptr"); +} #[inline] pub fn must_succeed(p: *mut T) -> *mut T { - if p.is_null() { - alloc::handle_alloc_error(alloc::Layout::new::()) - } + assert!(!p.is_null(), "allocation failure"); p } diff --git a/src/main.rs b/src/main.rs index 4283396..c79f1da 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,53 +1,39 @@ #![no_main] #![no_std] -#![feature(alloc_error_handler)] -mod system_alloc; - -extern crate alloc; -use libc::cstr; use safe_libc as libc; +use libc::cstr; use core::fmt::Write; #[no_mangle] pub extern "C" fn main(_nargs: libc::c_int, _args: *const *const libc::c_char) -> libc::c_int { - let mut stdout = libc::stdio::stdout(); + let mut stdout = unsafe { libc::stdio::stdout() }; { let foo = cstr!("Foo!\n"); - let _ = stdout.puts(foo); + stdout.puts(foo); } { let x = libc::string::CString::from("foo"); let l = x.len(); let y = x.into_raw(); - let z = unsafe { alloc::boxed::Box::from_raw(core::ptr::slice_from_raw_parts_mut(y, l)) }; + let z = unsafe { + libc::boxed::CBox::slice_from_raw_parts(y, l) + }; let _ = writeln!(stdout, "Foo: {} {} {}", z[0], z[1], z[2]); } { - let x = libc::string::CString::from("bar"); - let _ = stdout.puts(&x); - } - - { - let b = alloc::boxed::Box::new(42); + let b = libc::boxed::CBox::new(42); let _ = writeln!(stdout, "Bar: {}", b); } - - { - let x = alloc::boxed::Box::new(()); - let _ = writeln!(stdout, "Box: {:?}", x); - } - 0 } -#[cfg(not(test))] #[panic_handler] fn panic(info: &core::panic::PanicInfo) -> ! { - let _ = writeln!(libc::stdio::stderr(), "Panic: {}", info); + let _ = writeln!(unsafe { libc::stdio::stderr() }, "Panic: {}", info); unsafe { libc::abort() } } diff --git a/src/system_alloc.rs b/src/system_alloc.rs deleted file mode 100644 index 7c0bac9..0000000 --- a/src/system_alloc.rs +++ /dev/null @@ -1,22 +0,0 @@ -use safe_libc as libc; - -struct System; - -unsafe impl alloc::alloc::GlobalAlloc for System { - unsafe fn alloc(&self, layout: alloc::alloc::Layout) -> *mut u8 { - libc::memalign(layout.align(), layout.size()).cast() - } - - unsafe fn dealloc(&self, ptr: *mut u8, _layout: alloc::alloc::Layout) { - libc::free(ptr.cast()); - } -} - -#[global_allocator] -static SYSTEM_ALLOC: System = System; - -#[cfg(not(test))] -#[alloc_error_handler] -fn alloc_error(_: core::alloc::Layout) -> ! { - panic!("allocation failure"); -}