This commit is contained in:
nora 2022-04-20 10:33:36 +02:00
commit b7b4283a77
4 changed files with 140 additions and 0 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
/target
.idea

16
Cargo.lock generated Normal file
View file

@ -0,0 +1,16 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "ohno"
version = "0.1.0"
dependencies = [
"sptr",
]
[[package]]
name = "sptr"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e0d4365121b91e8522958da77ce29c004333daeaf0711225c4727c8c5f49941"

9
Cargo.toml Normal file
View file

@ -0,0 +1,9 @@
[package]
name = "ohno"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
sptr = "0.3.1"

113
src/main.rs Normal file
View file

@ -0,0 +1,113 @@
// we are law abiding citizens using only the best features of the strict provenance world
use sptr::Strict;
// used to store provenance
use std::mem::MaybeUninit;
// writing a cfged version for 32 bit is trivial, we don't care
#[cfg(not(target_pointer_width = "64"))]
compile_error!("not supported");
// a pointer sized buffer used to store a single pointer
type Buf = MaybeUninit<[usize; 1]>;
// a double pointer sized buffer used to store two pointers and rip out the center
type DBuf = MaybeUninit<[usize; 2]>;
/// just a pointer, doesn't matter which one
type Ptr = *const u8;
/// combines two provenances into a single pointer sized value
unsafe fn combine(prov_a: Ptr, prov_b: Ptr) -> Buf {
// a buffer where we write in both pointers and then read out of from center, 1/2 of each provenance
let mut double_buf = DBuf::zeroed();
let ptr = double_buf.as_mut_ptr();
// write the a pointer to the first slot
ptr.cast::<Ptr>().write(prov_a);
// write the b pointer to the second slot
ptr.cast::<Buf>().add(1).cast::<Ptr>().write(prov_b);
// and read out the center
let center = ptr.cast::<u8>().add(4).cast::<Buf>().read();
center
}
/// extracts the two provenances from [`combine`]
unsafe fn extract(buf: Buf) -> (Ptr, Ptr) {
let mut double_buf = DBuf::zeroed();
// write the the pointer sized value into the center of the double buffer
// splitting the provenances between the first and second slow
double_buf
.as_mut_ptr()
.cast::<u8>()
.add(4)
.cast::<Buf>()
.write(buf);
// a copy of the first half of the dbuf, where the second half of it contains a provenance
let mut a_buf: Buf = double_buf.as_ptr().cast::<Buf>().read();
// a copy of the second half of the dbuf, where the first half of it contains b provenance
let mut b_buf: Buf = double_buf.as_ptr().cast::<Buf>().add(1).read();
// the pointer to the dbuf
let ptr = double_buf.as_ptr();
// copy the 4 a provenance bytes from the dbuf into the empty space in the a_buf
// this way a_buf now contains 8 a provenance bytes
std::ptr::copy_nonoverlapping(
ptr.cast::<MaybeUninit<u8>>().add(4),
a_buf.as_mut_ptr().cast::<MaybeUninit<u8>>(),
4,
);
// repeat the same thing for the b provenance
std::ptr::copy_nonoverlapping(
ptr.cast::<MaybeUninit<u8>>().add(8),
b_buf.as_mut_ptr().cast::<MaybeUninit<u8>>().add(4),
4,
);
// both buffers are now filled with fancy provenance bytes, read the pointers out and return them
let a = a_buf.as_ptr().cast::<Ptr>().read();
let b = b_buf.as_ptr().cast::<Ptr>().read();
(a, b)
}
fn main() {
unsafe {
// two innocent looking variants
let a = 5u8;
let b = 3u8;
// two innocent looking pointers
let a_ptr = &a as Ptr;
let b_ptr = &b as Ptr;
// extract the addresses for later use. in the real xorlist, these would be xored
let a_addr = Strict::addr(a_ptr);
let b_addr = Strict::addr(b_ptr);
// if we were implementing an actual xorlist, we would be setting the addresses of
// the pointers to our xored address so that the combined buffer stores the full address
// and we could get it out through more complex magic. this is besides the point of this
// demonstration, it is only concerned with combining provenances
// combine the provenances
let cursed = combine(a_ptr, b_ptr);
// do crimes here
// and get them out again
let (a_prov, b_prov) = extract(cursed);
// make them pointers again!
let new_a = Strict::with_addr(a_prov, a_addr);
let new_b = Strict::with_addr(b_prov, b_addr);
// it works now, right? :ferrisClueless:
assert_eq!(*new_a, 5);
assert_eq!(*new_b, 3);
}
}