Skip to content

strided-perm: copy_into/copy_into_par call copy_nonoverlapping without an aliasing/overlap check (UB from safe code) #225

Description

@tensor4all-ai-bot

Summary

strided-perm::copy_into (strided-perm/src/copy.rs:62) and copy_into_par (:113) both
take a contiguous fast path that calls std::ptr::copy_nonoverlapping without validating
that the source and destination memory ranges are non-overlapping. copy_nonoverlapping
is Undefined Behavior if src and dst overlap, and the overlapping case is reachable
from safe code: two views built over the same backing buffer via
StridedArray::from_parts (different offsets / strides / dims) can both satisfy
is_both_contiguous while their linear ranges intersect.

Location

  • strided-perm/src/copy.rs:80-84copy_into contiguous fast path
  • strided-perm/src/copy.rs:134-138copy_into_par contiguous fast path
  • Both are public API re-exported from strided-perm/src/lib.rs:26-28
// strided-perm/src/copy.rs:81-84
if is_both_contiguous(dst_dims, dst_strides, src_strides) {
    let len = total_len(dst_dims);
    unsafe { std::ptr::copy_nonoverlapping(src_ptr, dst_ptr, len) };  // NO overlap check
    return Ok(());
}

is_both_contiguous (copy.rs:13) checks only that each view's strides are col-major or
row-major; it does not check whether the two views alias. The function validates rank and
shape but omits the aliasing precondition entirely.

Rule violated

  • REPOSITORY_RULES.md:37-39 (## Public Boundary Safety): "User-reachable
    tensor/view/kernel APIs must validate rank, shape, dtype, stride/layout, output shape,
    and aliasing preconditions before no-op shortcuts, allocation, launch planning, or
    unsafe pointer loops."
  • REPOSITORY_RULES.md:51-52 (## Unsafe And Fast-Path Boundaries): "Keep unsafe
    pointer arithmetic close to the validation that proves it safe, and cover new unsafe
    branches with focused tests."

Sibling-crate contrast

The strided-kernel counterpart (strided-kernel/src/ops_view.rs:209-235) at least has a
debug_assert! overlap check before its copy_nonoverlapping fast path. strided-perm
has no check of any kind (not even debug-only), and the debug-only check in strided-kernel
compiles out in release builds anyway.

Reproducible

Yes. Build two StridedArray over the same buffer with overlapping contiguous views,
construct a StridedViewMut and a StridedView, and call strided_perm::copy_into
(which re-exports copy::copy_into) when is_both_contiguous returns true. The
copy_nonoverlapping call fires on overlapping memory -> UB.

Proposed fix shape

  • Validate src/dst non-overlap (e.g. byte-range disjointness as in
    strided-kernel/src/ops_view.rs:212-224) at the public boundary before the fast path;
    return a crate error (or fall back to the blocked permutation path) on overlap.
  • Keep the check in release builds, not only debug_assert!.
  • Add focused overlapping-view tests for both copy_into and copy_into_par.

Optional repository rule revision

Consider making the audit rule explicit that debug-only aliasing checks are not
sufficient
at a public boundary — the "validate aliasing preconditions before unsafe
pointer loops" rule in REPOSITORY_RULES.md should apply in release builds and should be
called out for every ptr::copy_nonoverlapping / raw-pointer fast path (notably the
strided-kernel copy_into fast path, whose check is debug-gated).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions