@@ -7340,7 +7340,9 @@ impl Interpreter {
73407340 {
73417341 let left = self . parse_arithmetic_impl ( & expr[ ..i - 1 ] , arith_depth + 1 ) ;
73427342 let right = self . parse_arithmetic_impl ( & expr[ i + 1 ..] , arith_depth + 1 ) ;
7343- return left << right;
7343+ // THREAT[TM-DOS-029]: clamp shift to 0..=63 to prevent panic
7344+ let shift = right. clamp ( 0 , 63 ) as u32 ;
7345+ return left. wrapping_shl ( shift) ;
73447346 }
73457347 '>' if depth == 0
73467348 && i > 0
@@ -7350,7 +7352,9 @@ impl Interpreter {
73507352 {
73517353 let left = self . parse_arithmetic_impl ( & expr[ ..i - 1 ] , arith_depth + 1 ) ;
73527354 let right = self . parse_arithmetic_impl ( & expr[ i + 1 ..] , arith_depth + 1 ) ;
7353- return left >> right;
7355+ // THREAT[TM-DOS-029]: clamp shift to 0..=63 to prevent panic
7356+ let shift = right. clamp ( 0 , 63 ) as u32 ;
7357+ return left. wrapping_shr ( shift) ;
73547358 }
73557359 _ => { }
73567360 }
@@ -7378,10 +7382,11 @@ impl Interpreter {
73787382 }
73797383 let left = self . parse_arithmetic_impl ( & expr[ ..i] , arith_depth + 1 ) ;
73807384 let right = self . parse_arithmetic_impl ( & expr[ i + 1 ..] , arith_depth + 1 ) ;
7385+ // THREAT[TM-DOS-029]: wrapping to prevent overflow panic
73817386 return if chars[ i] == '+' {
7382- left + right
7387+ left. wrapping_add ( right)
73837388 } else {
7384- left - right
7389+ left. wrapping_sub ( right)
73857390 } ;
73867391 }
73877392 _ => { }
@@ -7404,22 +7409,24 @@ impl Interpreter {
74047409 }
74057410 let left = self . parse_arithmetic_impl ( & expr[ ..i] , arith_depth + 1 ) ;
74067411 let right = self . parse_arithmetic_impl ( & expr[ i + 1 ..] , arith_depth + 1 ) ;
7407- return left * right;
7412+ // THREAT[TM-DOS-029]: wrapping to prevent overflow panic
7413+ return left. wrapping_mul ( right) ;
74087414 }
74097415 '/' | '%' if depth == 0 => {
74107416 let left = self . parse_arithmetic_impl ( & expr[ ..i] , arith_depth + 1 ) ;
74117417 let right = self . parse_arithmetic_impl ( & expr[ i + 1 ..] , arith_depth + 1 ) ;
7418+ // THREAT[TM-DOS-029]: wrapping to prevent i64::MIN / -1 panic
74127419 return match chars[ i] {
74137420 '/' => {
74147421 if right != 0 {
7415- left / right
7422+ left. wrapping_div ( right)
74167423 } else {
74177424 0
74187425 }
74197426 }
74207427 '%' => {
74217428 if right != 0 {
7422- left % right
7429+ left. wrapping_rem ( right)
74237430 } else {
74247431 0
74257432 }
@@ -7441,7 +7448,9 @@ impl Interpreter {
74417448 let left = self . parse_arithmetic_impl ( & expr[ ..i] , arith_depth + 1 ) ;
74427449 // Right-associative: parse from i+2 onward (may contain more **)
74437450 let right = self . parse_arithmetic_impl ( & expr[ i + 2 ..] , arith_depth + 1 ) ;
7444- return left. pow ( right as u32 ) ;
7451+ // THREAT[TM-DOS-029]: clamp exponent to 0..=63 to prevent panic/hang
7452+ let exp = right. clamp ( 0 , 63 ) as u32 ;
7453+ return left. wrapping_pow ( exp) ;
74457454 }
74467455 _ => { }
74477456 }
@@ -7451,7 +7460,10 @@ impl Interpreter {
74517460 if let Some ( rest) = expr. strip_prefix ( '-' ) {
74527461 let rest = rest. trim ( ) ;
74537462 if !rest. is_empty ( ) {
7454- return -self . parse_arithmetic_impl ( rest, arith_depth + 1 ) ;
7463+ // THREAT[TM-DOS-029]: wrapping to prevent i64::MIN negation panic
7464+ return self
7465+ . parse_arithmetic_impl ( rest, arith_depth + 1 )
7466+ . wrapping_neg ( ) ;
74557467 }
74567468 }
74577469 if let Some ( rest) = expr. strip_prefix ( '~' ) {
@@ -9314,4 +9326,52 @@ mod tests {
93149326 let result = run_script ( "set a b c\n echo $#\n echo $1 $2 $3" ) . await ;
93159327 assert_eq ! ( result. stdout, "3\n a b c\n " ) ;
93169328 }
9329+
9330+ #[ tokio:: test]
9331+ async fn test_arithmetic_exponent_negative_no_panic ( ) {
9332+ let result = run_script ( "echo $(( 2 ** -1 ))" ) . await ;
9333+ assert_eq ! ( result. exit_code, 0 ) ;
9334+ }
9335+
9336+ #[ tokio:: test]
9337+ async fn test_arithmetic_exponent_large_no_panic ( ) {
9338+ let result = run_script ( "echo $(( 2 ** 100 ))" ) . await ;
9339+ assert_eq ! ( result. exit_code, 0 ) ;
9340+ }
9341+
9342+ #[ tokio:: test]
9343+ async fn test_arithmetic_shift_large_no_panic ( ) {
9344+ let result = run_script ( "echo $(( 1 << 64 ))" ) . await ;
9345+ assert_eq ! ( result. exit_code, 0 ) ;
9346+ }
9347+
9348+ #[ tokio:: test]
9349+ async fn test_arithmetic_shift_negative_no_panic ( ) {
9350+ let result = run_script ( "echo $(( 1 << -1 ))" ) . await ;
9351+ assert_eq ! ( result. exit_code, 0 ) ;
9352+ }
9353+
9354+ #[ tokio:: test]
9355+ async fn test_arithmetic_div_min_neg1_no_panic ( ) {
9356+ let result = run_script ( "echo $(( -9223372036854775808 / -1 ))" ) . await ;
9357+ assert_eq ! ( result. exit_code, 0 ) ;
9358+ }
9359+
9360+ #[ tokio:: test]
9361+ async fn test_arithmetic_mod_min_neg1_no_panic ( ) {
9362+ let result = run_script ( "echo $(( -9223372036854775808 % -1 ))" ) . await ;
9363+ assert_eq ! ( result. exit_code, 0 ) ;
9364+ }
9365+
9366+ #[ tokio:: test]
9367+ async fn test_arithmetic_overflow_add_no_panic ( ) {
9368+ let result = run_script ( "echo $(( 9223372036854775807 + 1 ))" ) . await ;
9369+ assert_eq ! ( result. exit_code, 0 ) ;
9370+ }
9371+
9372+ #[ tokio:: test]
9373+ async fn test_arithmetic_overflow_mul_no_panic ( ) {
9374+ let result = run_script ( "echo $(( 9223372036854775807 * 2 ))" ) . await ;
9375+ assert_eq ! ( result. exit_code, 0 ) ;
9376+ }
93179377}
0 commit comments