From e9d36451e6bc3eb6060789957c4159bda42ccab9 Mon Sep 17 00:00:00 2001 From: Michael Anderson Date: Tue, 13 Oct 2026 07:11:00 -0500 Subject: [PATCH] Never crash: Fix overflow in mask.move with a huge delta Saturate index + delta so any delta just moves the mask to the top or bottom. --- crates/engine/src/cmd/masks.rs | 3 ++- crates/engine/src/tests.rs | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/crates/engine/src/cmd/masks.rs b/crates/engine/src/cmd/masks.rs index ae8b862b6..c53314c10 100644 --- a/crates/engine/src/cmd/masks.rs +++ b/crates/engine/src/cmd/masks.rs @@ -639,7 +639,8 @@ pub fn specs() -> Vec { let i = find(&mut masks, mid, "mask.move")?; let to = match (p.get("to").and_then(Value::as_i64), p.get("delta").and_then(Value::as_i64)) { (Some(t), _) => t, - (None, Some(d)) => i as i64 + d, + // (an agent's delta can be anything: saturate rather than overflow) + (None, Some(d)) => (i as i64).saturating_add(d), (None, None) => return Err(bad("mask.move", "give `to` (index) or `delta`")), } .clamp(0, masks.len() as i64 - 1) as usize; diff --git a/crates/engine/src/tests.rs b/crates/engine/src/tests.rs index 6aad89b31..d0d6d4340 100644 --- a/crates/engine/src/tests.rs +++ b/crates/engine/src/tests.rs @@ -402,6 +402,26 @@ fn paste_selected_settings_pastes_only_chosen_copied_groups() { assert_eq!(s.develop_of(ids[1]).unwrap().color.vibrance, 30.0); } +/// `mask.move` with a delta near `i64::MAX` overflowed `index + delta` (a panic wherever overflow +/// checks are on); extreme deltas and indices now move the mask to the bottom or the top. +#[test] +fn mask_move_with_extreme_deltas() { + let mut s = demo(); + for kind in ["linear", "radial", "brush"] { + s.execute("mask.add", &json!({"kind": kind})).unwrap(); + } + let ids = |s: &Session| active_dev(s).masks.iter().map(|m| m.id).collect::>(); + let [a, b, c] = ids(&s)[..] else { panic!("{:?}", ids(&s)) }; + s.execute("mask.move", &json!({"id": b, "delta": i64::MAX})).unwrap(); + assert_eq!(ids(&s), [a, c, b], "to the bottom"); + s.execute("mask.move", &json!({"id": b, "delta": i64::MIN})).unwrap(); + assert_eq!(ids(&s), [b, a, c], "to the top"); + s.execute("mask.move", &json!({"id": c, "to": i64::MIN})).unwrap(); + assert_eq!(ids(&s), [c, b, a]); + s.execute("mask.move", &json!({"id": c, "to": i64::MAX})).unwrap(); + assert_eq!(ids(&s), [b, a, c]); +} + #[test] fn masks_reorder_and_duplicate_and_invert() { let mut s = demo();