@@ -57,6 +57,49 @@ struct Config {
5757 command : Vec < String > ,
5858}
5959
60+ /// Parse a duration string with overflow protection
61+ /// Caps extremely large values at a safe maximum that works on all platforms
62+ fn parse_duration_with_overflow_protection ( duration_str : & str ) -> UResult < Duration > {
63+ // Find where the unit suffix starts (first non-digit character)
64+ let numeric_end = duration_str
65+ . find ( |c : char | !c. is_ascii_digit ( ) )
66+ . unwrap_or ( duration_str. len ( ) ) ;
67+ let numeric_part = & duration_str[ ..numeric_end] ;
68+ let unit_suffix = & duration_str[ numeric_end..] ;
69+
70+ if let Ok ( num) = numeric_part. parse :: < u128 > ( ) {
71+ match unit_suffix {
72+ "" | "s" | "m" | "h" | "d" => {
73+ let ( multiplier, max_safe) = match unit_suffix {
74+ "" | "s" => ( 1u64 , u64:: MAX ) ,
75+ "m" => ( 60 , u64:: MAX / 60 ) ,
76+ "h" => ( 3600 , u64:: MAX / 3600 ) ,
77+ "d" => ( 86400 , u64:: MAX / 86400 ) ,
78+ _ => unreachable ! ( ) ,
79+ } ;
80+
81+ if num > max_safe as u128 {
82+ // Cap at a safe maximum (~34 years) that works on all platforms
83+ // This matches the cap in process.rs for kqueue/sigtimedwait
84+ const MAX_SAFE_TIMEOUT_SECS : u64 = ( i32:: MAX / 2 ) as u64 ;
85+ Ok ( Duration :: from_secs ( MAX_SAFE_TIMEOUT_SECS ) )
86+ } else {
87+ let secs = ( num as u64 ) * multiplier;
88+ Ok ( Duration :: from_secs ( secs) )
89+ }
90+ }
91+ _ => {
92+ // Unknown suffix, fallback to parse_time
93+ parse_time:: from_str ( duration_str, true )
94+ . map_err ( |err| UUsageError :: new ( ExitStatus :: TimeoutFailed . into ( ) , err) )
95+ }
96+ }
97+ } else {
98+ parse_time:: from_str ( duration_str, true )
99+ . map_err ( |err| UUsageError :: new ( ExitStatus :: TimeoutFailed . into ( ) , err) )
100+ }
101+ }
102+
60103impl Config {
61104 fn from ( options : & clap:: ArgMatches ) -> UResult < Self > {
62105 let signal = match options. get_one :: < String > ( options:: SIGNAL ) {
@@ -77,55 +120,11 @@ impl Config {
77120
78121 let kill_after = match options. get_one :: < String > ( options:: KILL_AFTER ) {
79122 None => None ,
80- Some ( kill_after) => match parse_time:: from_str ( kill_after, true ) {
81- Ok ( k) => Some ( k) ,
82- Err ( err) => return Err ( UUsageError :: new ( ExitStatus :: TimeoutFailed . into ( ) , err) ) ,
83- } ,
123+ Some ( kill_after_str) => Some ( parse_duration_with_overflow_protection ( kill_after_str) ?) ,
84124 } ;
85125
86- // Pre-validate duration string to prevent overflow in parse_time
87126 let duration_str = options. get_one :: < String > ( options:: DURATION ) . unwrap ( ) ;
88- let duration = {
89- // Find where the unit suffix starts (first non-digit character)
90- let numeric_end = duration_str
91- . find ( |c : char | !c. is_ascii_digit ( ) )
92- . unwrap_or ( duration_str. len ( ) ) ;
93- let numeric_part = & duration_str[ ..numeric_end] ;
94- let unit_suffix = & duration_str[ numeric_end..] ;
95-
96- if let Ok ( num) = numeric_part. parse :: < u128 > ( ) {
97- match unit_suffix {
98- "" | "s" | "m" | "h" | "d" => {
99- let ( multiplier, max_safe) = match unit_suffix {
100- "" | "s" => ( 1u64 , u64:: MAX ) ,
101- "m" => ( 60 , u64:: MAX / 60 ) ,
102- "h" => ( 3600 , u64:: MAX / 3600 ) ,
103- "d" => ( 86400 , u64:: MAX / 86400 ) ,
104- _ => unreachable ! ( ) ,
105- } ;
106-
107- if num > max_safe as u128 {
108- // Cap at a safe maximum (~34 years) that works on all platforms
109- // This matches the cap in process.rs for kqueue/sigtimedwait
110- const MAX_SAFE_TIMEOUT_SECS : u64 = ( i32:: MAX / 2 ) as u64 ;
111- Duration :: from_secs ( MAX_SAFE_TIMEOUT_SECS )
112- } else {
113- let secs = ( num as u64 ) * multiplier;
114- Duration :: from_secs ( secs)
115- }
116- }
117- _ => {
118- // Unknown suffix, fallback to parse_time
119- parse_time:: from_str ( duration_str, true ) . map_err ( |err| {
120- UUsageError :: new ( ExitStatus :: TimeoutFailed . into ( ) , err)
121- } ) ?
122- }
123- }
124- } else {
125- parse_time:: from_str ( duration_str, true )
126- . map_err ( |err| UUsageError :: new ( ExitStatus :: TimeoutFailed . into ( ) , err) ) ?
127- }
128- } ;
127+ let duration = parse_duration_with_overflow_protection ( duration_str) ?;
129128
130129 let preserve_status: bool = options. get_flag ( options:: PRESERVE_STATUS ) ;
131130 let foreground = options. get_flag ( options:: FOREGROUND ) ;
0 commit comments