Skip to content

fix(cuda): refuse tensors exceeding 2^31 elements to prevent silent miscomputation - #31

Open
srivilliamsai wants to merge 5 commits into
hanzoai:mainfrom
srivilliamsai:fix/cuda-max-element-limit
Open

srivilliamsai wants to merge 5 commits into
hanzoai:mainfrom
srivilliamsai:fix/cuda-max-element-limit

Conversation

@srivilliamsai

Copy link
Copy Markdown

Summary

Per LLM.md ("Past 2^31 elements on CUDA"):

"A tensor of more than 2^31 elements miscomputes on CUDA with no error: the whole padded F32 pass at 16,384 tokens (12 heads, 3.2e9 scores) read 56% from the CPU's states. Tiles keep every score block far below it; a follow-up should refuse such a tensor rather than compute it."

CUDA kernel launches and internal indexing variables rely on signed 32-bit integers (int / i32). When total element counts exceed i32::MAX ($2^{31} - 1 = 2{,}147{,}483{,}647$), integer overflow produces negative offsets or incorrect wraparounds, leading to silent calculation corruptions with zero hardware errors.

Changes

  1. Error Variant: Added CudaError::TooManyElements { elem_count: usize, max_elements: usize }.
  2. Limit Constant & Validator: Defined pub const MAX_CUDA_ELEMENTS: usize = i32::MAX as usize; and check_elem_count(len: usize) -> Result<()> in cuda_backend and dummy_cuda_backend.
  3. Allocation & Copy Guards: Added validation checks in:
    • CudaDevice::alloc<T>
    • CudaDevice::alloc_zeros<T>
    • CudaDevice::clone_htod
    • CudaDevice::zeros_impl
    • CudaDevice::rand_uniform
    • CudaDevice::rand_normal
    • CudaDevice::alloc_uninit
    • CudaDevice::storage_from_slice
    • Tensor::broadcast_as (when target device is CUDA)
  4. Unit Tests: Added hanzo-ml/tests/cuda_limit_tests.rs covering:
    • MAX_CUDA_ELEMENTS == i32::MAX as usize
    • Boundary condition at exact limit vs limit + 1
    • The 16,384 tokens $\times$ 16,384 $\times$ 12 heads score tensor ($3{,}221{,}225{,}472$ elements) cited in LLM.md
  5. Version Bumps:
    • hanzo-ml: 0.11.114 -> 0.11.115
    • hanzo-nn: updated hanzo-ml dependency to 0.11.115
    • hanzo-transformers: updated hanzo-ml dependency to 0.11.115

Validation

  • cargo fmt --all -- --check (clean)
  • cargo clippy -p hanzo-ml -p hanzo-nn -p hanzo-transformers --tests -- -D warnings (clean, 0 warnings)
  • cargo test -p hanzo-ml --test cuda_limit_tests (2 passed)
  • cargo test -p hanzo-ml --lib (76 passed)
  • cargo test -p hanzo-nn -p hanzo-transformers --lib (48 passed)

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 12, 2026 •

Copy link
Copy Markdown
Codex Review Summary: ✅ Completed

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-12T17:48:01.787833Z 75f7661 Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6563e0aa70

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread hanzo-ml/src/cuda_backend/device.rs Outdated

