Skip to content

Commit 6a71a45

Browse files
always use parse_time::from_str first. Only if it fails AND the value is a huge overflow case, then cap it
1 parent 81cd33b commit 6a71a45

File tree

1 file changed

+41
-36
lines changed

1 file changed

+41
-36
lines changed

src/uu/timeout/src/timeout.rs

Lines changed: 41 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -63,45 +63,50 @@ struct Config {
6363
/// Parse a duration string with overflow protection
6464
/// Caps extremely large values at a safe maximum that works on all platforms
6565
fn parse_duration_with_overflow_protection(duration_str: &str) -> UResult<Duration> {
66-
// Pre-check for extremely large values that would overflow
67-
// Only intercept if it's a simple integer + unit suffix pattern
68-
let numeric_end = duration_str
69-
.find(|c: char| !c.is_ascii_digit())
70-
.unwrap_or(duration_str.len());
71-
72-
// Only apply overflow protection if we have a simple pattern: all digits followed by optional unit
73-
if numeric_end > 0
74-
&& (numeric_end == duration_str.len()
75-
|| matches!(
76-
duration_str.chars().nth(numeric_end),
77-
Some('s') | Some('m') | Some('h') | Some('d')
78-
))
79-
{
80-
let numeric_part = &duration_str[..numeric_end];
81-
let unit_suffix = &duration_str[numeric_end..];
82-
83-
if let Ok(num) = numeric_part.parse::<u128>() {
84-
// Check if this would cause overflow
85-
let (_multiplier, max_safe) = match unit_suffix {
86-
"" | "s" => (1u64, u64::MAX),
87-
"m" => (60, u64::MAX / 60),
88-
"h" => (3600, u64::MAX / 3600),
89-
"d" => (86400, u64::MAX / 86400),
90-
_ => (1u64, u64::MAX), // Shouldn't reach here
91-
};
92-
93-
if num > max_safe as u128 {
94-
// Cap at a safe maximum (~34 years) that works on all platforms
95-
// This matches the cap in process.rs for kqueue/sigtimedwait
96-
const MAX_SAFE_TIMEOUT_SECS: u64 = (i32::MAX / 2) as u64;
97-
return Ok(Duration::from_secs(MAX_SAFE_TIMEOUT_SECS));
66+
// Try standard parsing first
67+
match parse_time::from_str(duration_str, true) {
68+
Ok(duration) => Ok(duration),
69+
Err(_) => {
70+
// If parsing fails, check if it's due to overflow with huge values
71+
// Only try to handle simple numeric patterns: digits + optional unit suffix
72+
let numeric_end = duration_str
73+
.find(|c: char| !c.is_ascii_digit())
74+
.unwrap_or(duration_str.len());
75+
76+
if numeric_end > 0 {
77+
let numeric_part = &duration_str[..numeric_end];
78+
let unit_suffix = &duration_str[numeric_end..];
79+
80+
if let Ok(num) = numeric_part.parse::<u128>() {
81+
// Check if this looks like a huge value that would overflow
82+
let max_safe = match unit_suffix {
83+
"" | "s" => u64::MAX,
84+
"m" => u64::MAX / 60,
85+
"h" => u64::MAX / 3600,
86+
"d" => u64::MAX / 86400,
87+
_ => {
88+
return Err(UUsageError::new(
89+
ExitStatus::TimeoutFailed.into(),
90+
format!("invalid time interval {duration_str}"),
91+
));
92+
}
93+
};
94+
95+
if num > max_safe as u128 {
96+
// Cap at a safe maximum (~34 years) that works on all platforms
97+
const MAX_SAFE_TIMEOUT_SECS: u64 = (i32::MAX / 2) as u64;
98+
return Ok(Duration::from_secs(MAX_SAFE_TIMEOUT_SECS));
99+
}
100+
}
98101
}
102+
103+
// If it wasn't an overflow case, return the original error
104+
Err(UUsageError::new(
105+
ExitStatus::TimeoutFailed.into(),
106+
format!("invalid time interval {duration_str}"),
107+
))
99108
}
100109
}
101-
102-
// For all other cases (including normal values), use the standard parser
103-
parse_time::from_str(duration_str, true)
104-
.map_err(|err| UUsageError::new(ExitStatus::TimeoutFailed.into(), err))
105110
}
106111

107112
impl Config {

0 commit comments

Comments
 (0)