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.
This commit is contained in:
Michael Anderson
2026-10-13 10:44:46 -05:00
parent b3d392b499
commit e9d36451e6
2 changed files with 22 additions and 1 deletions
+2 -1
View File
@@ -639,7 +639,8 @@ pub fn specs() -> Vec<CommandSpec> {
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;
+20
View File
@@ -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::<Vec<_>>();
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();