-
Notifications
You must be signed in to change notification settings - Fork 28
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[safety-dance] Remove some unsound usages of unsafe #19
base: master
Are you sure you want to change the base?
[safety-dance] Remove some unsound usages of unsafe #19
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.
@@ -167,10 +163,6 @@ impl<R: ReadBytes> ReadBytes for Crc16Reader<R> { | |||
} | |||
} | |||
|
|||
fn read_into(&mut self, _buffer: &mut [u8]) -> io::Result<()> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
.read_into
is just .read_exact
from io::Read
@@ -70,7 +70,7 @@ impl error::Error for Error { | |||
} | |||
} | |||
|
|||
fn cause(&self) -> Option<&error::Error> { | |||
fn cause(&self) -> Option<&dyn error::Error> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
@@ -189,7 +191,7 @@ impl<'a> Iterator for GetTag<'a> { | |||
#[inline] | |||
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
::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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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-dance
to 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
unsafe
usage, and all quite well documented / justified, it turns out that the usage of unininitalized bytes has currently no defined behavior in Rust. Instead, theMaybeUninit
abstraction can be used, to safely handle uninitialized memory. Sadly, theRead
trait of thestd
library 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::ReadIntoUninit
to be usable with the custom reader of the crate, thisunsafe
trait has been required to be (re)implemented, resulting in some otherunsafe
instances. However, theseunsafe
instances 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
unsafe
for theReadIntoUninit
impl
, the otherunsafe
blocks have been defused, except for two very well documented and justified cases ofunchecked
indexing, 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 😕