Skip to content

fix(core): honour DLTensor.byte_offset in StridedMemoryView.from_dlpack - #2576

Open
LeSingh1 wants to merge 1 commit into
NVIDIA:mainfrom
LeSingh1:memoryview-dlpack-byte-offset
Open

fix(core): honour DLTensor.byte_offset in StridedMemoryView.from_dlpack#2576
LeSingh1 wants to merge 1 commit into
NVIDIA:mainfrom
LeSingh1:memoryview-dlpack-byte-offset

Conversation

@LeSingh1

@LeSingh1 LeSingh1 commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Problem

view_as_dlpack builds the view pointer from data alone (_memoryview.pyx:1105):

    buf.ptr = <intptr_t>(dl_tensor.data)

byte_offset is a mandatory DLTensor field (_dlpack.pxd:55), and a producer may legally leave the allocation base in data and encode a slice in byte_offset. The sibling capsule-consuming helper in the same file already handles it (_smv_from_dlpack_capsule, _memoryview.pyx:866):

    view.ptr = <intptr_t>(dl_tensor.data) + <intptr_t>(dl_tensor.byte_offset)

view_as_dlpack is the public consumer path — StridedMemoryView.from_dlpack() (:228), StridedMemoryView.from_any_interface() (via :283), and the deprecated StridedMemoryView(obj, stream_ptr) constructor (:194) all funnel through it. Nothing downstream recovers the offset: layout_from_dlpack() (:1305) reads only ndim / shape / strides / dtype.

So for any exporter with byte_offset != 0, view.ptr is short by exactly byte_offset bytes, and every consumer that reads ptr alone operates on the wrong memory with no exception raised:

  • as_tensor_map() (_tensor_map.pyx:599)
  • the __dlpack__ re-export (_smv_setup_dl_tensor_common:740-741, which writes byte_offset = 0 and uses ptr as data — so the offset is lost permanently on a round-trip)
  • the 16-byte alignment check at _tensor_map.pyx:391

Fix

One line, matching the sibling helper.

Tests

test_from_dlpack_honours_byte_offset in cuda_core/tests/test_utils_dlpack.py, built on the ctypes DLTensor / DLManagedTensor structures already defined in that file and following test_from_dlpack_null_deleter_dealloc's pattern: export a real capsule, rewrite it in place to describe src[1:] as data = base, byte_offset = itemsize, shape[0] = n - 1, then import it and assert ptr == src.ctypes.data + itemsize. Parametrised over the versioned and unversioned capsule flavours. It uses the CPU device path (stream_ptr=-1), like the neighbouring tests, so it needs no GPU.

What I ran

Environment: macOS, no CUDA driver and no CUDA toolkit, so cuda.core cannot be built or imported here.

  • Did not run: the new test, or anything else under cuda_core/tests/ — they need a built cuda.core.
  • Ran: python -m py_compile, ruff check, ruff format --check on cuda_core/tests/test_utils_dlpack.py — clean, no new findings against a main baseline.
  • Checked by inspection: byte_offset appears at exactly four places in the tree (_dlpack.pxd:55 declaration; _dlpack.pyx:88 and _memoryview.pyx:741 both on the export side, where the offset is folded into data and the field set to 0; _memoryview.pyx:866 on the capsule import side). _memoryview.pyx:1105 was the only import site missing it, and the export side's byte_offset = 0 convention is exactly why folding it into ptr on import is the correct fix rather than propagating the field.
  • Checked: no other open PR touches _memoryview.pyx; Test using pytest-run-parallel and related fixups in the tests #2194 touches test_utils_dlpack.py but not this region.

`view_as_dlpack` -- the path behind `StridedMemoryView.from_dlpack()`,
`StridedMemoryView.from_any_interface()` on a DLPack producer, and the
deprecated `StridedMemoryView(obj, stream_ptr)` constructor -- built the view
pointer from `data` alone:

    buf.ptr = <intptr_t>(dl_tensor.data)

`byte_offset` is a mandatory DLTensor field, and a producer may legally leave
the allocation base in `data` and encode a slice in `byte_offset`. The
sibling capsule-consuming helper in the same file already does the right thing
(`_smv_from_dlpack_capsule`, _memoryview.pyx:866):

    view.ptr = <intptr_t>(dl_tensor.data) + <intptr_t>(dl_tensor.byte_offset)

Nothing downstream recovers the offset -- `layout_from_dlpack` reads only
ndim/shape/strides/dtype -- so `view.ptr` was short by exactly `byte_offset`
bytes, and every consumer that reads `ptr` alone (`as_tensor_map()`, the
`__dlpack__` re-export, which writes `byte_offset = 0` and uses `ptr` as
`data`, and the 16-byte alignment check in _tensor_map.pyx) silently operated
on the wrong memory. No exception was raised.

Test builds a real capsule, rewrites it to describe src[1:] as base +
byte_offset, and asserts `ptr`, for both the versioned and unversioned
capsule flavours. It uses the CPU device path, so it needs no GPU.
@copy-pr-bot

copy-pr-bot Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@github-actions github-actions Bot added the cuda.core Everything related to the cuda.core module label Aug 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cuda.core Everything related to the cuda.core module

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant