diff --git a/src/c_vec.rs b/src/c_vec.rs new file mode 100644 index 0000000000000000000000000000000000000000..e9036fe5a2578792e0088d5d9b1805f79be91395 --- /dev/null +++ b/src/c_vec.rs @@ -0,0 +1,175 @@ +use core::iter::IntoIterator; +use core::ops::{Deref, DerefMut}; +use core::ptr::{self, NonNull}; +use core::{cmp, mem, slice}; +use crate::platform::types::*; +use crate::platform; + +/// Error that occurs when an allocation fails +#[derive(Debug, Default, Hash, PartialEq, Eq, Clone, Copy)] +pub struct AllocError; + +/// A normal vector allocated in Rust needs to be dropped from Rust +/// too, in order to avoid UB. This CVec is an abstraction that works +/// using only C allocations functions and can therefore be dropped +/// from C. Just like the Rust Vec, this does bounds checks to assure +/// you never reach isize::MAX. Unless you need to drop something from +/// C, prefer Rust's builtin Vec. +pub struct CVec<T> { + ptr: NonNull<T>, + len: usize, + cap: usize +} +impl<T> CVec<T> { + pub fn new() -> Self { + Self { + ptr: NonNull::dangling(), + len: 0, + cap: 0 + } + } + fn check_bounds(i: usize) -> Result<usize, AllocError> { + if i > core::isize::MAX as usize { + Err(AllocError) + } else { + Ok(i) + } + } + fn check_mul(x: usize, y: usize) -> Result<usize, AllocError> { + x.checked_mul(y).ok_or(AllocError) + .and_then(Self::check_bounds) + } + pub fn with_capacity(cap: usize) -> Result<Self, AllocError> { + if cap == 0 { + return Ok(Self::new()); + } + let size = Self::check_mul(cap, mem::size_of::<T>())?; + let ptr = NonNull::new(unsafe { platform::alloc(size) as *mut T }).ok_or(AllocError)?; + Ok(Self { + ptr, + len: 0, + cap + }) + } + unsafe fn resize(&mut self, cap: usize) -> Result<(), AllocError> { + let size = Self::check_mul(cap, mem::size_of::<T>())?; + let ptr = NonNull::new(platform::realloc(self.ptr.as_ptr() as *mut c_void, size) as *mut T).ok_or(AllocError)?; + self.ptr = ptr; + self.cap = cap; + Ok(()) + } + unsafe fn drop_range(&mut self, start: usize, end: usize) { + let mut start = self.ptr.as_ptr().add(start); + let end = self.ptr.as_ptr().add(end); + while start < end { + ptr::drop_in_place(start); + start = start.add(1); + } + } + pub fn reserve(&mut self, required: usize) -> Result<(), AllocError> { + let reserved_len = self.len.checked_add(required) + .ok_or(AllocError) + .and_then(Self::check_bounds)?; + let new_cap = cmp::min(reserved_len.next_power_of_two(), core::isize::MAX as usize); + if new_cap > self.cap { + unsafe { + self.resize(new_cap)?; + } + } + Ok(()) + } + pub fn push(&mut self, elem: T) -> Result<(), AllocError> { + unsafe { + self.reserve(1)?; + ptr::write(self.ptr.as_ptr().add(self.len), elem); + } + self.len += 1; // no need to bounds check, as new len <= cap + Ok(()) + } + pub fn extend_from_slice(&mut self, elems: &[T]) -> Result<(), AllocError> + where T: Copy + { + unsafe { + self.reserve(elems.len())?; + ptr::copy_nonoverlapping(elems.as_ptr(), self.ptr.as_ptr().add(self.len), elems.len()); + } + self.len += elems.len(); // no need to bounds check, as new len <= cap + Ok(()) + } + pub fn append(&mut self, other: &mut Self) -> Result<(), AllocError> { + unsafe { + self.reserve(other.len())?; + ptr::copy_nonoverlapping(other.as_ptr(), self.ptr.as_ptr().add(self.len), other.len()); + } + self.len += other.len(); // no need to bounds check, as new len <= cap + Ok(()) + } + pub fn truncate(&mut self, len: usize) { + if len < self.len { + unsafe { + self.drop_range(len, self.len); + } + self.len = len; + } + } + pub fn shrink_to_fit(&mut self) -> Result<(), AllocError> { + if self.len < self.cap { + unsafe { + self.resize(self.len)?; + } + } + Ok(()) + } + pub fn capacity(&self) -> usize { + self.cap + } + pub fn as_ptr(&self) -> *const T { + self.ptr.as_ptr() + } + pub fn as_mut_ptr(&mut self) -> *mut T { + self.ptr.as_ptr() + } + /// Leaks the inner data. This is safe to drop from C! + pub fn leak(mut self) -> *mut T { + let ptr = self.as_mut_ptr(); + mem::forget(self); + ptr + } +} +impl<T> Deref for CVec<T> { + type Target = [T]; + + fn deref(&self) -> &Self::Target { + unsafe { + slice::from_raw_parts(self.ptr.as_ptr(), self.len) + } + } +} +impl<T> DerefMut for CVec<T> { + fn deref_mut(&mut self) -> &mut Self::Target { + unsafe { + slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len) + } + } +} +impl<T> Drop for CVec<T> { + fn drop(&mut self) { + unsafe { + self.drop_range(0, self.len); + } + } +} +impl<'a, T> IntoIterator for &'a CVec<T> { + type Item = <&'a [T] as IntoIterator>::Item; + type IntoIter = <&'a [T] as IntoIterator>::IntoIter; + fn into_iter(self) -> Self::IntoIter { + <&[T]>::into_iter(&*self) + } +} +impl<'a, T> IntoIterator for &'a mut CVec<T> { + type Item = <&'a mut [T] as IntoIterator>::Item; + type IntoIter = <&'a mut [T] as IntoIterator>::IntoIter; + fn into_iter(self) -> Self::IntoIter { + <&mut [T]>::into_iter(&mut *self) + } +} diff --git a/src/header/dirent/mod.rs b/src/header/dirent/mod.rs index 8696b9ad01669fed2e3a1c39ad3b66f610747d4e..e0f9eb26b091381742517bdcf8c6b2012379e712 100644 --- a/src/header/dirent/mod.rs +++ b/src/header/dirent/mod.rs @@ -4,12 +4,13 @@ use alloc::boxed::Box; use core::{mem, ptr}; use c_str::CStr; +use c_vec::CVec; use fs::File; use header::{errno, fcntl, stdlib, string}; use io::{Seek, SeekFrom}; -use platform; use platform::types::*; use platform::{Pal, Sys}; +use platform; const DIR_BUF_SIZE: usize = mem::size_of::<dirent>() * 3; @@ -134,15 +135,16 @@ pub unsafe extern "C" fn scandir( return -1; } - let old_errno = platform::errno; + let mut vec = match CVec::with_capacity(4) { + Ok(vec) => vec, + Err(err) => return -1 + }; - let mut len: isize = 0; - let mut cap: isize = 4; - *namelist = platform::alloc(cap as usize * mem::size_of::<*mut dirent>()) as *mut *mut dirent; + let old_errno = platform::errno; + platform::errno = 0; loop { - platform::errno = 0; - let entry = readdir(dir); + let entry: *mut dirent = readdir(dir); if entry.is_null() { break; } @@ -153,30 +155,31 @@ pub unsafe extern "C" fn scandir( } } - if len >= cap { - cap *= 2; - *namelist = platform::realloc( - *namelist as *mut c_void, - cap as usize * mem::size_of::<*mut dirent>(), - ) as *mut *mut dirent; - } - let copy = platform::alloc(mem::size_of::<dirent>()) as *mut dirent; - *copy = (*entry).clone(); - *(*namelist).offset(len) = copy; - len += 1; + if copy.is_null() { + break; + } + ptr::write(copy, (*entry).clone()); + if let Err(_) = vec.push(copy) { + break; + } } closedir(dir); + let len = vec.len(); + if let Err(_) = vec.shrink_to_fit() { + return -1; + } + if platform::errno != 0 { - while len > 0 { - len -= 1; - platform::free(*(*namelist).offset(len) as *mut c_void); + for ptr in &mut vec { + platform::free(*ptr as *mut c_void); } - platform::free(*namelist as *mut c_void); -1 } else { + *namelist = vec.leak(); + platform::errno = old_errno; stdlib::qsort( *namelist as *mut c_void, @@ -184,6 +187,7 @@ pub unsafe extern "C" fn scandir( mem::size_of::<*mut dirent>(), mem::transmute(compare), ); + len as c_int } } diff --git a/src/header/stdio/scanf.rs b/src/header/stdio/scanf.rs index 6eb85b3481077375115a48ed1c2233441cc7bbd0..1d3d5e2106782547bac05f3b8001835ae75eee14 100644 --- a/src/header/stdio/scanf.rs +++ b/src/header/stdio/scanf.rs @@ -404,8 +404,7 @@ unsafe fn inner_scanf<R: Read>( let mut ptr: Option<*mut c_char> = if ignore { None } else { Some(ap.arg()) }; // While we haven't used up all the width, and it matches - while width.map(|w| w > 0).unwrap_or(true) && !invert == matches.contains(&byte) - { + while width.map(|w| w > 0).unwrap_or(true) && !invert == matches.contains(&byte) { if let Some(ref mut ptr) = ptr { **ptr = byte as c_char; *ptr = ptr.offset(1); diff --git a/src/lib.rs b/src/lib.rs index 2489dc879b568ba647b4813a6dc93c98ce2a6e2c..3ea4a1f0613d04d5567e28ee4d4b6073e3c5f839 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -51,6 +51,7 @@ extern crate spin; #[macro_use] mod macros; pub mod c_str; +pub mod c_vec; pub mod cxa; pub mod db; pub mod fs; diff --git a/tests/stdio/scanf.c b/tests/stdio/scanf.c index c9ed3f438abfd1204e64ce90eab24d1648cb663a..f2fc626793b5d39ef884145daebafea4921bdd2d 100644 --- a/tests/stdio/scanf.c +++ b/tests/stdio/scanf.c @@ -54,10 +54,28 @@ int main(void) { char hostbuf[100]; char pathbuf[100]; + // don't push NUL, make sure scanf does that + memset(protobuf, 97, 16); + memset(slashbuf, 97, 4); + memset(hostbuf, 97, 100); + memset(pathbuf, 97, 100); + int ret = sscanf( "https://redox-os.org", "%15[^\n/:]:%3[/]%[^\n/?#]%[^\n]", &protobuf, &slashbuf, &hostbuf, &pathbuf ); + if (ret < 4) { + *pathbuf = 0; + } + if (ret < 3) { + *hostbuf = 0; + } + if (ret < 2) { + *slashbuf = 0; + } + if (ret < 1) { + *protobuf = 0; + } printf("%d \"%s\" \"%s\" \"%s\" \"%s\"\n", ret, &protobuf, &slashbuf, &hostbuf, &pathbuf); }