I'm having an issue regarding keeping a heap-allocated slice in a struct with a PhantomPinned field. My goal is to work with some sensitive bytes (e.g. representing a private key), and ensure that they are not copied anywhere. Existing crates for this kind of thing secrecy, zeroizing, don't pin the data, so if it's stack-allocated it gets copied around, and if it's heap-allocated there's still no semantic guarantees that it won't move.
My first approach was simply using a Pin<Box<[u8]>>, but since [u8] is Unpin, that doesn't help.
So I created the following, with the goal of using Unmovable<[u8]> to keep my data in:
However, I'm now stuck on how to create a new Unmovable<[u8]> with the right length, so that I can fill the private data into it. I can use Box::new_uninit_slice (or new_zeroed_slice), to get a heap-allocated slice of the right size, but then I can't put that slice into my WithPhantomPin struct.
As I understood it, all types in Rust are by default freely-movable, which is very much not what I want here, and using Pin is a way to stop that from happening.
If I use a Box<[u8]>, the semantics say the [u8] can be moved in memory, no?
A raw pointer probably wouldn't prevent it either. The optimizer in LLVM understands raw pointers and if it can see the full range in which the raw pointer is used, it could optimize based on that.
For example, if you are writing asynchronous code and pin a future somewhere, and then proceed to do stuff with it that doesn't involve moving the future, then the compiler is absolutely allowed to introduce optimizations that move the future around. The only requirement is that the behavior is indistinguishable from the version where it is not moved.
What Pin actually does is use module privacy to prevent users that use only safe code from explicitly inserting a move or swap operation. It does not do anything to prevent the optimizer from inserting moves.
I think if you really want to have a wrapper type to prevent the data from getting copied to somewhere else, to start with you probably want to avoid those public inner() and inner_mut() methods, and only have methods that operate against the data in place.
Well, I need to be able to fill the buffer with stuff, so I'm going to need to expose a &mut [u8] somewhere. Like others have said though, Pin isn't getting me anything here, so I'll just use Box instead.
It would be good to be able to guarantee that the data is not going to be moved around on the heap through compiler optimisations at any point though, but I guess that's just not possible when using Rust.