diff --git a/_posts/2019-02-14-insane-allocators.md b/_posts/2019-02-14-insane-allocators.md new file mode 100644 index 0000000..daf5d7c --- /dev/null +++ b/_posts/2019-02-14-insane-allocators.md @@ -0,0 +1,71 @@ +--- +layout: post +title: "Insane Allocators: SEGFAULTs in safe Rust" +description: "\"Trusting trust\" with allocators." +category: rust, memory +tags: [] +--- + +Having recently spent a lot of time studying the weird ways that +[Rust uses memory](/2019/02/understanding-allocations-in-rust.html), +I like to think I finally understand the rules well enough to +break them. Specifically - what are the assumptions that underpin +Rust's memory model? It wasn't a question particularly relevant +to understanding how Rust allocates memory, but is certainly worth +discussing as an addendum. Let's finish off this series on Rust and +memory by breaking the most important rules Rust has! + +Rust's whole shtick is that it's "memory safe." In practice, +this (should) mean that there's no undefined behavior in safe Rust, +because the compiler/borrow checker makes sure you can't get yourself +into a situation where you misuse or corrupt memory. But is it possible +for Rust programs, *written without using `unsafe`*, to encounter a +[segfault](https://en.wikipedia.org/wiki/Segmentation_fault)? + +Of course it is! Using an unmodified compiler, I can build a simple +"Hello, world!" application that dies due to memory corruption: + + + +# Wait, wat? + +There's obviously something nefarious going on. I mean, why would +anyone use `sudo` to run the `rustc` compiler? + +And for that matter, why does Rust 1.31.0 behave differently +from Rust 1.32.0? + +To pull off this chicanery, I'm making use of a special environment +variable in Linux called [`LD_PRELOAD`](https://blog.fpmurphy.com/2012/09/all-about-ld_preload.html). +I won't go into detail the way [Matt Godbolt does](https://www.youtube.com/watch?v=dOfucXtyEsU), +but the important bit is this: I can insert my own code in place of +functions typically implemented by the [C standard library](https://www.gnu.org/software/libc/). + +Second, there's a very special implementation of [`malloc`](https://linux.die.net/man/3/malloc) +that is being picked up by `LD_PRELOAD`: + +```rust +use std::ffi::c_void; +use std::ptr::null_mut; + +// Start off with an empty pointer +static mut ALLOC: *mut c_void = null_mut(); + +#[no_mangle] +pub extern "C" fn malloc(size: usize) -> *mut c_void { + unsafe { + // If we've never allocated anything, ask the operating system + // for some memory... + if ALLOC == null_mut() { + ALLOC = libc::malloc(size) + } + // ...and then give that same section of memory to everyone, + // corrupting the location. + return ALLOC; + } +} +``` + +Now, there are two questions yet to answer: +1. Why was `sudo` used to compile? +2. Why did Rust 1.31 work when 1.32 didn't?