rust-secure-code / safety-dance

Auditing crates for unsafe code which can be safely replaced
Apache License 2.0
536 stars 10 forks source link

Audit rand #54

Open TyPR124 opened 5 years ago

TyPR124 commented 5 years ago

https://crates.io/crates/rand

Currently the most downloaded crate on crates.io.

Contains quite a few unsafe

Functions  Expressions  Impls  Traits  Methods  Dependency

0/0        37/95        0/0    0/0     0/0      !  rand 0.7.2
0/4        4/80         0/0    0/0     0/1      !  ├── getrandom 0.1.13
0/0        0/0          0/0    0/0     0/0      ?  │   └── cfg-if 0.1.10
0/0        0/0          0/0    0/0     0/0      ?  ├── rand_chacha 0.2.1
0/0        0/0          0/0    0/0     0/0      ?  │   ├── c2-chacha 0.2.3
2/2        469/500      0/0    0/0     14/22    !  │   │   └── ppv-lite86 0.2.6
0/0        22/22        0/0    0/0     0/0      !  │   └── rand_core 0.5.1
0/4        4/80         0/0    0/0     0/1      !  │       └── getrandom 0.1.13
0/0        22/22        0/0    0/0     0/0      !  ├── rand_core 0.5.1
0/0        0/0          0/0    0/0     0/0      ?  └── rand_pcg 0.2.1
0/0        22/22        0/0    0/0     0/0      !      └── rand_core 0.5.1
Lokathor commented 5 years ago

I audited some of it in the past and sent in a few small fixes.

Unfortunately, having them take on extra dependencies is less likely because of how central the crate is, but there is probably still space to improve.

Phlosioneer commented 4 years ago

This task should probably be broken up into the sub-crates. A lot of the unsafe code is mostly to use OS functions while in no_std mode, from what I've gathered. The first step is to figure where nontrivial unsafe code is.

dhardy commented 4 years ago

Extracted from a reddit post:

[..] inspired me to have a quick look at uses of unsafe in the Rand crate. It would seem that uses can be categorised under:

So in my view, unsafe is a big hammer where often a much smaller, more specialised tool could do the job. I have in the past found memory safety issues in unsafe code which had nothing to do with the motivation for using unsafe, but which were nevertheless hidden by use of it. Better tools could go a long way to improving this situation, e.g. things like unsafe_assertion(i < len) or unsafe(concurrent_access).


It looks like this repository is focussed on memory safety, so I'd just like to quickly mention that Rand has a few other safety concerns: that generated keys/values are filled with random data, that RNGs are correctly initialised, that CSPRNG state is not inadvertently leaked, that CSPRNGs correspond to published test vectors, and a few other bits like fork detection.

alex commented 4 years ago

To point number two, could those be replaced with u64::from_ne_bytes()?

dhardy commented 4 years ago

@alex ~no, but see this post to avoid redundant discussion.~

Possibly yes actually, though I think it requires a more recent compiler than our current MSRV of 1.32.

TyPR124 commented 4 years ago

u64::from_ne_bytes() is available on 1.32 so it should be doable while keeping MSRV 1.32, unless there's something else that would be needed.

Shnatsel commented 4 years ago

If you're converting from &[u8] instead of [u8; 8] you also need TryFrom, which has MSRV of 1.34

tarcieri commented 4 years ago

Alternatively you can create a [0u8; 8] and use copy_from_slice. The TryFrom method is definitely nicer though.

Shnatsel commented 4 years ago

The copy_from_slice approach can be fiddly, sometimes rustc doesn't optimize equivalent-looking code properly: https://godbolt.org/z/jmac5x

alex commented 4 years ago

Has a Rust (LLVM?) bug been filed on that?

Shnatsel commented 4 years ago

Yes, rustc bug: https://github.com/rust-lang/rust/issues/70439

Shnatsel commented 4 years ago

I've found some code that's unsound but doesn't pose a security issue and sent in a fix: https://github.com/rust-random/rand/issues/959

Shnatsel commented 4 years ago

I've also managed to get rid of unsafe code in &[u32] to u64 conversion by leaning into the optimizer: https://github.com/rust-random/rand/pull/963

Shnatsel commented 4 years ago

Another small reduction: https://github.com/rust-random/rand/pull/962

Shnatsel commented 4 years ago

I've looked into https://github.com/rust-random/rand/blob/05a1273ea83eeb0c0ade64ea55600b7f1fa39ec5/rand_core/src/block.rs#L352-L373 and it seems this unsafe cannot be removed without degrading performance and/or a major refactoring. But memory safety is just one of many guarantees this code must uphold, so I'm not too concerned about the unsafe here.

On the other hand, the uses of unsafe in the following files seem avoidable:

Unfortunately, I probably won't have the time to make actual pull requests or look into the remaining unsafe code.

Shnatsel commented 4 years ago

MSRV bump from 1.32 to 1.34 should be harmless because even Debian Stable ships 1.34 by now.

dhardy commented 4 years ago

Rust 1.34 is also nearly a year old. I don't see any problem bumping to this version for the 0.8 release, which is what the master branch is already working towards. (Maybe should ping @vks and @newpavlov to check, but I don't see any issue.)

Shnatsel commented 4 years ago

Copying from https://github.com/rust-random/rand/issues/957:

Most unsafe code was removed in https://github.com/rust-random/rand/pull/1011

However, there is one use case remaining (fill_via_chunks) where we could not make the safe code as fast as the unsafe code.

Conversion of fill_via_chunks to safe code has been attempted in https://github.com/rust-random/rand/pull/1011, see that PR for more info.