Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Code shared from the Rust Playground
// This PoC has two different exploit methods; you can compile one or
// the other.
// Since Rust Playground doesn't let you set arbitrary features or
// other --cfg options, here is a silly macro. Change `method1` to
// `method2` to try method 2.
macro_rules! lol_cfg {
(method1 => { $($t:tt)* }) => ($($t)*);
//^^^^^^^ Change this!
($x:ident => { $($t:tt)* }) => ();
}
// The only non-standard-library imports are a convenience function
// and macro from `futures`.
extern crate futures;
use futures::task::noop_waker;
use futures::pending;
// Standard library imports
use std::cell::Cell;
use std::future::Future;
use std::mem;
use std::pin::Pin;
#[allow(unused_imports)]
use std::ops::{Deref, DerefMut};
use std::task::Context;
// Trait implemented by 'before' and 'after' objects, so that clone()
// or deref_mut() on a mutable or immutable (respectively) reference
// to trait object can switch from one to the other.
trait Dubious<'a> {
// function implemented for 'before' object:
fn get_evil_reference(&self) -> &'a mut (dyn Dubious<'a> + 'a);
// function implemented for 'after' object:
fn as_future(self: Pin<&mut Self>) -> Pin<&mut dyn Future<Output=()>>;
}
lol_cfg!(method1 => {
// Evil DerefMut implementation (method 1).
impl<'a> DerefMut for &'a (dyn Dubious<'a> + 'a) {
fn deref_mut(&mut self) -> &mut (dyn Dubious<'a> + 'a) {
self.get_evil_reference()
}
}
});
lol_cfg!(method2 => {
// Evil Clone implementation (method 2).
impl<'a> Clone for &'a mut (dyn Dubious<'a> + 'a) {
fn clone(&self) -> Self {
self.get_evil_reference()
}
}
});
// The 'before' object that will be passed into clone()/deref_mut().
// It contains a mutable reference which will be moved out and
// returned.
// - For method 1, we implement the DerefMut trait (taking `&mut
// self`) on an immutable reference, so we get `&mut &Wrapper`.
// - For method 2, we implement the Clone trait (taking `&self`) on a
// mutable reference, so we get `&&mut Wrapper`.
// Either way, we need interior mutability to move from the field.
struct Wrapper<'a>(Cell<Option<&'a mut (dyn Dubious<'a> + 'a)>>);
impl<'a> Wrapper<'a> {
fn new(p: &'a mut (dyn Dubious<'a> + 'a)) -> Self {
Wrapper(Cell::new(Some(p)))
}
}
impl<'a> Dubious<'a> for Wrapper<'a> {
fn get_evil_reference(&self) -> &'a mut (dyn Dubious<'a> + 'a) {
self.0.replace(None).unwrap()
}
fn as_future(self: Pin<&mut Self>) -> Pin<&mut dyn Future<Output=()>> {
panic!()
}
}
// The 'after' object that will come out of clone(): a future from an
// `async` block. Actually, we can't name that type, so we just
// blanket implement for all Futures.
impl<'a, MyFuture: Future<Output=()>> Dubious<'a> for MyFuture {
fn get_evil_reference(&self) -> &'a mut (dyn Dubious<'a> + 'a) {
panic!()
}
fn as_future(self: Pin<&mut Self>) -> Pin<&mut dyn Future<Output=()>> {
self
}
}
// Helper that makes the demonstration of 'use-after-free' more
// consistent. The exploit lets us move an already-started future;
// using this enum we can fill the original location with known bytes
// so that it deterministically crashes.
enum UAF<MyFuture: Future<Output=()>> {
Future(MyFuture),
Dummy([u8; 256]),
}
pub fn go_inner<MyFuture: Future<Output=()>>(p_myfuture: MyFuture) {
let waker = noop_waker();
let mut cx = Context::from_waker(&waker);
let mut uaf: UAF<MyFuture> = UAF::Future(p_myfuture);
{
// Get a reference to the future within the enum.
let p_myfuture: &mut MyFuture = match &mut uaf {
UAF::Future(p_myfuture) => p_myfuture,
_ => panic!()
};
lol_cfg!(method1 => {{
// Method 1:
let wrapper = Wrapper::new(p_myfuture);
let p_wrapper: Pin<&Wrapper<'_>> = Pin::new(&wrapper);
// Convert to pinned immutable reference to trait object.
let mut p_wrapper_as_dubious: Pin<&dyn Dubious<'_>> =
p_wrapper.as_ref();
// Now call as_mut() on the Pin, which will call our
// custom deref_mut() impl on the immutable reference.
let p_myfuture_as_dubious: Pin<&mut dyn Dubious<'_>> =
p_wrapper_as_dubious.as_mut();
// `p_myfuture_as_dubious` points to the MyFuture. Downcast...
let p_myfuture_as_future: Pin<&mut dyn Future<Output=()>>
= p_myfuture_as_dubious.as_future();
// ...and poll.
let _ = p_myfuture_as_future.poll(&mut cx);
}});
lol_cfg!(method2 => {{
// Method 2:
let mut wrapper = Wrapper::new(p_myfuture);
let mut p_wrapper: Pin<&mut Wrapper<'_>> = Pin::new(&mut wrapper);
// Convert to pinned mutable reference to trait object.
let p_wrapper_as_dubious: Pin<&mut dyn Dubious<'_>>
= p_wrapper.as_mut();
// Now call clone() on the Pin, which will call our custom
// clone() impl on the mutable reference.
let p_myfuture_as_dubious: Pin<&mut dyn Dubious<'_>> =
p_wrapper_as_dubious.clone();
// (The following is identical to method 1; needed to copy+paste due to scoping:)
// `p_myfuture_as_dubious` points to the MyFuture. Downcast...
let p_myfuture_as_future: Pin<&mut dyn Future<Output=()>>
= p_myfuture_as_dubious.as_future();
// ...and poll.
let _ = p_myfuture_as_future.poll(&mut cx);
}});
}
{
// Move the MyFuture; fill the original location with 0x41 bytes.
let p_myfuture = match mem::replace(&mut uaf, UAF::Dummy([0x41; 256])) {
UAF::Future(p_myfuture) => p_myfuture,
_ => panic!()
};
// Trigger the crash. This is just a normal pin -> poll
// sequence, which is only dangerous because the async fn was
// already started before being moved.
let mut pin_box_mf: Pin<Box<MyFuture>> = Box::pin(p_myfuture);
let _ = pin_box_mf.as_mut().poll(&mut cx);
}
}
fn main() {
go_inner(async {
let integer = 42;
let string = "hello";
let p_integer = &integer;
let p_string = &string;
loop {
println!("hi");
println!(" integer={:x}", *p_integer);
println!(" string={}", *p_string);
// Yield to caller:
pending!();
}
})
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.