Skip to content
Open

Refs #20

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"
Expand Down
6 changes: 5 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
///
Expand Down Expand Up @@ -119,3 +119,7 @@ mod tests {
crate::download_more_ram::<u64>();
}
}

pub mod prelude {
pub use super::{delete, free, malloc, new};
}
208 changes: 207 additions & 1 deletion src/references.rs
Original file line number Diff line number Diff line change
@@ -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)
}
Expand All @@ -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<T>` is guaranteed to be the same size as `usize`
#[derive(Copy, PartialEq, Eq, PartialOrd, Ord)]
#[repr(C)]
pub struct Ref<T>(usize, PhantomData<Mutex<T>>)
where
T: Sized;
impl<T> Clone for Ref<T> {
fn clone(&self) -> Self {
Self(self.0, PhantomData)
}
}
impl<T> Ref<T> {
pub fn new(value: T) -> Self {
Self(Box::leak(Box::new(value)) as *const _ as usize, PhantomData)
}

pub fn free(self) {
let boxx: Box<T> = crate::transmute(self);
drop(boxx);
}

pub fn cast<U>(self) -> Ref<U> {
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<T> Ref<Ref<T>> {
pub fn flatten(self) -> Ref<T> {
self.deref().clone()
}
}
impl<T> AsRef<T> for Ref<T> {
fn as_ref(&self) -> &T {
crate::transmute(self.0)
}
}
impl<T> AsMut<T> for Ref<T> {
fn as_mut(&mut self) -> &mut T {
crate::transmute(self.0)
}
}
impl<T> Deref for Ref<T> {
type Target = T;

fn deref(&self) -> &Self::Target {
crate::transmute(self.0)
}
}
impl<T> DerefMut for Ref<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
crate::transmute(self.0)
}
}
impl<T> From<&T> for Ref<T> {
fn from(value: &T) -> Self {
crate::transmute(value)
}
}
impl<T> From<Box<T>> for Ref<T> {
fn from(value: Box<T>) -> Self {
let reff = crate::transmute(&*value);
std::mem::forget(value);
reff
}
}
impl<T> From<Rc<T>> for Ref<T> {
fn from(value: Rc<T>) -> Self {
crate::transmute(&*value)
}
}
impl<T> From<Arc<T>> for Ref<T> {
fn from(value: Arc<T>) -> Self {
crate::transmute(&*value)
}
}
impl<T> From<*const T> for Ref<T> {
fn from(value: *const T) -> Self {
Self(value as usize, PhantomData)
}
}
impl<T> From<*mut T> for Ref<T> {
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<T>` 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<T>(value: T) -> Ref<T>
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<T>` 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<T>(value: T) -> Ref<T>
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<T>`
/// again or calling `delete` on it is Undefined Behavior.
pub extern "C" fn delete<T>(reff: Ref<T>)
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<T>`
/// again or calling `free` on it is Undefined Behavior.
pub extern "C" fn free<T>(reff: Ref<T>)
where
T: Sized,
{
reff.free()
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can remove the module-level fns now that they're methods

Copy link
Author

@5GameMaker 5GameMaker Feb 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those functions exist as to be inclusive towards the C/C++ developer community. Those can be removed, but it will make Rust less accessible for that group.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We ought to make them extern "C" in that case

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ye makes sense

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, those have names which cannot be exported

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmmm
maybe #[export(cve_rs_malloc)]/free?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No such attribute

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cargo mommy lmfao


#[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<i32> = 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<i32> = 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<i32> = ptr.into();
*rf = 69420;
assert_eq!(*rf, 69420);
}
}