PyTorch — CUDA Kernels & cdist
I contribute to PyTorch, focusing on CUDA kernel correctness and on the boundary cases where 32-bit index arithmetic stops being safe.
Merged contribution
PR #198452 — Fix int32 overflow in the cdist CUDA backward kernel · Merged September 25, 2026 (+21 / −4 across 2 files). Fixes #128791.
torch.cdist(x1, x2, p=1) backward raised CUDA error: an illegal memory access was encountered on an H200 once either of two quantities reached 2³¹:
(b, r, m) = (2, 8191, 32) OK
(b, r, m) = (2, 8192, 32) illegal memory access # r1 * r2 * m = 2^31
(b, r, m) = (31, 8192, 1) OK
(b, r, m) = (32, 8192, 1) illegal memory access # dist.numel() = 2^31
The forward half of this issue had already been fixed by #188006, which capped the cdist/pdist forward grid and grid-strides over the outputs. The backward kernel still failed at the same scale, because cdist_backward_kernel_cuda_impl computed its indices in int while the forward kernel already used int64_t for the same quantities:
y = (blockIdx.y * gridDim.z + blockIdx.z) * blockDim.y + threadIdx.y. The launcher rounds the grid up, so oncedist.numel()reaches 2³¹ the extra threads havey >= 2^31. That wraps negative, passes they >= countguard, and readsgrad[y]/dist[y]out of bounds.l_size = r_size * mis the per-batch stride of the(batch, r2, r1, m)scratch buffer. Whenr1 * r2 * mreaches 2³¹ it wraps, sobuffer + l * l_sizepoints before the buffer for every batchl >= 1.
The fix makes y, l, k and l_size int64_t, casting at the first multiply. They are computed once per thread before the inner loop, so the loop keeps its existing pointer-increment form. I considered templating the kernel on index_t and dispatching on canUse32BitIndexMath instead, but rejected it: that doubles the number of kernel instantiations to save a few 64-bit multiplies per thread, and it would leave the backward kernel inconsistent with the forward one, which uses int64_t unconditionally. The launch configuration itself was already fine — grid_y/grid_z are split to stay under 65535 — so it only overflows when dist.numel() exceeds roughly 6.9e10.
New test test_cdist_backward_large_index covers both overflows (largeTensorTest('32GB'); measured peaks 16.1 GiB and 24.3 GiB). It compares the last batch against that same batch computed on its own, which stays within int32; integer-valued grad with p=1 makes the reductions exact, so the comparison is bitwise rather than order-dependent. On 1× H200 with CUDA 12.8: the targeted test fails on the nightly and passes with the fix, 29 pass on the pdist or cdist subset, 80 pass across test_ops.py, and compute-sanitizer --tool memcheck reports 0 errors on both reproducers. Backward timing is unchanged (differences within run-to-run noise across eight shape/p combinations).