Skip to content

Commit 4118cec

Browse files
committed
Prevent various panics when deserializing malformed SystemTime
1 parent c261015 commit 4118cec

File tree

3 files changed

+68
-3
lines changed

3 files changed

+68
-3
lines changed

serde/build.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,12 +76,14 @@ fn main() {
7676
println!("cargo:rustc-cfg=serde_derive");
7777
}
7878

79-
// TryFrom, Atomic types, and non-zero signed integers stabilized in Rust 1.34:
79+
// TryFrom, Atomic types, non-zero signed integers, and `SystemTime::checked_add`
80+
// stabilized in Rust 1.34:
8081
// https://blog.rust-lang.org/2019/04/11/Rust-1.34.0.html#tryfrom-and-tryinto
8182
// https://blog.rust-lang.org/2019/04/11/Rust-1.34.0.html#library-stabilizations
8283
if minor >= 34 {
8384
println!("cargo:rustc-cfg=core_try_from");
8485
println!("cargo:rustc-cfg=num_nonzero_signed");
86+
println!("cargo:rustc-cfg=systemtime_checked_add");
8587

8688
// Whitelist of archs that support std::sync::atomic module. Ideally we
8789
// would use #[cfg(target_has_atomic = "...")] but it is not stable yet.

serde/src/de/impls.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2046,6 +2046,17 @@ impl<'de> Deserialize<'de> for SystemTime {
20462046
}
20472047
}
20482048

2049+
fn check_overflow<E>(secs: u64, nanos: u32) -> Result<(), E>
2050+
where
2051+
E: Error,
2052+
{
2053+
static NANOS_PER_SEC: u32 = 1_000_000_000;
2054+
match secs.checked_add((nanos / NANOS_PER_SEC) as u64) {
2055+
Some(_) => Ok(()),
2056+
None => Err(E::custom("overflow deserializing SystemTime epoch offset")),
2057+
}
2058+
}
2059+
20492060
struct DurationVisitor;
20502061

20512062
impl<'de> Visitor<'de> for DurationVisitor {
@@ -2071,6 +2082,7 @@ impl<'de> Deserialize<'de> for SystemTime {
20712082
return Err(Error::invalid_length(1, &self));
20722083
}
20732084
};
2085+
try!(check_overflow(secs, nanos));
20742086
Ok(Duration::new(secs, nanos))
20752087
}
20762088

@@ -2108,13 +2120,20 @@ impl<'de> Deserialize<'de> for SystemTime {
21082120
Some(nanos) => nanos,
21092121
None => return Err(<A::Error as Error>::missing_field("nanos_since_epoch")),
21102122
};
2123+
try!(check_overflow(secs, nanos));
21112124
Ok(Duration::new(secs, nanos))
21122125
}
21132126
}
21142127

21152128
const FIELDS: &'static [&'static str] = &["secs_since_epoch", "nanos_since_epoch"];
21162129
let duration = try!(deserializer.deserialize_struct("SystemTime", FIELDS, DurationVisitor));
2117-
Ok(UNIX_EPOCH + duration)
2130+
#[cfg(systemtime_checked_add)]
2131+
let ret = UNIX_EPOCH
2132+
.checked_add(duration)
2133+
.ok_or(D::Error::custom("overflow deserializing SystemTime"));
2134+
#[cfg(not(systemtime_checked_add))]
2135+
let ret = Ok(UNIX_EPOCH + duration);
2136+
ret
21182137
}
21192138
}
21202139

test_suite/tests/test_de.rs

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use std::sync::atomic::{
1515
AtomicUsize, Ordering,
1616
};
1717
use std::sync::{Arc, Weak as ArcWeak};
18-
use std::time::{Duration, UNIX_EPOCH};
18+
use std::time::{Duration, SystemTime, UNIX_EPOCH};
1919

2020
#[cfg(target_arch = "x86_64")]
2121
use std::sync::atomic::{AtomicI64, AtomicU64};
@@ -199,6 +199,19 @@ macro_rules! declare_error_tests {
199199
assert_de_tokens_error::<$target>($tokens, $expected);
200200
}
201201
)+
202+
};
203+
204+
($(
205+
$(#[$cfg:meta])*
206+
$name:ident<$target:ty> { $tokens:expr, $expected:expr, }
207+
)+) => {
208+
$(
209+
$(#[$cfg])*
210+
#[test]
211+
fn $name() {
212+
assert_de_tokens_error::<$target>($tokens, $expected);
213+
}
214+
)+
202215
}
203216
}
204217

@@ -1614,4 +1627,35 @@ declare_error_tests! {
16141627
],
16151628
"overflow deserializing Duration",
16161629
}
1630+
test_systemtime_overflow_seq<SystemTime> {
1631+
&[
1632+
Token::Seq { len: Some(2) },
1633+
Token::U64(u64::max_value()),
1634+
Token::U32(1_000_000_000),
1635+
Token::SeqEnd,
1636+
],
1637+
"overflow deserializing SystemTime epoch offset",
1638+
}
1639+
test_systemtime_overflow_struct<SystemTime> {
1640+
&[
1641+
Token::Struct { name: "SystemTime", len: 2 },
1642+
Token::Str("secs_since_epoch"),
1643+
Token::U64(u64::max_value()),
1644+
1645+
Token::Str("nanos_since_epoch"),
1646+
Token::U32(1_000_000_000),
1647+
Token::StructEnd,
1648+
],
1649+
"overflow deserializing SystemTime epoch offset",
1650+
}
1651+
#[cfg(systemtime_checked_add)]
1652+
test_systemtime_overflow<SystemTime> {
1653+
&[
1654+
Token::Seq { len: Some(2) },
1655+
Token::U64(u64::max_value()),
1656+
Token::U32(0),
1657+
Token::SeqEnd,
1658+
],
1659+
"overflow deserializing SystemTime",
1660+
}
16171661
}

0 commit comments

Comments
 (0)