[safety-dance] Remove some unsound usages of unsafe#19
[safety-dance] Remove some unsound usages of unsafe#19danielhenrymantilla wants to merge 3 commits intoruuda:masterfrom
Conversation
`mem::uninitialized` is unsound; and if refresh reader code panicked between the enclosing `mem::replace`s, the code would be dropping and thus reading uninitalized memory. Taking ownership in and back out is the most simple solution to the problem.
| } | ||
| } | ||
|
|
||
| fn read_into(&mut self, _buffer: &mut [u8]) -> io::Result<()> { |
There was a problem hiding this comment.
.read_into is just .read_exact from io::Read
| } | ||
|
|
||
| fn cause(&self) -> Option<&error::Error> { | ||
| fn cause(&self) -> Option<&dyn error::Error> { |
There was a problem hiding this comment.
this may not have been a great idea if the plan is to support old versions of Rust
| buffer: &'buf mut [MaybeUninit<u8>], | ||
| ) -> io::Result<&'buf mut [u8]> | ||
| { | ||
| self.read_into_uninit_exact(buffer) |
There was a problem hiding this comment.
I wanted to avoid code repetition for a start, later on this could be improved to use .read_into_uninit_exact's implementation, but replacing the EOF error with a return of the initialized subslice.
| fn next(&mut self) -> Option<&'a str> { | ||
| // This import is actually required on Rust 1.13. | ||
| #[allow(unused_imports)] | ||
| #[allow(deprecated, unused_imports)] |
There was a problem hiding this comment.
(imho we shouldn't be targetting such an old version of Rust ...)
| // is not exposed. If `read_into` succeeds, it will have overwritten all | ||
| // bytes. If not, an error is returned and the memory is never exposed. | ||
| unsafe { vendor_bytes.set_len(vendor_len as usize); } | ||
| try!(input.read_into(&mut vendor_bytes)); |
There was a problem hiding this comment.
this was UB because it was creating a &mut reference to uninitialized bytes
| unsafe { vendor_bytes.set_len(vendor_len as usize); } | ||
| try!(input.read_into(&mut vendor_bytes)); | ||
| let mut vendor_bytes = Vec::new(); | ||
| try!(vendor_bytes.extend_from_reader(vendor_len as usize, &mut input)); |
There was a problem hiding this comment.
::uninit is perfect for this kind of patterns
| where | ||
| R : ReadIntoUninit + ReadBytes, | ||
| { | ||
| let length_minus_four = match (length as usize).checked_sub(4) { |
There was a problem hiding this comment.
let's keep a witness of length - 4 not underflowing for later usage.
|
Why is implementing |
|
Take this code pattern (which I expect to be kind of reimplemented by crate users for similar but not quite equal needs): https://docs.rs/uninit/0.1.0/src/uninit/lib.rs.html#195-226 The last step, where it is assumed that
From the docs:
So, this is one of these cases where the trait needs to be |
|
I'm not sure where this PR left off, but I found this library today but the UB is concerning, especially considering I want to parse potentially untrusted FLAC files. Can I help this one out somehow? I tried benchmarking this branch vs The |
|
Thanks for reminding me of this PR; I now have enough time to go back at tackling and polishing this 🙂 . For instance, the |
|
I'd really love something like this PR. I've reviewed the |
|
As an update, rust is considering to deprecate |
(This is currently a WIP PR, for other members of
safety-danceto help audit and improve the code)This PR aims to tackle the issues raised in rust-secure-code/safety-dance#4
Although the author had done a great job with only few instances of
unsafeusage, and all quite well documented / justified, it turns out that the usage of unininitalized bytes has currently no defined behavior in Rust. Instead, theMaybeUninitabstraction can be used, to safely handle uninitialized memory. Sadly, theReadtrait of thestdlibrary does not yet offer an API to work withMaybeUninit.Hence the usage of a helper crate,
::uninit, which provides precisely these zero-cost sound constructs.the only thing is that for
::uninit::ReadIntoUninitto be usable with the custom reader of the crate, thisunsafetrait has been required to be (re)implemented, resulting in some otherunsafeinstances. However, theseunsafeinstances are more of a formality (they aim to guard against a malicious implementation of the trait, which is not the case here).so besides these 3 sound usages of
unsafefor theReadIntoUninitimpl, the otherunsafeblocks have been defused, except for two very well documented and justified cases ofuncheckedindexing, that I have therefore let be (I consider them sound).Sadly, I have not been able to run the unit tests because some files are not in the repository. So I haven't been able to benchmark the impact of these changes 😕