Merge pull request #1997 from cyang1/systemtime-panics

Prevent various panics when deserializing malformed SystemTime
This commit is contained in:
David Tolnay 2021-03-05 18:35:48 -08:00 committed by GitHub
commit d91075c8d5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 68 additions and 3 deletions

View File

@ -76,12 +76,14 @@ fn main() {
println!("cargo:rustc-cfg=serde_derive");
}
// TryFrom, Atomic types, and non-zero signed integers stabilized in Rust 1.34:
// TryFrom, Atomic types, non-zero signed integers, and `SystemTime::checked_add`
// stabilized in Rust 1.34:
// https://blog.rust-lang.org/2019/04/11/Rust-1.34.0.html#tryfrom-and-tryinto
// https://blog.rust-lang.org/2019/04/11/Rust-1.34.0.html#library-stabilizations
if minor >= 34 {
println!("cargo:rustc-cfg=core_try_from");
println!("cargo:rustc-cfg=num_nonzero_signed");
println!("cargo:rustc-cfg=systemtime_checked_add");
// Whitelist of archs that support std::sync::atomic module. Ideally we
// would use #[cfg(target_has_atomic = "...")] but it is not stable yet.

View File

@ -2046,6 +2046,17 @@ impl<'de> Deserialize<'de> for SystemTime {
}
}
fn check_overflow<E>(secs: u64, nanos: u32) -> Result<(), E>
where
E: Error,
{
static NANOS_PER_SEC: u32 = 1_000_000_000;
match secs.checked_add((nanos / NANOS_PER_SEC) as u64) {
Some(_) => Ok(()),
None => Err(E::custom("overflow deserializing SystemTime epoch offset")),
}
}
struct DurationVisitor;
impl<'de> Visitor<'de> for DurationVisitor {
@ -2071,6 +2082,7 @@ impl<'de> Deserialize<'de> for SystemTime {
return Err(Error::invalid_length(1, &self));
}
};
try!(check_overflow(secs, nanos));
Ok(Duration::new(secs, nanos))
}
@ -2108,13 +2120,20 @@ impl<'de> Deserialize<'de> for SystemTime {
Some(nanos) => nanos,
None => return Err(<A::Error as Error>::missing_field("nanos_since_epoch")),
};
try!(check_overflow(secs, nanos));
Ok(Duration::new(secs, nanos))
}
}
const FIELDS: &'static [&'static str] = &["secs_since_epoch", "nanos_since_epoch"];
let duration = try!(deserializer.deserialize_struct("SystemTime", FIELDS, DurationVisitor));
Ok(UNIX_EPOCH + duration)
#[cfg(systemtime_checked_add)]
let ret = UNIX_EPOCH
.checked_add(duration)
.ok_or(D::Error::custom("overflow deserializing SystemTime"));
#[cfg(not(systemtime_checked_add))]
let ret = Ok(UNIX_EPOCH + duration);
ret
}
}

View File

@ -15,7 +15,7 @@ use std::sync::atomic::{
AtomicUsize, Ordering,
};
use std::sync::{Arc, Weak as ArcWeak};
use std::time::{Duration, UNIX_EPOCH};
use std::time::{Duration, SystemTime, UNIX_EPOCH};
#[cfg(target_arch = "x86_64")]
use std::sync::atomic::{AtomicI64, AtomicU64};
@ -199,6 +199,19 @@ macro_rules! declare_error_tests {
assert_de_tokens_error::<$target>($tokens, $expected);
}
)+
};
($(
$(#[$cfg:meta])*
$name:ident<$target:ty> { $tokens:expr, $expected:expr, }
)+) => {
$(
$(#[$cfg])*
#[test]
fn $name() {
assert_de_tokens_error::<$target>($tokens, $expected);
}
)+
}
}
@ -1614,4 +1627,35 @@ declare_error_tests! {
],
"overflow deserializing Duration",
}
test_systemtime_overflow_seq<SystemTime> {
&[
Token::Seq { len: Some(2) },
Token::U64(u64::max_value()),
Token::U32(1_000_000_000),
Token::SeqEnd,
],
"overflow deserializing SystemTime epoch offset",
}
test_systemtime_overflow_struct<SystemTime> {
&[
Token::Struct { name: "SystemTime", len: 2 },
Token::Str("secs_since_epoch"),
Token::U64(u64::max_value()),
Token::Str("nanos_since_epoch"),
Token::U32(1_000_000_000),
Token::StructEnd,
],
"overflow deserializing SystemTime epoch offset",
}
#[cfg(systemtime_checked_add)]
test_systemtime_overflow<SystemTime> {
&[
Token::Seq { len: Some(2) },
Token::U64(u64::max_value()),
Token::U32(0),
Token::SeqEnd,
],
"overflow deserializing SystemTime",
}
}