From b7b4283a770593486c1daaffe33ddf355fe81877 Mon Sep 17 00:00:00 2001 From: Nils Heydecker <48135649+Nilstrieb@users.noreply.github.com> Date: Wed, 20 Apr 2022 10:33:36 +0200 Subject: [PATCH] crimes --- .gitignore | 2 + Cargo.lock | 16 ++++++++ Cargo.toml | 9 +++++ src/main.rs | 113 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 140 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/main.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3a8cabc --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +.idea diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..d2e4431 --- /dev/null +++ b/Cargo.lock @@ -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" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..74ca879 --- /dev/null +++ b/Cargo.toml @@ -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" diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..fc21d38 --- /dev/null +++ b/src/main.rs @@ -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::().write(prov_a); + // write the b pointer to the second slot + ptr.cast::().add(1).cast::().write(prov_b); + + // and read out the center + let center = ptr.cast::().add(4).cast::().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::() + .add(4) + .cast::() + .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::().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::().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::>().add(4), + a_buf.as_mut_ptr().cast::>(), + 4, + ); + + // repeat the same thing for the b provenance + std::ptr::copy_nonoverlapping( + ptr.cast::>().add(8), + b_buf.as_mut_ptr().cast::>().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::().read(); + let b = b_buf.as_ptr().cast::().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); + } +}