fn zeros_impl(&self, shape: &Shape, dtype: DType) -> Result<CudaStorage> {
let elem_count = shape.elem_count();
check_elem_count(elem_count)?;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Check element-count arithmetic before applying the limit

On 64-bit release builds, Shape::elem_count() uses unchecked multiplication, so a CUDA shape such as (1usize << 32, 1usize << 32) wraps to 0 before this guard; Tensor::zeros then passes the limit check and requests a zero-length backing allocation for a logical 2^64-element tensor instead of returning TooManyElements. The rand_normal path likewise checks only after its odd-count + 1 can wrap. Check shape multiplication and cuRAND rounding with checked arithmetic before comparing to the cap.

AGENTS.md reference: AGENTS.md:L74-L77

Useful? React with 👍 / 👎.

@srivilliamsai

Copy link
Copy Markdown
Author

Addressed in commit 0c7bf58f:

  • Added check_shape_elem_count(shape: &Shape) -> Result<usize> using checked_mul so shape dimensions whose product overflows usize (such as (1usize << 32, 1usize << 32)) or exceeds MAX_CUDA_ELEMENTS return CudaError::TooManyElements rather than wrapping to 0.
  • Updated zeros_impl, rand_uniform, alloc_uninit, and Tensor::broadcast_as to use check_shape_elem_count.
  • Hardened rand_normal cuRAND rounding with checked_add(1) before applying check_elem_count.
  • Added unit tests in cuda_limit_tests.rs verifying checked arithmetic on the (1usize << 32, 1usize << 32) wrapping shape, multi-dim products, and boundary conditions.

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0c7bf58f0a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread hanzo-ml/src/tensor.rs
Comment on lines +2513 to +2514
if self.device().is_cuda() {
crate::cuda::check_shape_elem_count(&shape)?;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Validate broadcast shapes for the destination CUDA device

If a CPU tensor is broadcast to a shape with more than 2^31 elements and then moved to CUDA, this condition is false, so the check is skipped. Tensor::to_device copies only the backing storage and preserves the broadcast layout, allowing the oversized logical tensor to reach CUDA kernels and miscompute; validate the layout when transferring to CUDA as well.

AGENTS.md reference: AGENTS.md:L74-L77

Useful? React with 👍 / 👎.

@srivilliamsai

Copy link
Copy Markdown
Author

Addressed in commit eb22324:

• Added CUDA shape validation in Tensor::to_device:

if device.is_cuda() {
    crate::cuda::check_shape_elem_count(self.shape())?;
}

This prevents broadcasted CPU tensors with > 2^31 logical elements from transferring their broadcast layout onto CUDA devices.
• Added test test_cuda_refuses_broadcast_to_device verifying that a CPU tensor broadcast to > 2^31 elements returns an error upon to_device(&cuda_dev).
• Added test test_cuda_refuses_broadcast_as verifying broadcast directly on CUDA.

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: eb22324cd1

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

let mut count: usize = 1;
for &d in shape.dims() {
match count.checked_mul(d) {
Some(c) if c <= MAX_CUDA_ELEMENTS => count = c,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve empty CUDA shapes with zero elements

For a shape such as (MAX_CUDA_ELEMENTS + 1, 0), this branch returns TooManyElements before reaching the zero dimension, even though the complete shape has zero elements; the reversed shape (0, MAX_CUDA_ELEMENTS + 1) passes. Thus creating or transferring an empty tensor to CUDA depends on axis order. Check for a zero dimension before enforcing the product cap.

AGENTS.md reference: AGENTS.md:L74-L77

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in commit 75f7661:

Checked for zero dimensions upfront before enforcing the product cap:

if shape.dims().contains(&0) {
    return Ok(0);
}

Both trailing and leading zero shapes (such as (MAX_CUDA_ELEMENTS + 1, 0) and (0, MAX_CUDA_ELEMENTS + 1)) now consistently return Ok(0) regardless of axis order. Added unit test test_cuda_empty_shapes_with_large_dim covering these cases.

@srivilliamsai

Copy link
Copy Markdown
Author

Addressed discussion on preserving empty CUDA shapes in commit 75f7661:

• check_shape_elem_count in both CUDA and dummy backend checks for zero dimensions upfront:

if shape.dims().contains(&0) {
    return Ok(0);
}

This guarantees that empty shapes (such as (MAX_CUDA_ELEMENTS + 1, 0) and (0, MAX_CUDA_ELEMENTS + 1)) evaluate to Ok(0) regardless of dimension ordering.
• Added test test_cuda_empty_shapes_with_large_dim covering leading, trailing, and middle-zero shapes with dimensions exceeding ^{31} - 1$.
• Verified cargo fmt, cargo clippy (zero warnings), and cargo test.

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 75f76617aa

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread hanzo-ml/src/tensor.rs
.bt());
}
if self.device().is_cuda() {
crate::cuda::check_shape_elem_count(&shape)?;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Validate reshape dimensions before computing their product

With CUDA available in a default debug/test build, reshaping a zero-element CUDA tensor to (1usize << 32, 1usize << 32) panics inside ShapeWithOneHole::into_shape when it calls Shape::elem_count() before this guard runs. That makes the new test_cuda_refuses_reshape_overflow fail on GPU test runners instead of returning TooManyElements; validate the requested dimensions with checked arithmetic before into_shape or elem_count.

AGENTS.md reference: AGENTS.md:L74-L77

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant