Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions library/std/src/sys/pal/hermit/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,20 +28,20 @@ impl Timespec {
#[rustc_const_unstable(feature = "const_system_time", issue = "144517")]
const fn sub_timespec(&self, other: &Timespec) -> Result<Duration, Duration> {
// FIXME: const PartialOrd
let mut cmp = self.t.tv_sec - other.t.tv_sec;
let mut cmp = self.t.tv_sec.saturating_sub(other.t.tv_sec);
if cmp == 0 {
cmp = self.t.tv_nsec as i64 - other.t.tv_nsec as i64;
}

if cmp >= 0 {
Ok(if self.t.tv_nsec >= other.t.tv_nsec {
Duration::new(
(self.t.tv_sec - other.t.tv_sec) as u64,
self.t.tv_sec.wrapping_sub(other.t.tv_sec) as u64,
(self.t.tv_nsec - other.t.tv_nsec) as u32,
)
} else {
Duration::new(
(self.t.tv_sec - 1 - other.t.tv_sec) as u64,
self.t.tv_sec.wrapping_sub(other.t.tv_sec) as u64 - 1_u64,
(self.t.tv_nsec + NSEC_PER_SEC - other.t.tv_nsec) as u32,
)
})
Expand Down
2 changes: 1 addition & 1 deletion library/std/src/sys/pal/uefi/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ impl SystemTime {

// Check if can be represented in UEFI
// FIXME: const PartialOrd
let mut cmp = temp.as_secs() - MAX_UEFI_TIME.0.as_secs();
let mut cmp = temp.as_secs().saturating_sub(MAX_UEFI_TIME.0.as_secs());
if cmp == 0 {
cmp = temp.subsec_nanos() as u64 - MAX_UEFI_TIME.0.subsec_nanos() as u64;
}
Expand Down
6 changes: 3 additions & 3 deletions library/std/src/sys/pal/unix/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ impl Timespec {
#[rustc_const_unstable(feature = "const_system_time", issue = "144517")]
pub const fn sub_timespec(&self, other: &Timespec) -> Result<Duration, Duration> {
// FIXME: const PartialOrd
let mut cmp = self.tv_sec - other.tv_sec;
let mut cmp = self.tv_sec.saturating_sub(other.tv_sec);
if cmp == 0 {
cmp = self.tv_nsec.as_inner() as i64 - other.tv_nsec.as_inner() as i64;
}
Expand All @@ -160,12 +160,12 @@ impl Timespec {
// directly expresses the lower-cost behavior we want from it.
let (secs, nsec) = if self.tv_nsec.as_inner() >= other.tv_nsec.as_inner() {
(
(self.tv_sec - other.tv_sec) as u64,
self.tv_sec.wrapping_sub(other.tv_sec) as u64,
self.tv_nsec.as_inner() - other.tv_nsec.as_inner(),
)
} else {
(
(self.tv_sec - other.tv_sec - 1) as u64,
self.tv_sec.wrapping_sub(other.tv_sec) as u64 - 1_u64,
self.tv_nsec.as_inner() + (NSEC_PER_SEC as u32) - other.tv_nsec.as_inner(),
)
};
Expand Down
26 changes: 26 additions & 0 deletions library/std/tests/time.rs
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noting to myself:

  • SOLID's changes were purely organizational
  • SGX's changed to explicit match because or_else isn't constifiable
  • "unsupported" removed or_else
  • WASI removed or_else and try_into
  • Windows removed try_into

So they had no notable logic changes.

Original file line number Diff line number Diff line change
Expand Up @@ -227,3 +227,29 @@ fn big_math() {
check(instant.checked_add(Duration::from_secs(100)), Instant::checked_sub);
check(instant.checked_add(Duration::from_secs(i64::MAX as _)), Instant::checked_sub);
}

#[test]
#[cfg(unix)]
fn system_time_extreme_values_regression() {
// Test for regression in SystemTime comparison with extreme values
// This test covers the bug introduced in PR #144519 where integer overflow
// in the comparison logic caused incorrect results when dealing with times
// near i64::MIN and i64::MAX.
//
// This is the exact test case from GitHub issue #146228
let t = SystemTime::UNIX_EPOCH;
let early = t - (Duration::from_secs(i64::MAX as u64 + 1));
let later = t + (Duration::from_secs(i64::MAX as u64) + Duration::from_nanos(999_999_999));
Comment on lines +240 to +242
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Offset of the SystemTime by a Duration is not guaranteed to give a sensible result and I do not believe cfg(unix) is sufficient for this.

Copy link
Contributor

@stepancheg stepancheg Sep 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In current std implementation, this operation supposed to succeed, because, SystemTime is defined using Timespec that has i64 seconds part:

cfg_select! {
unix => {
mod unix;
pub use self::unix::*;
}

#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct SystemTime {
pub(crate) t: Timespec,
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub(crate) struct Timespec {
tv_sec: i64,
tv_nsec: Nanoseconds,
}

Rust std may want to reduce the range of SystemTime or Timespec one day, but I don't think this is likely, and if that happens, the test can be changed (and likely other tests too).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm. My concern was that I am not entirely sure everything that uses cfg(unix) uses that file, but I can allow such problems to be handled separately, so I suppose I will.


// This should succeed and not return a SystemTimeError due to incorrect comparison overflow
let delta =
later.duration_since(early).expect("duration_since should work with extreme values");

// Verify that the delta calculation is reasonable
// early is at approximately -i64::MAX-1 seconds from epoch
// later is at approximately i64::MAX seconds + 999_999_999 nanoseconds from epoch
// So delta should be approximately (i64::MAX + i64::MAX + 1) seconds + 999_999_999 nanoseconds
let expected_secs = (i64::MAX as u64) * 2 + 1;
let expected = Duration::new(expected_secs, 999_999_999);
assert_eq!(delta, expected, "Duration calculation should be correct for extreme values");
}
Loading