NVIDIA / NVIDIA/cuvs

[BUG] Rust: ManagedTensor borrows host ndarray shape storage; index outliving the host array reads a dangling pointer at Drop

Open
#2,232 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Cuda
Stars
854
Forks
236
Avg merge
3d 3h
Merged PRs (30d)
62

Description

Describe the bug

In the Rust crate, ManagedTensor::from(&ndarray) stores arr.shape().as_ptr() — a pointer into the host ndarray's owned dimension storage — and .to_device() copies the DLTensor struct including that borrowed shape pointer. Index types that retain a non-owning device view of their build dataset (observed with brute_force::Index, which keeps _dataset) therefore alias the host array's dims. If the host Array2 is dropped (or moved out of scope) while the index is alive, the index's Drop reads the dangling shape pointer — observed as SIGABRT via multiply-overflow in dlpack.rs::dl_tensor_bytes.

Steps/Code to reproduce bug

let index = {
    let dataset: ndarray::Array2<f32> = /* ... */;
    let dev = ManagedTensor::from(&dataset).to_device(&res)?;
    brute_force::Index::build(&res, &params, &dev)?
    // dataset dropped here; index._dataset.shape now dangles
};
// index.drop() -> dl_tensor_bytes reads garbage dims -> SIGABRT

Nothing in the type system prevents this; it compiles cleanly.

Expected behavior

Either the borrow is impossible to outlive, or the tensor owns its shape.

Recommended fix

Make ManagedTensor own its shape/strides storage: copy the dims into an internal Box<[i64]> at construction and point DLTensor.shape at that owned buffer (likewise strides). This is API-compatible, costs a few words per tensor, and removes the hazard for every index type at once. The stricter alternative — threading a lifetime parameter (ManagedTensor<'a> with PhantomData<&'a ()>) — is sound but viral across every index API signature, so the owned-shape approach seems the better fit. We hit this while adding serialize bindings (#2231) and worked around it test-side with drop ordering; happy to submit the owned-shape fix as a PR if the approach sounds right.

Environment details

Rust crate at main (78135be4), conda libcuvs 26.06, CUDA 13.1, RTX 4000.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Trace ManagedTensor::from(&ndarray) through .to_device() and brute_force::Index::build, then inspect dlpack.rs::dl_tensor_bytes. Verify how shape and strides storage are retained across device copies and reproduce the index drop after the host array leaves scope. Done means the tensor owns the relevant storage and the dangling-pointer failure no longer occurs.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
backend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.