fix(collection): guard INT64_MIN count in take/drop - #419
Conversation
take and drop turn their raw i64 count into a magnitude with `n < 0 ? -n : n`, so a count of INT64_MIN (0Nl, or the literal -9223372036854775808) hit `-INT64_MIN` — signed-integer-overflow UB (UBSan: src/ops/collection.c). It fired in every scalar-count take branch (vector, string, char, scalar, list) and in ray_drop_fn. take: reject an INT64_MIN count once, up front, before dispatching to the per-shape branches — its magnitude is unrepresentable as int64 and could never be allocated (a range error, which is what the downstream negative-capacity check already produced for the vector path, minus the UB). drop: a drop-from-end of that magnitude removes the whole collection (|n| >= len), so treat it as cut == len (empty result) — matching (drop x -N) for any N >= len. Normal positive/negative takes and drops are unchanged, and the table/dict paths recurse through these kernels. Adds regressions to collection/take.rfl (all take shapes) and collection/drop_cut_rotate_cross.rfl.
|
Reviewed (with an adversarial verification pass). The guard itself is right, and the snapshot placement (before the per-branch negations) covers every scalar path in 1. The same 2. Pre-existing, same function, memory-unsafe: range-take 3. The PR enshrines a take/drop asymmetry on the null sentinel. INT64_MIN is Minor: the guard's |
|
Thanks — reproduced all of this under UBSan against current One meta note first: the guard in this PR already landed on 1. 2. Range-take 3. take/drop null-sentinel asymmetry — agreed; going with error-in-both. Minor — agreed. Net: close #419 as superseded, and one follow-up PR covering (1) the |
|
Opened the follow-up: #424 — covers all four items (select take: negation guard across the no-sort / apply_sort_take / DAG-pushdown paths, the range- |
Summary
Tests