Skip to content

Commit e359b27

Browse files
authored
Merge pull request #1160 from vks/fix-unsafe-block
Fix unsoundness in `<BlockRng64 as RngCore>::next_u32`, with less `unsafe` code
2 parents 9a00a43 + 90b89cd commit e359b27

File tree

6 files changed

+132
-19
lines changed

6 files changed

+132
-19
lines changed

CHANGELOG.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,22 @@ A [separate changelog is kept for rand_core](rand_core/CHANGELOG.md).
88

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

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

18+
### Rngs
19+
- `StdRng`: Switch from HC128 to ChaCha12 on emscripten (#1142).
20+
We now use ChaCha12 on all platforms.
21+
22+
### Documentation
23+
- Added docs about rand's use of const generics (#1150)
24+
- Better random chars example (#1157)
25+
26+
1727
## [0.8.4] - 2021-06-15
1828
### Additions
1929
- Use const-generics to support arrays of all sizes (#1104)

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "rand"
3-
version = "0.8.4"
3+
version = "0.8.5"
44
authors = ["The Rand Project Developers", "The Rust Project Developers"]
55
license = "MIT OR Apache-2.0"
66
readme = "README.md"

rand_core/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7+
## [0.6.4] - 2021-08-20
8+
### Fixed
9+
- Fix unsoundness in `<BlockRng64 as RngCore>::next_u32` (#1160)
10+
711
## [0.6.3] - 2021-06-15
812
### Changed
913
- Improved bound for `serde` impls on `BlockRng` (#1130)

rand_core/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "rand_core"
3-
version = "0.6.3"
3+
version = "0.6.4"
44
authors = ["The Rand Project Developers", "The Rust Project Developers"]
55
license = "MIT OR Apache-2.0"
66
readme = "README.md"

rand_core/src/block.rs

Lines changed: 114 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -352,27 +352,21 @@ where
352352
{
353353
#[inline]
354354
fn next_u32(&mut self) -> u32 {
355-
let mut index = self.index * 2 - self.half_used as usize;
356-
if index >= self.results.as_ref().len() * 2 {
355+
let mut index = self.index - self.half_used as usize;
356+
if index >= self.results.as_ref().len() {
357357
self.core.generate(&mut self.results);
358358
self.index = 0;
359+
index = 0;
359360
// `self.half_used` is by definition `false`
360361
self.half_used = false;
361-
index = 0;
362362
}
363363

364+
let shift = 32 * (self.half_used as usize);
365+
364366
self.half_used = !self.half_used;
365367
self.index += self.half_used as usize;
366368

367-
// Index as if this is a u32 slice.
368-
unsafe {
369-
let results = &*(self.results.as_ref() as *const [u64] as *const [u32]);
370-
if cfg!(target_endian = "little") {
371-
*results.get_unchecked(index)
372-
} else {
373-
*results.get_unchecked(index ^ 1)
374-
}
375-
}
369+
(self.results.as_ref()[index] >> shift) as u32
376370
}
377371

378372
#[inline]
@@ -435,3 +429,111 @@ impl<R: BlockRngCore + SeedableRng> SeedableRng for BlockRng64<R> {
435429
}
436430

437431
impl<R: BlockRngCore + CryptoRng> CryptoRng for BlockRng<R> {}
432+
433+
#[cfg(test)]
434+
mod test {
435+
use crate::{SeedableRng, RngCore};
436+
use crate::block::{BlockRng, BlockRng64, BlockRngCore};
437+
438+
#[derive(Debug, Clone)]
439+
struct DummyRng {
440+
counter: u32,
441+
}
442+
443+
impl BlockRngCore for DummyRng {
444+
type Item = u32;
445+
446+
type Results = [u32; 16];
447+
448+
fn generate(&mut self, results: &mut Self::Results) {
449+
for r in results {
450+
*r = self.counter;
451+
self.counter = self.counter.wrapping_add(3511615421);
452+
}
453+
}
454+
}
455+
456+
impl SeedableRng for DummyRng {
457+
type Seed = [u8; 4];
458+
459+
fn from_seed(seed: Self::Seed) -> Self {
460+
DummyRng { counter: u32::from_le_bytes(seed) }
461+
}
462+
}
463+
464+
#[test]
465+
fn blockrng_next_u32_vs_next_u64() {
466+
let mut rng1 = BlockRng::<DummyRng>::from_seed([1, 2, 3, 4]);
467+
let mut rng2 = rng1.clone();
468+
let mut rng3 = rng1.clone();
469+
470+
let mut a = [0; 16];
471+
(&mut a[..4]).copy_from_slice(&rng1.next_u32().to_le_bytes());
472+
(&mut a[4..12]).copy_from_slice(&rng1.next_u64().to_le_bytes());
473+
(&mut a[12..]).copy_from_slice(&rng1.next_u32().to_le_bytes());
474+
475+
let mut b = [0; 16];
476+
(&mut b[..4]).copy_from_slice(&rng2.next_u32().to_le_bytes());
477+
(&mut b[4..8]).copy_from_slice(&rng2.next_u32().to_le_bytes());
478+
(&mut b[8..]).copy_from_slice(&rng2.next_u64().to_le_bytes());
479+
assert_eq!(a, b);
480+
481+
let mut c = [0; 16];
482+
(&mut c[..8]).copy_from_slice(&rng3.next_u64().to_le_bytes());
483+
(&mut c[8..12]).copy_from_slice(&rng3.next_u32().to_le_bytes());
484+
(&mut c[12..]).copy_from_slice(&rng3.next_u32().to_le_bytes());
485+
assert_eq!(a, c);
486+
}
487+
488+
#[derive(Debug, Clone)]
489+
struct DummyRng64 {
490+
counter: u64,
491+
}
492+
493+
impl BlockRngCore for DummyRng64 {
494+
type Item = u64;
495+
496+
type Results = [u64; 8];
497+
498+
fn generate(&mut self, results: &mut Self::Results) {
499+
for r in results {
500+
*r = self.counter;
501+
self.counter = self.counter.wrapping_add(2781463553396133981);
502+
}
503+
}
504+
}
505+
506+
impl SeedableRng for DummyRng64 {
507+
type Seed = [u8; 8];
508+
509+
fn from_seed(seed: Self::Seed) -> Self {
510+
DummyRng64 { counter: u64::from_le_bytes(seed) }
511+
}
512+
}
513+
514+
#[test]
515+
fn blockrng64_next_u32_vs_next_u64() {
516+
let mut rng1 = BlockRng64::<DummyRng64>::from_seed([1, 2, 3, 4, 5, 6, 7, 8]);
517+
let mut rng2 = rng1.clone();
518+
let mut rng3 = rng1.clone();
519+
520+
let mut a = [0; 16];
521+
(&mut a[..4]).copy_from_slice(&rng1.next_u32().to_le_bytes());
522+
(&mut a[4..12]).copy_from_slice(&rng1.next_u64().to_le_bytes());
523+
(&mut a[12..]).copy_from_slice(&rng1.next_u32().to_le_bytes());
524+
525+
let mut b = [0; 16];
526+
(&mut b[..4]).copy_from_slice(&rng2.next_u32().to_le_bytes());
527+
(&mut b[4..8]).copy_from_slice(&rng2.next_u32().to_le_bytes());
528+
(&mut b[8..]).copy_from_slice(&rng2.next_u64().to_le_bytes());
529+
assert_ne!(a, b);
530+
assert_eq!(&a[..4], &b[..4]);
531+
assert_eq!(&a[4..12], &b[8..]);
532+
533+
let mut c = [0; 16];
534+
(&mut c[..8]).copy_from_slice(&rng3.next_u64().to_le_bytes());
535+
(&mut c[8..12]).copy_from_slice(&rng3.next_u32().to_le_bytes());
536+
(&mut c[12..]).copy_from_slice(&rng3.next_u32().to_le_bytes());
537+
assert_eq!(b, c);
538+
}
539+
}

rand_distr/src/skew_normal.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,7 @@ use rand::Rng;
3939
///
4040
/// [skew normal distribution]: https://en.wikipedia.org/wiki/Skew_normal_distribution
4141
/// [`Normal`]: struct.Normal.html
42-
/// [A Method to Simulate the Skew Normal Distribution]:
43-
/// Ghorbanzadeh, D. , Jaupi, L. and Durand, P. (2014)
44-
/// [A Method to Simulate the Skew Normal Distribution](https://dx.doi.org/10.4236/am.2014.513201).
45-
/// Applied Mathematics, 5, 2073-2076.
42+
/// [A Method to Simulate the Skew Normal Distribution]: https://dx.doi.org/10.4236/am.2014.513201
4643
#[derive(Clone, Copy, Debug)]
4744
#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
4845
pub struct SkewNormal<F>

0 commit comments

Comments
 (0)