Skip to content

Commit 57dee85

Browse files
Check if numeric value is too large before calling parse_time to avoid overflow errors. If value exceeds safe limits, cap at time_t::MAX seconds instead of failing.
1 parent 256817a commit 57dee85

File tree

1 file changed

+25
-15
lines changed

1 file changed

+25
-15
lines changed

src/uu/timeout/src/timeout.rs

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -83,22 +83,32 @@ impl Config {
8383
},
8484
};
8585

86-
let duration =
87-
match parse_time::from_str(options.get_one::<String>(options::DURATION).unwrap(), true)
88-
{
89-
Ok(d) => d,
90-
Err(err) => {
91-
// If parsing fails due to overflow, use maximum valid duration
92-
// This handles cases like "9223372036854775808d" (i64::MAX days)
93-
let err_str = err.clone();
94-
if err_str.contains("overflow") || err_str.contains("too large") {
95-
// Cap at time_t::MAX seconds (maximum valid timeout)
96-
Duration::from_secs(libc::time_t::MAX as u64)
97-
} else {
98-
return Err(UUsageError::new(ExitStatus::TimeoutFailed.into(), err));
99-
}
86+
// Pre-validate the duration string to avoid overflow in parse_time
87+
// Extract numeric part and check if it's too large before parsing
88+
let duration_str = options.get_one::<String>(options::DURATION).unwrap();
89+
let duration = {
90+
// Find where the unit suffix starts (first non-digit, non-decimal character)
91+
let numeric_end = duration_str
92+
.find(|c: char| !c.is_ascii_digit() && c != '.')
93+
.unwrap_or(duration_str.len());
94+
let numeric_part = &duration_str[..numeric_end];
95+
96+
// If the numeric part is huge, cap at maximum valid duration
97+
// i64::MAX seconds is ~292 billion years, i64::MAX days overflows
98+
if let Ok(num) = numeric_part.parse::<f64>() {
99+
// Cap at i64::MAX / 86400 days to prevent overflow in day->second conversion
100+
const MAX_SAFE_DAYS: f64 = (i64::MAX / 86400) as f64;
101+
if num > MAX_SAFE_DAYS {
102+
Duration::from_secs(libc::time_t::MAX as u64)
103+
} else {
104+
parse_time::from_str(duration_str, true)
105+
.map_err(|err| UUsageError::new(ExitStatus::TimeoutFailed.into(), err))?
100106
}
101-
};
107+
} else {
108+
parse_time::from_str(duration_str, true)
109+
.map_err(|err| UUsageError::new(ExitStatus::TimeoutFailed.into(), err))?
110+
}
111+
};
102112

103113
let preserve_status: bool = options.get_flag(options::PRESERVE_STATUS);
104114
let foreground = options.get_flag(options::FOREGROUND);

0 commit comments

Comments
 (0)