Skip to content

Commit 21ce61b

Browse files
committed
Backport #1790: Check UniformChar validity on deser
Prevent memory safety violation in `UniformChar` via deserialization.
1 parent 5309f25 commit 21ce61b

3 files changed

Lines changed: 46 additions & 0 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,3 +76,4 @@ libc = { version = "0.2.22", optional = true, default-features = false }
7676
rand_pcg = { path = "rand_pcg", version = "0.3.0" }
7777
# Only to test serde1
7878
bincode = "1.2.1"
79+
serde_json = "1.0.100"

src/distributions/uniform.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,15 @@ pub struct UniformInt<X> {
426426

427427
macro_rules! uniform_int_impl {
428428
($ty:ty, $unsigned:ident, $u_large:ident) => {
429+
impl UniformInt<$ty> {
430+
/// Get the maximum possible value
431+
#[allow(unused)]
432+
#[inline]
433+
pub(crate) fn max(&self) -> $ty {
434+
self.range.wrapping_sub(1).wrapping_add(self.low)
435+
}
436+
}
437+
429438
impl SampleUniform for $ty {
430439
type Sampler = UniformInt<$ty>;
431440
}
@@ -583,9 +592,24 @@ impl SampleUniform for char {
583592
#[derive(Clone, Copy, Debug)]
584593
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
585594
pub struct UniformChar {
595+
#[cfg_attr(feature = "serde1", serde(deserialize_with = "deser_sampler"))]
586596
sampler: UniformInt<u32>,
587597
}
588598

599+
#[cfg(feature = "serde1")]
600+
fn deser_sampler<'de, D>(d: D) -> Result<UniformInt<u32>, D::Error>
601+
where
602+
D: serde::Deserializer<'de>,
603+
{
604+
let sampler = <UniformInt<u32> as serde::Deserialize>::deserialize(d)?;
605+
if sampler.max() > char::MAX as u32 - CHAR_SURROGATE_LEN {
606+
return Err(serde::de::Error::custom(
607+
"bad sampler range for UniformChar",
608+
));
609+
}
610+
Ok(sampler)
611+
}
612+
589613
/// UTF-16 surrogate range start
590614
const CHAR_SURROGATE_START: u32 = 0xD800;
591615
/// UTF-16 surrogate range size
@@ -1154,6 +1178,24 @@ mod tests {
11541178
}
11551179
}
11561180

1181+
#[test]
1182+
#[cfg(feature = "serde1")]
1183+
fn test_char_bad_deser() {
1184+
let json = r#"{"sampler":{"low":4294967200,"range":0,"z":0}}"#;
1185+
let result = serde_json::from_str::<Uniform<char>>(json);
1186+
assert!(result.is_err());
1187+
let err = result.unwrap_err();
1188+
assert_eq!(err.classify(), serde_json::error::Category::Data);
1189+
1190+
#[cfg(feature = "alloc")]
1191+
{
1192+
assert_eq!(
1193+
alloc::string::ToString::to_string(&err),
1194+
"bad sampler range for UniformChar at line 1 column 46"
1195+
);
1196+
}
1197+
}
1198+
11571199
#[test]
11581200
#[cfg_attr(miri, ignore)] // Miri is too slow
11591201
fn test_floats() {
@@ -1381,6 +1423,7 @@ mod tests {
13811423
let r = Uniform::from(2u32..7);
13821424
assert_eq!(r.0.low, 2);
13831425
assert_eq!(r.0.range, 5);
1426+
assert_eq!(r.0.max(), 6);
13841427
let r = Uniform::from(2.0f64..7.0);
13851428
assert_eq!(r.0.low, 2.0);
13861429
assert_eq!(r.0.scale, 5.0);
@@ -1391,6 +1434,7 @@ mod tests {
13911434
let r = Uniform::from(2u32..=6);
13921435
assert_eq!(r.0.low, 2);
13931436
assert_eq!(r.0.range, 5);
1437+
assert_eq!(r.0.max(), 6);
13941438
let r = Uniform::from(2.0f64..=7.0);
13951439
assert_eq!(r.0.low, 2.0);
13961440
assert!(r.0.scale > 5.0);

src/seq/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1310,6 +1310,7 @@ mod test {
13101310

13111311
#[test]
13121312
#[cfg(feature = "std")]
1313+
#[cfg_attr(miri, ignore)] // Miri is too slow
13131314
fn test_multiple_weighted_distributions() {
13141315
use super::*;
13151316

0 commit comments

Comments
 (0)