since reflection is not something triviul too understand, you might expect the example showing problems with it to be complex to.
thankfully, it can be demonstrated with some widely used Rust types, like Box<i32>
- which means you don't need too much naledge to understand how things work.
let us suppose we have some way of accessing all fields using reflection, and we use that hypothetical API to write a function like this:
/// Gets a mutable reference to the first field of a struct using reflection
fn get_first_field_mut<T,F>(t:&mut T)->&mut F{
// Implemented using reflection
}
at a 1st glance, it does not seem all that scary. it doesn’t do much, besides just getting a mutable reference to a (potentially) private field.
in other languages, accesing private fields is considered a necessary evil. sure, if you get to creative with it, your coworkers might want to make you past tense. still, as long as you have a very good reason to use reflection like this, and use it sparingly, everything is going to be fine.
in rust, this is something that simply can’t be done - allowing reflection to access private fields .
using this seemingly inasent function, we write something like this:
// A `Box<i32>` points to a heap-allocated integer
let boxed = Box::new(1_i32);
// Walk through internal types of `Box`, to find its internal pointer
let unique = get_first_field_mut(&mut boxed);
let non_null = get_first_field_mut(unique);
let raw_ptr:&mut *const i32 = get_first_field_mut(non_null);
// After getting the internal pointer of `Box<i32>`, we replace it with an invalid one.
*raw_ptr = 0xDEAD_BEEF_usize as *const i32;
// The box now points to an invalid memory location, so trying to read its value is UB
// and may cause our program to crash
let val:i32 = *boxed;
he Box
type allows us to store data on the heap. inside a Box, we can find a pointer to this data.
a Rust Box is made up from several layers of types(*T
, NonNull<T>
, Unique<T>
, Box<T>
), each telling us more about the pointer.
here, we use reflection to go through all of those types, and replace that pointer (eg. 0x7dff_3340
) with a garbidge value (0xDEAD_BEEF
).
now, the box "thinks" it's value is at some unrelated adres. when we try to get that value back, we will cause undefined behaviur(UB).
depending on what exactly is present at that memory location, our program might crash, or read some unrelated peece of data.
this effectively breaks memory safety, so it must be, by definition, impossible in safe Rust.
Comments
Displaying 0 of 0 comments ( View all | Add Comment )