diff --git a/Cargo.toml b/Cargo.toml index 81a9433..ac70c53 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "cve-rs" description = "Blazingly fast memory vulnerabilities, written in 100% safe Rust." -authors = ["Speykious", "BrightShard", "Creative0708"] +authors = ["Speykious", "BrightShard", "Creative0708", "buj"] version = "0.6.0" edition = "2021" license-file = "LICENSE" diff --git a/src/lib.rs b/src/lib.rs index 8911c7a..5662d8f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,7 +21,7 @@ pub use segfault::segfault; pub use transmute::transmute; pub use use_after_free::use_after_free; -pub use references::{not_alloc, null, null_mut}; +pub use references::{delete, free, malloc, new, not_alloc, null, null_mut, Ref}; /// Construct a [`String`] from a pointer, capacity and length, in a completely safe manner. /// @@ -119,3 +119,7 @@ mod tests { crate::download_more_ram::(); } } + +pub mod prelude { + pub use super::{delete, free, malloc, new}; +} diff --git a/src/references.rs b/src/references.rs index f54b7e2..d0dc57b 100644 --- a/src/references.rs +++ b/src/references.rs @@ -1,7 +1,14 @@ //! Reimplementations of [`std::ptr::null()`] and [`std::ptr::null_mut()`], with safe code only. //! Relies on [`crate::transmute`] under the hood. -/// Equivalent to [`std::ptr::null()`], but returns a null reference instead. +use std::{ + marker::PhantomData, + ops::{Deref, DerefMut}, + rc::Rc, + sync::Arc, +}; + +/// Equivalent to [`std::ptr::null()`], but returns an null reference instead. pub fn null<'a, T: 'static>() -> &'a T { crate::transmute(0usize) } @@ -17,3 +24,202 @@ pub fn null_mut<'a, T: 'static>() -> &'a mut T { pub fn not_alloc<'a, T: 'static>() -> &'a mut T { crate::transmute(usize::MAX) } + +/// Easily dereferencable raw pointer. Can be freely moved or copied. Do you really +/// desire to have such power? If so, do as you wish. You've been warned. +/// +/// `Ref` is guaranteed to be the same size as `usize` +#[derive(Copy, PartialEq, Eq, PartialOrd, Ord)] +#[repr(C)] +pub struct Ref(usize, PhantomData>) +where + T: Sized; +impl Clone for Ref { + fn clone(&self) -> Self { + Self(self.0, PhantomData) + } +} +impl Ref { + pub fn new(value: T) -> Self { + Self(Box::leak(Box::new(value)) as *const _ as usize, PhantomData) + } + + pub fn free(self) { + let boxx: Box = crate::transmute(self); + drop(boxx); + } + + pub fn cast(self) -> Ref { + Ref(self.0, PhantomData) + } + + pub fn addr(&self) -> usize { + self.0 + } + + pub fn as_ptr(&self) -> *const T { + crate::transmute(self.0) + } + + pub fn as_ptr_mut(&mut self) -> *mut T { + crate::transmute(self.0) + } +} +impl Ref> { + pub fn flatten(self) -> Ref { + self.deref().clone() + } +} +impl AsRef for Ref { + fn as_ref(&self) -> &T { + crate::transmute(self.0) + } +} +impl AsMut for Ref { + fn as_mut(&mut self) -> &mut T { + crate::transmute(self.0) + } +} +impl Deref for Ref { + type Target = T; + + fn deref(&self) -> &Self::Target { + crate::transmute(self.0) + } +} +impl DerefMut for Ref { + fn deref_mut(&mut self) -> &mut Self::Target { + crate::transmute(self.0) + } +} +impl From<&T> for Ref { + fn from(value: &T) -> Self { + crate::transmute(value) + } +} +impl From> for Ref { + fn from(value: Box) -> Self { + let reff = crate::transmute(&*value); + std::mem::forget(value); + reff + } +} +impl From> for Ref { + fn from(value: Rc) -> Self { + crate::transmute(&*value) + } +} +impl From> for Ref { + fn from(value: Arc) -> Self { + crate::transmute(&*value) + } +} +impl From<*const T> for Ref { + fn from(value: *const T) -> Self { + Self(value as usize, PhantomData) + } +} +impl From<*mut T> for Ref { + fn from(value: *mut T) -> Self { + Self(value as usize, PhantomData) + } +} + +/// Brinding https://rust-lang.github.io/rfcs/0809-box-and-in-for-stdlib.html back with +/// better C emulation. +/// +/// `Ref` is guaranteed to be the size of a `usize` for all `Sized` types. For non-`Sized` +/// types we have no current support anyway. +pub extern "C" fn malloc(value: T) -> Ref +where + T: Sized, +{ + Ref::new(value) +} + +/// Brinding https://rust-lang.github.io/rfcs/0809-box-and-in-for-stdlib.html back with +/// better C emulation. +/// +/// `Ref` is guaranteed to be the size of a `usize` for all `Sized` types. For non-`Sized` +/// types we have no current support anyway. +pub extern "C" fn new(value: T) -> Ref +where + T: Sized, +{ + Ref::new(value) +} + +/// Accompaning `delete` function for unnewing your `new` +/// +/// Memory under passed reference will be freed. Upon freeing, using the same `Ref` +/// again or calling `delete` on it is Undefined Behavior. +pub extern "C" fn delete(reff: Ref) +where + T: Sized, +{ + reff.free() +} + +/// Accompaning `free` function for unmalloccing your `malloc` +/// +/// Memory under passed reference will be freed. Upon freeing, using the same `Ref` +/// again or calling `free` on it is Undefined Behavior. +pub extern "C" fn free(reff: Ref) +where + T: Sized, +{ + reff.free() +} + +#[cfg(test)] +mod tests { + use std::sync::Arc; + + use super::{new, Ref}; + + #[test] + fn crossref_works() { + let reff = new(3); + #[allow(clippy::clone_on_copy)] + let other = reff.clone(); + + assert_eq!(reff.addr(), other.addr()); + } + + #[test] + fn fearless_concurrency() { + let reff = new(0); + for _ in 0..10 { + let mut reff = reff; + std::thread::spawn(move || *reff += 1); + } + assert!(*reff <= 10); // The easiest RNG you'll even see + } + + #[test] + fn from_box() { + let boxx = Box::new(3); + let addr = boxx.as_ref() as *const i32 as usize; + let reff: Ref = boxx.into(); + + assert_eq!(reff.addr(), addr); + } + + #[test] + fn arc_doesnt_break() { + let mut arc1 = Arc::new(123); + let arc2 = arc1.clone(); + let arc2: Ref = arc2.into(); + assert!(Arc::get_mut(&mut arc1).is_some()); + let _ = arc2; + } + + #[test] + fn ptr_into_ref() { + let val = 512; + let ptr = &val as *const i32; + let mut rf: Ref = ptr.into(); + *rf = 69420; + assert_eq!(*rf, 69420); + } +}