diff --git a/kernel/src/syscall/select.rs b/kernel/src/syscall/select.rs index a95e15772..7a073f127 100644 --- a/kernel/src/syscall/select.rs +++ b/kernel/src/syscall/select.rs @@ -19,9 +19,10 @@ pub fn sys_select( let timeout = if timeval_addr == 0 { None } else { - let mut timeval = ctx.get_user_space().read_val::(timeval_addr)?; - timeval.sec += timeval.usec / 1_000_000; - timeval.usec %= 1_000_000; + let timeval = ctx + .get_user_space() + .read_val::(timeval_addr)? + .normalize(); Some(Duration::try_from(timeval)?) }; diff --git a/kernel/src/time/mod.rs b/kernel/src/time/mod.rs index 626a6fd3e..18b09ed3c 100644 --- a/kernel/src/time/mod.rs +++ b/kernel/src/time/mod.rs @@ -21,6 +21,10 @@ pub type time_t = i64; pub type suseconds_t = i64; pub type clock_t = i64; +const NSEC_PER_USEC: i64 = 1_000; +const USEC_PER_SEC: i64 = 1_000_000; +const NSEC_PER_SEC: i64 = 1_000_000_000; + pub(super) fn init() { system_time::init(); clocks::init(); @@ -46,7 +50,7 @@ impl From for timespec_t { impl From for timespec_t { fn from(timeval: timeval_t) -> timespec_t { let sec = timeval.sec; - let nsec = timeval.usec * 1000; + let nsec = timeval.usec * NSEC_PER_USEC; debug_assert!(sec >= 0); // nsec >= 0 always holds timespec_t { sec, nsec } } @@ -60,7 +64,7 @@ impl TryFrom for Duration { return_errno_with_message!(Errno::EINVAL, "timesepc_t cannot be negative"); } - if value.nsec > 1_000_000_000 { + if value.nsec > NSEC_PER_SEC { // The value of nanoseconds cannot exceed 10^9, // otherwise the value for seconds should be set. return_errno_with_message!(Errno::EINVAL, "nsec is not normalized"); @@ -77,6 +81,21 @@ pub struct timeval_t { pub usec: suseconds_t, } +impl timeval_t { + /// Normalizes time by adding carries from microseconds to seconds. + /// + /// Some Linux system calls do this before checking the validity (e.g., the [select] + /// implementation). + /// + /// [select]: https://elixir.bootlin.com/linux/v6.10.5/source/fs/select.c#L716 + pub fn normalize(&self) -> Self { + Self { + sec: self.sec.wrapping_add(self.usec / USEC_PER_SEC), + usec: self.usec % USEC_PER_SEC, + } + } +} + impl From for timeval_t { fn from(duration: Duration) -> timeval_t { let sec = duration.as_secs() as time_t; @@ -93,7 +112,7 @@ impl TryFrom for Duration { if timeval.sec < 0 || timeval.usec < 0 { return_errno_with_message!(Errno::EINVAL, "timeval_t cannot be negative"); } - if timeval.usec > 1_000_000 { + if timeval.usec > USEC_PER_SEC { // The value of microsecond cannot exceed 10^6, // otherwise the value for seconds should be set. return_errno_with_message!(Errno::EINVAL, "nsec is not normalized"); @@ -101,7 +120,7 @@ impl TryFrom for Duration { Ok(Duration::new( timeval.sec as u64, - (timeval.usec * 1000) as u32, + (timeval.usec * NSEC_PER_USEC) as u32, )) } }