Skip to content

Commit

Permalink
Merge pull request #1160 from vks/fix-unsafe-block
Browse files Browse the repository at this point in the history
Fix unsoundness in `<BlockRng64 as RngCore>::next_u32`, with less `unsafe` code
  • Loading branch information
vks committed Sep 12, 2021
2 parents 9a00a43 + 90b89cd commit e359b27
Show file tree
Hide file tree
Showing 6 changed files with 132 additions and 19 deletions.
12 changes: 11 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,22 @@ A [separate changelog is kept for rand_core](rand_core/CHANGELOG.md).

You may also find the [Upgrade Guide](https://rust-random.github.io/book/update.html) useful.

## [0.8.5] - unreleased
## [0.8.5] - 2021-08-20
### Fixes
- Fix build on non-32/64-bit architectures (#1144)
- Fix "min_const_gen" feature for `no_std` (#1173)
- Check `libc::pthread_atfork` return value with panic on error (#1178)
- More robust reseeding in case `ReseedingRng` is used from a fork handler (#1178)

### Rngs
- `StdRng`: Switch from HC128 to ChaCha12 on emscripten (#1142).
We now use ChaCha12 on all platforms.

### Documentation
- Added docs about rand's use of const generics (#1150)
- Better random chars example (#1157)


## [0.8.4] - 2021-06-15
### Additions
- Use const-generics to support arrays of all sizes (#1104)
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "rand"
version = "0.8.4"
version = "0.8.5"
authors = ["The Rand Project Developers", "The Rust Project Developers"]
license = "MIT OR Apache-2.0"
readme = "README.md"
Expand Down
4 changes: 4 additions & 0 deletions rand_core/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.6.4] - 2021-08-20
### Fixed
- Fix unsoundness in `<BlockRng64 as RngCore>::next_u32` (#1160)

## [0.6.3] - 2021-06-15
### Changed
- Improved bound for `serde` impls on `BlockRng` (#1130)
Expand Down
2 changes: 1 addition & 1 deletion rand_core/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "rand_core"
version = "0.6.3"
version = "0.6.4"
authors = ["The Rand Project Developers", "The Rust Project Developers"]
license = "MIT OR Apache-2.0"
readme = "README.md"
Expand Down
126 changes: 114 additions & 12 deletions rand_core/src/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -352,27 +352,21 @@ where
{
#[inline]
fn next_u32(&mut self) -> u32 {
let mut index = self.index * 2 - self.half_used as usize;
if index >= self.results.as_ref().len() * 2 {
let mut index = self.index - self.half_used as usize;
if index >= self.results.as_ref().len() {
self.core.generate(&mut self.results);
self.index = 0;
index = 0;
// `self.half_used` is by definition `false`
self.half_used = false;
index = 0;
}

let shift = 32 * (self.half_used as usize);

self.half_used = !self.half_used;
self.index += self.half_used as usize;

// Index as if this is a u32 slice.
unsafe {
let results = &*(self.results.as_ref() as *const [u64] as *const [u32]);
if cfg!(target_endian = "little") {
*results.get_unchecked(index)
} else {
*results.get_unchecked(index ^ 1)
}
}
(self.results.as_ref()[index] >> shift) as u32
}

#[inline]
Expand Down Expand Up @@ -435,3 +429,111 @@ impl<R: BlockRngCore + SeedableRng> SeedableRng for BlockRng64<R> {
}

impl<R: BlockRngCore + CryptoRng> CryptoRng for BlockRng<R> {}

#[cfg(test)]
mod test {
use crate::{SeedableRng, RngCore};
use crate::block::{BlockRng, BlockRng64, BlockRngCore};

#[derive(Debug, Clone)]
struct DummyRng {
counter: u32,
}

impl BlockRngCore for DummyRng {
type Item = u32;

type Results = [u32; 16];

fn generate(&mut self, results: &mut Self::Results) {
for r in results {
*r = self.counter;
self.counter = self.counter.wrapping_add(3511615421);
}
}
}

impl SeedableRng for DummyRng {
type Seed = [u8; 4];

fn from_seed(seed: Self::Seed) -> Self {
DummyRng { counter: u32::from_le_bytes(seed) }
}
}

#[test]
fn blockrng_next_u32_vs_next_u64() {
let mut rng1 = BlockRng::<DummyRng>::from_seed([1, 2, 3, 4]);
let mut rng2 = rng1.clone();
let mut rng3 = rng1.clone();

let mut a = [0; 16];
(&mut a[..4]).copy_from_slice(&rng1.next_u32().to_le_bytes());
(&mut a[4..12]).copy_from_slice(&rng1.next_u64().to_le_bytes());
(&mut a[12..]).copy_from_slice(&rng1.next_u32().to_le_bytes());

let mut b = [0; 16];
(&mut b[..4]).copy_from_slice(&rng2.next_u32().to_le_bytes());
(&mut b[4..8]).copy_from_slice(&rng2.next_u32().to_le_bytes());
(&mut b[8..]).copy_from_slice(&rng2.next_u64().to_le_bytes());
assert_eq!(a, b);

let mut c = [0; 16];
(&mut c[..8]).copy_from_slice(&rng3.next_u64().to_le_bytes());
(&mut c[8..12]).copy_from_slice(&rng3.next_u32().to_le_bytes());
(&mut c[12..]).copy_from_slice(&rng3.next_u32().to_le_bytes());
assert_eq!(a, c);
}

#[derive(Debug, Clone)]
struct DummyRng64 {
counter: u64,
}

impl BlockRngCore for DummyRng64 {
type Item = u64;

type Results = [u64; 8];

fn generate(&mut self, results: &mut Self::Results) {
for r in results {
*r = self.counter;
self.counter = self.counter.wrapping_add(2781463553396133981);
}
}
}

impl SeedableRng for DummyRng64 {
type Seed = [u8; 8];

fn from_seed(seed: Self::Seed) -> Self {
DummyRng64 { counter: u64::from_le_bytes(seed) }
}
}

#[test]
fn blockrng64_next_u32_vs_next_u64() {
let mut rng1 = BlockRng64::<DummyRng64>::from_seed([1, 2, 3, 4, 5, 6, 7, 8]);
let mut rng2 = rng1.clone();
let mut rng3 = rng1.clone();

let mut a = [0; 16];
(&mut a[..4]).copy_from_slice(&rng1.next_u32().to_le_bytes());
(&mut a[4..12]).copy_from_slice(&rng1.next_u64().to_le_bytes());
(&mut a[12..]).copy_from_slice(&rng1.next_u32().to_le_bytes());

let mut b = [0; 16];
(&mut b[..4]).copy_from_slice(&rng2.next_u32().to_le_bytes());
(&mut b[4..8]).copy_from_slice(&rng2.next_u32().to_le_bytes());
(&mut b[8..]).copy_from_slice(&rng2.next_u64().to_le_bytes());
assert_ne!(a, b);
assert_eq!(&a[..4], &b[..4]);
assert_eq!(&a[4..12], &b[8..]);

let mut c = [0; 16];
(&mut c[..8]).copy_from_slice(&rng3.next_u64().to_le_bytes());
(&mut c[8..12]).copy_from_slice(&rng3.next_u32().to_le_bytes());
(&mut c[12..]).copy_from_slice(&rng3.next_u32().to_le_bytes());
assert_eq!(b, c);
}
}
5 changes: 1 addition & 4 deletions rand_distr/src/skew_normal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,7 @@ use rand::Rng;
///
/// [skew normal distribution]: https://en.wikipedia.org/wiki/Skew_normal_distribution
/// [`Normal`]: struct.Normal.html
/// [A Method to Simulate the Skew Normal Distribution]:
/// Ghorbanzadeh, D. , Jaupi, L. and Durand, P. (2014)
/// [A Method to Simulate the Skew Normal Distribution](https://dx.doi.org/10.4236/am.2014.513201).
/// Applied Mathematics, 5, 2073-2076.
/// [A Method to Simulate the Skew Normal Distribution]: https://dx.doi.org/10.4236/am.2014.513201
#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
pub struct SkewNormal<F>
Expand Down

0 comments on commit e359b27

Please sign in to comment.