Skip to content

Introduce Safe Arithmetic - #8171

Merged
rapids-bot[bot] merged 14 commits into
NVIDIA:mainfrom
divyegala:safe-arithmetic
Jun 23, 2026
Merged

Introduce Safe Arithmetic#8171
rapids-bot[bot] merged 14 commits into
NVIDIA:mainfrom
divyegala:safe-arithmetic

Conversation

@divyegala

@divyegala divyegala commented May 27, 2026

Copy link
Copy Markdown
Contributor

Safe host-side integer arithmetic in cuML

Motivation

A class of host-side bugs, including silent integer overflow, underflow, divide-by-zero, and truncating narrowing conversions, can produce undersized allocations, OOM crashes, and invalid kernel launch configurations on large-batch ML workloads.

The same root cause shows up in two directions:

  • Widening too late. A product like n * batch_size is computed in int and then widened to size_t for the allocator. The widening does not undo the overflow that already happened.
  • Silent narrowing. A size_t row count, container size, or matrix dimension is assigned to an int, and the high bits are discarded. The narrowed value then propagates as a count or index into downstream code.

Kernel-side arithmetic is intentionally out of scope. All checks added here run on the host at allocation, launch, and cast sites.

The idea: fix arithmetic at the source

Rather than redesign the affected APIs, this PR adds host-side safeguards at the points where the dangerous operation happens, so silent corruption becomes a clear RAFT_FAIL with a message naming the offending operand.

Two complementary mechanisms cover the two directions:

1. Widening is now safe end-to-end

Host-side arithmetic that feeds an allocation size, CUDA launch dimension, span constructor, or host pointer offset is now performed in the wider target type (size_t / int64_t) and verified to fit that type.

This is exposed via four helpers:

Helper Purpose
ML::checked_mul<T>(a, b, ...) Variadic product; RAFT_FAIL on overflow.
ML::checked_add<T>(a, b, ...) Variadic sum / cumulative offset; RAFT_FAIL on overflow.
ML::checked_sub<T>(a, b) Difference; RAFT_FAIL on underflow.
ML::checked_div<T>(a, b) Quotient; RAFT_FAIL on divide-by-zero and signed division overflow.

2. Narrowing is now trapped, not eliminated

Many existing APIs require an int parameter even when the natural type of the value is size_t. Restructuring those APIs is a larger effort and is out of scope here.

Instead, this PR introduces:

Helper Purpose
ML::narrow_cast<T>(value) Trap if a value cannot be represented in the narrower target type.

narrow_cast is a runtime safety net, not an API redesign. The narrowing still happens at the call site, but it is now checked.

Helper implementation notes

All helpers live in cpp/include/cuml/common/checked_arithmetic.hpp under namespace ML. They are C++20 concept-constrained, constexpr, and use compiler overflow builtins where applicable.

Reviewer guidance now codified

  • cpp/agents.md adds guidance for integer arithmetic used in sizes, launches, and host indexing.
  • .coderabbit.yaml asks CodeRabbit to flag unchecked host-side arithmetic and silent narrowing in C++ paths.

Why a runtime helper instead of compiler warnings

cuML already builds with -Werror (-Wall -Werror on GCC, -Werror=all-warnings on nvcc). However, -Wall does not include the warnings that catch silent integer narrowing or implicit conversions:

Warning Catches In -Wall?
-Wnarrowing Brace-init narrowing only (int x{some_size_t}) Yes
-Wconversion Implicit value-changing conversions (int x = some_size_t) No
-Wsign-conversion Signed/unsigned conversions No

Enabling -Wconversion is the eventual right answer, but it would require an exhaustive cleanup PR series. The checked helpers and CodeRabbit rule are the bridge until then.

What is not changed

  • No public C++ or Python API signatures.
  • No CUDA kernels.
  • No new dependencies.
  • No build system changes beyond the C++20 requirement needed by the helper implementation.

Closes #8256

@divyegala
divyegala requested review from a team as code owners May 27, 2026 00:46
@divyegala
divyegala requested review from aamijar, jcrist and msarahan May 27, 2026 00:46
@divyegala divyegala added bug Something isn't working non-breaking Non-breaking change labels May 27, 2026
@coderabbitai

coderabbitai Bot commented May 27, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a checked-arithmetic header and review guidance, plus comprehensive applications of ML::checked_* and ML::narrow_cast across host-side allocation, indexing, and kernel-launch sizing code paths; includes unit tests and CMake adjustments for C++20.

Changes

Checked Arithmetic System for Host-Side Overflow Prevention

Layer / File(s) Summary
Review configuration and guidance
.coderabbit.yaml, cpp/agents.md
Adds a HIGH-priority lint rule and reviewer guidance documenting required ML::checked_* helpers, narrowing patterns, and scope constraints for host-side integer arithmetic.
Checked arithmetic library core
cpp/include/cuml/common/checked_arithmetic.hpp
New header exports ML::cuda_launch_t, integral concepts checked_target/checked_source, narrow_cast, and variadic checked_mul/checked_add/checked_sub/checked_div that widen operands, detect overflow/underflow via compiler builtins, and fail via RAFT_FAIL.
Unit tests and build system
cpp/tests/prims/checked_arithmetic.cpp, cpp/tests/CMakeLists.txt, python/cuml/CMakeLists.txt
GoogleTest unit tests validate narrow/overflow/underflow behavior; CMake enforces C++20 standard and registers test target.
ARIMA, time-series, and Holt-Winters
cpp/include/cuml/tsa/arima_common.h, cpp/src/arima/batched_arima.cu, cpp/src/arima/batched_kalman.cu, cpp/src_prims/random/make_arima.cuh, cpp/src_prims/timeSeries/fillna.cuh, cpp/src_prims/timeSeries/stationarity.cuh, cpp/src/holtwinters/...
Allocation/batch-size and differencing calculations switched to checked_mul/checked_sub with RAFT_FAIL guards; hw_narrow_size helper added for safe int64→int validation; batch dimensions narrowed with narrow_cast.
Batched linear algebra primitives
cpp/src_prims/linalg/batched/matrix.cuh
Matrix operations (difference, inv, lagged_mat, hessenberg, schur, trsyl_uplo, lyapunov) use narrow_cast for dimension narrowing and checked_mul/checked_sub for element counts and buffer allocations; loop indices converted to size_t where appropriate.
Solvers and general ML algorithms
cpp/src/glm/qn/qn.cuh, cpp/src/solver/lars_impl.cuh, cpp/src/solver/sgd.cuh, cpp/src/svm/linear.cu, cpp/src/svm/results.cuh, cpp/src/svm/sparse_util.cuh, cpp/src/svm/svc_impl.cuh, cpp/src/decisiontree/batched-levelalgo/quantiles.cuh, cpp/src/knn/knn.cu, cpp/src/randomforest/randomforest.cu, cpp/src/genetic/program.cu, cpp/src/tsa/auto_arima.cuh
Device allocations, temporary buffers, and kernel launch grid sizing use checked_mul; workspace and container sizes narrowed with narrow_cast<int>.
Explainers, HDBSCAN, TSNE, UMAP
cpp/src/explainer/kernel_shap.cu, cpp/src/explainer/tree_shap.cu, cpp/src/hdbscan/detail/..., cpp/src/hdbscan/runner.h, cpp/src/tsne/barnes_hut_tsne.cuh, cpp/src/tsne/exact_tsne.cuh, cpp/src/tsne/tsne_runner.cuh, cpp/src/umap/simpl_set_embed/...
SHAP offsets computed with checked_sub/checked_mul; SHAP/HDBSCAN dimensions narrowed with narrow_cast; HDBSCAN logging updated to size_t format; TSNE Barnes-Hut uses size_t overflow-checked node counting with bounds validation; TSNE runner and UMAP use checked_mul for KNN/chunk sizing.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • rapidsai/cuml#7808: Time-series checked arithmetic changes overlap in cpp/src/tsa/auto_arima.cuh, cpp/src_prims/timeSeries/fillna.cuh, and cpp/src_prims/timeSeries/stationarity.cuh host-side allocation/count logic.

Suggested labels

improvement

Suggested reviewers

  • jcrist
  • vyasr
  • aamijar
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Introduce Safe Arithmetic' is concise and clearly summarizes the main change—adding safe/checked integer arithmetic helpers to prevent silent overflow and narrowing bugs.
Description check ✅ Passed The description is comprehensive and directly related to the changeset, explaining the motivation, mechanisms, implementation details, and scope of the safe arithmetic changes.
Linked Issues check ✅ Passed The PR fully implements the requirements from issue #8256: adds checked host-side arithmetic (checked_mul, checked_add, checked_sub, checked_div, narrow_cast) for allocation/launch/span/indexing sizes, prevents silent overflow/underflow, and keeps kernel-side arithmetic out of scope.
Out of Scope Changes check ✅ Passed All changes are in scope: the PR adds safe arithmetic helpers, applies them to existing unsafe sites, updates configuration/documentation, and avoids modifying public APIs, CUDA kernels, or dependencies beyond the C++20 requirement.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai 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.

Actionable comments posted: 12

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
cpp/src/tsne/barnes_hut_tsne.cuh (1)

60-109: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

FOUR_NNODES and FOUR_N still use unchecked host-side multiplication.

Lines 89-90 compute 4 * nnodes and 4 * n in value_idx. These can overflow even when nnodes itself is in range, which can corrupt kernel bounds.

Suggested fix
-  const value_idx FOUR_NNODES = 4 * nnodes;
-  const value_idx FOUR_N      = 4 * n;
+  std::size_t const four_nnodes_sz = checked_mul<std::size_t>(nnodes_sz, 4);
+  std::size_t const four_n_sz      = checked_mul<std::size_t>(static_cast<std::size_t>(n), 4);
+  const value_idx FOUR_NNODES      = ML::narrow_cast<value_idx>(four_nnodes_sz);
+  const value_idx FOUR_N           = ML::narrow_cast<value_idx>(four_n_sz);

As per coding guidelines: host-side size/launch arithmetic using sub-size_t integers must use checked helpers (e.g., ML::checked_mul<size_t>(...)) before narrowing.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cpp/src/tsne/barnes_hut_tsne.cuh` around lines 60 - 109, FOUR_NNODES and
FOUR_N are computed with host-side unchecked multiplication in value_idx and can
overflow; change their computation to do size_t checked multiplication (use
checked_mul<std::size_t>(nnodes_sz, 4) and
checked_mul<std::size_t>(static_cast<std::size_t>(n), 4) or similar), verify the
resulting size_t fits into value_idx (compare to
std::numeric_limits<value_idx>::max()), then narrow/cast the checked result to
value_idx for FOUR_NNODES and FOUR_N; update any uses of FOUR_NNODES/FOUR_N to
the new safe variables.
cpp/src/explainer/kernel_shap.cu (1)

181-194: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Validate len_samples lower bound before computing nblks.

Line 181 uses nrows_X - len_samples before len_samples is validated. A negative len_samples increases nblks and can launch exact_rows_kernel past valid rows.

Suggested fix
-  nthreads = min(512, ncols);
-  nblks    = nrows_X - len_samples;
+  if (len_samples < 0 || len_samples > nrows_X) {
+    RAFT_FAIL("kernel_dataset: len_samples (%d) must be in [0, %d]", len_samples, nrows_X);
+  }
+
+  nthreads = min(512, ncols);
+  nblks    = checked_sub<IdxT>(nrows_X, static_cast<IdxT>(len_samples));
@@
-    if (len_samples > nrows_X) {
-      RAFT_FAIL("kernel_dataset: len_samples (%d) must be <= nrows_X (%d)", len_samples, nrows_X);
-    }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cpp/src/explainer/kernel_shap.cu` around lines 181 - 194, Validate
len_samples before computing nblks: ensure len_samples is checked for a valid
non-negative (and <= nrows_X) value before you compute nblks = nrows_X -
len_samples and before launching exact_rows_kernel; move the existing bounds
check (the RAFT_FAIL that compares len_samples and nrows_X) to precede the nblks
calculation/launch (or add an explicit check for len_samples < 0), so that
nblks, exact_rows_kernel, and any subsequent logic use a validated len_samples
value.
cpp/src/svm/svc_impl.cuh (1)

248-250: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Unchecked products remain in batch-sizing and transform-count calculations.

Line 248-250 and Line 279 still use raw multiplication for size/count math (n_batch * n_support * sizeof(math_t) and batch_size * n_support). These should use checked helpers as well to avoid overflow in control-flow and iterator range calculations.

Suggested fix
-  if ((size_t)n_batch * model.n_support * sizeof(math_t) > buffer_size) {
-    n_batch = buffer_size / (model.n_support * sizeof(math_t));
+  auto const kernel_bytes =
+    checked_mul<std::size_t>(static_cast<std::size_t>(n_batch),
+                             static_cast<std::size_t>(model.n_support),
+                             sizeof(math_t));
+  if (kernel_bytes > static_cast<std::size_t>(buffer_size)) {
+    auto const denom =
+      checked_mul<std::size_t>(static_cast<std::size_t>(model.n_support), sizeof(math_t));
+    n_batch = narrow_cast<int>(checked_div<std::size_t>(static_cast<std::size_t>(buffer_size), denom));
     if (n_batch < 1) n_batch = 1;
   }
@@
-          int n_elems    = batch_size * n_support;
+          int n_elems = narrow_cast<int>(checked_mul<std::size_t>(batch_size, n_support));

As per coding guidelines, host-side size arithmetic passed to allocations/launch-related paths must use checked helpers instead of unchecked int/mixed-width products.

Also applies to: 279-279

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cpp/src/svm/svc_impl.cuh` around lines 248 - 250, The batch-sizing and
transform-count checks use unchecked mixed-width products (n_batch *
model.n_support * sizeof(math_t) and batch_size * model.n_support); replace
those raw multiplications with the project's checked-size helpers (e.g.,
safe_mul/safe_mult or the existing checked helpers used elsewhere) so
comparisons against buffer_size and assignments to n_batch use overflow-checked
size_t arithmetic, and use the same checked helpers when computing transform
counts for batch_size * model.n_support to ensure no overflow before converting
to iterators/launch sizes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cpp/include/cuml/common/checked_arithmetic.hpp`:
- Around line 82-90: The magnitude check in widen_or_fail uses -static_cast<long
long>(value)/static_cast<long long>(value) which can invoke UB for extreme
signed/unsigned inputs; change the computation of abs_val to work entirely in
the unsigned/common type instead of long long: determine common =
std::common_type_t<std::make_unsigned_t<U>, std::make_unsigned_t<T>> then
compute abs_val by casting value to that unsigned/common type (for negative
values compute abs as static_cast<common>(0) - static_cast<common>(value) or
equivalent) so no long long intermediate is used; keep the subsequent comparison
to static_cast<common>(std::numeric_limits<T>::max()) and the RAFT_FAIL call
unchanged.

In `@cpp/include/cuml/tsa/arima_common.h`:
- Around line 74-90: The deallocation code must mirror the hardened size
arithmetic used during allocation to avoid integer overflow; update the
rmm_alloc.deallocate calls for mu, beta, ar, ma, sar, sma, and sigma2 to use
checked_mul<std::size_t> (and the same bs and order.* operands) and pass the
resulting std::size_t byte counts to rmm_alloc.deallocate just as allocate does,
ensuring the same casts/size computations are used for all pointers (mu, beta,
ar, ma, sar, sma, sigma2) and leaving rmm_alloc and checked_mul usage consistent
with the allocation block.

In `@cpp/src_prims/linalg/batched/matrix.cuh`:
- Line 1295: Change the declaration of batch_size to avoid silent narrowing from
std::size_t: wrap the call to A.batches() with ML::narrow_cast<int>(...) instead
of assigning directly to an int, i.e., use ML::narrow_cast to perform the
explicit checked narrowing for the variable batch_size and add the necessary ML
header/import if not already included.
- Line 1919: The pivot buffer P is undersized: allocate it with n2 * batch_size
elements (where n2 = n * n) instead of n * batch_size because
_direct_lyapunov_helper performs LU on the Kronecker-product matrix of size
n2×n2 and cublasgetrfBatched writes n2 pivots per batch; update the allocation
of rmm::device_uvector<int> P(...) to use ML::checked_mul<std::size_t>(n2,
batch_size) (or equivalent multiplication of n * n * batch_size) so P has n2 *
batch_size capacity before calling cublasgetrfBatched.

In `@cpp/src_prims/random/make_arima.cuh`:
- Around line 223-226: The shared_mem_size calculation in make_arima.cuh uses a
raw "* sizeof(double)" after doing checked_add/checked_mul, which bypasses
overflow checks; change the final multiplication to use
ML::checked_mul<std::size_t>(..., static_cast<std::size_t>(sizeof(double))) (or
wrap the entire previous checked result in ML::checked_mul with sizeof(double))
so the allocation size is computed with checked arithmetic (refer to the
variable shared_mem_size and the ML::checked_mul/checked_add calls in that
expression).

In `@cpp/src_prims/timeSeries/fillna.cuh`:
- Around line 112-115: You computed total with ML::checked_mul but later
kernels/allocations still use raw batch_size * n_obs; replace every raw
multiplication (batch_size * n_obs) used as extents/allocs/launch sizes with the
checked total variable (e.g., use total for the sizes passed to
rmm::device_uvector allocations, inclusive/exclusive scan extents, and any
kernel launch/grid/thread-count arguments) and ensure any other temporary
variables or calls that previously recomputed batch_size * n_obs now take total
(affecting indices_fwd, indices_bwd, and any calls that use
transform_op_fwd/transform_op_bwd extents) so all host-side size arithmetic is
the checked value.

In `@cpp/src_prims/timeSeries/stationarity.cuh`:
- Around line 313-318: Compute d_sD using checked/ widened integer arithmetic
before comparing to n_obs: cast d, s, and D to the unsigned/index type IdxT (or
a wider integer like int64_t) and perform a checked multiply for s*D (verify D
== 0 or product/s == D or check s <= max/ D) and a checked addition for d +
(s*D); if either operation would overflow, RAFT_FAIL with a clear message; only
after successful checked arithmetic assign d_sD and use it in the existing
bounds check and to compute n_obs_diff. Ensure you reference the symbols d, s,
D, d_sD, n_obs, RAFT_FAIL and n_obs_diff when making the changes.

In `@cpp/src/arima/batched_arima.cu`:
- Around line 131-132: Replace the raw multiplication used when resizing
fc_buffer with the checked arithmetic helper to avoid overflow: change the
fc_buffer.resize call that currently uses num_steps * batch_size to use
checked_mul<std::size_t>(num_steps, batch_size) (analogous to the existing
exog_fut_buffer.resize call which uses checked_mul<std::size_t>(num_steps,
order.n_exog, batch_size)); ensure the same stream argument is preserved.
- Around line 979-984: Compute d + s*D using the checked arithmetic helpers
instead of raw int ops to prevent overflow: replace the raw int computation of
d_sD with a checked multiplication of order.s and order.D and then a checked
addition with order.d (e.g., use checked_mul<int>(order.s, order.D) and
checked_add<int>(order.d, ...)), then use that checked d_sD when comparing to
n_obs and when calling checked_sub to produce diff_rows so all shape/allocation
math is done with overflow-checked values (referencing d_sD, order.d, order.s,
order.D, checked_mul, checked_add, n_obs, checked_sub, diff_rows).

In `@cpp/src/holtwinters/internal/hw_decompose.cuh`:
- Around line 260-269: The code currently computes end as raw start_periods *
frequency and computes batch_trend_n as unchecked batch_size * trend_len; change
these to use the checked multiplication/subtraction helpers and fail cleanly on
overflow: compute end via ML::checked_mul<int>(start_periods, frequency) (or
equivalent checked type) and validate it before using RAFT_FAIL, compute
trend_len using a checked subtraction from end and filter_size, and compute
batch_trend_n with ML::checked_mul<std::size_t>(batch_size, trend_len); keep
using RAFT_FAIL to report overflow errors if the checked helpers indicate
overflow.

In `@cpp/src/solver/lars_impl.cuh`:
- Around line 1114-1117: The kernel launch computes the grid size with int
arithmetic (n_active * ld_X) which can overflow; change the multiplication to
use widened/checked arithmetic like checked_mul<std::size_t>(n_active, ld_X)
(same as used for active_count) and compute the ceildiv on that size_t product
(use raft::ceildiv with size_t) then validate/cast the resulting grid size to
the int TPB-compatible type before launching get_vecs<<<...>>>; update the
launch expression that currently uses raft::ceildiv(n_active * ld_X, TPB) to use
the checked/widened product and checked ceil division to avoid overflow when
computing the grid dimension for get_vecs.

In `@cpp/src/tsa/auto_arima.cuh`:
- Around line 208-213: cumul_count is the checked multiplication result used for
allocation but the subsequent scan/for_each uses an unchecked product
(batch_size * n_sub) which may overflow; update the scan/for_each extent to use
the checked cumul_count (and sizeof where appropriate) and ensure any other
host-side size/index computations in this block use
ML::checked_mul<std::size_t>(batch_size, n_sub) (i.e., replace direct uses of
batch_size * n_sub with cumul_count or the checked multiplication) so d_cumul
and the scan bounds are consistent and safe.

---

Outside diff comments:
In `@cpp/src/explainer/kernel_shap.cu`:
- Around line 181-194: Validate len_samples before computing nblks: ensure
len_samples is checked for a valid non-negative (and <= nrows_X) value before
you compute nblks = nrows_X - len_samples and before launching
exact_rows_kernel; move the existing bounds check (the RAFT_FAIL that compares
len_samples and nrows_X) to precede the nblks calculation/launch (or add an
explicit check for len_samples < 0), so that nblks, exact_rows_kernel, and any
subsequent logic use a validated len_samples value.

In `@cpp/src/svm/svc_impl.cuh`:
- Around line 248-250: The batch-sizing and transform-count checks use unchecked
mixed-width products (n_batch * model.n_support * sizeof(math_t) and batch_size
* model.n_support); replace those raw multiplications with the project's
checked-size helpers (e.g., safe_mul/safe_mult or the existing checked helpers
used elsewhere) so comparisons against buffer_size and assignments to n_batch
use overflow-checked size_t arithmetic, and use the same checked helpers when
computing transform counts for batch_size * model.n_support to ensure no
overflow before converting to iterators/launch sizes.

In `@cpp/src/tsne/barnes_hut_tsne.cuh`:
- Around line 60-109: FOUR_NNODES and FOUR_N are computed with host-side
unchecked multiplication in value_idx and can overflow; change their computation
to do size_t checked multiplication (use checked_mul<std::size_t>(nnodes_sz, 4)
and checked_mul<std::size_t>(static_cast<std::size_t>(n), 4) or similar), verify
the resulting size_t fits into value_idx (compare to
std::numeric_limits<value_idx>::max()), then narrow/cast the checked result to
value_idx for FOUR_NNODES and FOUR_N; update any uses of FOUR_NNODES/FOUR_N to
the new safe variables.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: c45394bc-58e1-4586-9939-9ed135ac614b

📥 Commits

Reviewing files that changed from the base of the PR and between 91b5386 and ea1a99d.

📒 Files selected for processing (36)
  • .coderabbit.yaml
  • cpp/agents.md
  • cpp/include/cuml/common/checked_arithmetic.hpp
  • cpp/include/cuml/tsa/arima_common.h
  • cpp/src/arima/batched_arima.cu
  • cpp/src/decisiontree/batched-levelalgo/quantiles.cuh
  • cpp/src/explainer/kernel_shap.cu
  • cpp/src/explainer/tree_shap.cu
  • cpp/src/genetic/program.cu
  • cpp/src/glm/qn/qn.cuh
  • cpp/src/hdbscan/detail/condense.cuh
  • cpp/src/hdbscan/detail/predict.cuh
  • cpp/src/hdbscan/detail/soft_clustering.cuh
  • cpp/src/hdbscan/detail/utils.h
  • cpp/src/hdbscan/runner.h
  • cpp/src/holtwinters/internal/hw_decompose.cuh
  • cpp/src/holtwinters/internal/hw_optim.cuh
  • cpp/src/holtwinters/runner.cuh
  • cpp/src/knn/knn.cu
  • cpp/src/randomforest/randomforest.cu
  • cpp/src/solver/lars_impl.cuh
  • cpp/src/solver/sgd.cuh
  • cpp/src/svm/linear.cu
  • cpp/src/svm/results.cuh
  • cpp/src/svm/sparse_util.cuh
  • cpp/src/svm/svc_impl.cuh
  • cpp/src/tsa/auto_arima.cuh
  • cpp/src/tsne/barnes_hut_tsne.cuh
  • cpp/src/tsne/exact_tsne.cuh
  • cpp/src/tsne/tsne_runner.cuh
  • cpp/src/umap/simpl_set_embed/algo.cuh
  • cpp/src/umap/simpl_set_embed/optimize_batch_kernel.cuh
  • cpp/src_prims/linalg/batched/matrix.cuh
  • cpp/src_prims/random/make_arima.cuh
  • cpp/src_prims/timeSeries/fillna.cuh
  • cpp/src_prims/timeSeries/stationarity.cuh

Comment thread cpp/include/cuml/common/checked_arithmetic.hpp Outdated
Comment thread cpp/include/cuml/tsa/arima_common.h
Comment thread cpp/src_prims/linalg/batched/matrix.cuh Outdated
Comment thread cpp/src_prims/linalg/batched/matrix.cuh Outdated
Comment thread cpp/src_prims/random/make_arima.cuh Outdated
Comment thread cpp/src/arima/batched_arima.cu
Comment thread cpp/src/arima/batched_arima.cu Outdated
Comment thread cpp/src/holtwinters/internal/hw_decompose.cuh
Comment thread cpp/src/solver/lars_impl.cuh Outdated
Comment thread cpp/src/tsa/auto_arima.cuh
@divyegala
divyegala requested a review from a team as a code owner May 27, 2026 01:32
@divyegala
divyegala requested a review from vyasr May 27, 2026 01:32
@github-actions github-actions Bot added Cython / Python Cython or Python issue CMake labels May 27, 2026
Comment thread cpp/include/cuml/common/checked_arithmetic.hpp
@copy-pr-bot

copy-pr-bot Bot commented Jun 10, 2026

Copy link
Copy Markdown

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.

@csadorf

This comment has been minimized.

@csadorf

This comment has been minimized.

@csadorf csadorf left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Approving this with the expectation that this is a temporary band-aid, and that we work towards enabling the appropriate compiler warnings after all necessary cleanups.

@dantegd dantegd left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The change makes sense to me, I think the only thing missing is that yhe ARIMA backing-buffer sizing path still has unchecked int products feeding offsets and the final allocation size, so the PR doesn’t make ARIMA safe end-to-end yet. Examples in cpp/include/cuml/tsa/arima_common.h: append_buffer(T_dense, rd * rd * batch_size);

We could convert append_buffer/buf_offsets to take checked element counts and also check the internal n_elem * sizeof(ValType) + ALIGN - 1 arithmetic, though I'd treat that as a follow up and not blocker for the PR.

@csadorf

This comment has been minimized.

@divyegala

Copy link
Copy Markdown
Contributor Author

/merge

@rapids-bot
rapids-bot Bot merged commit b71896c into NVIDIA:main Jun 23, 2026
102 checks passed
@csadorf

csadorf commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

The change makes sense to me, I think the only thing missing is that yhe ARIMA backing-buffer sizing path still has unchecked int products feeding offsets and the final allocation size, so the PR doesn’t make ARIMA safe end-to-end yet. Examples in cpp/include/cuml/tsa/arima_common.h: append_buffer(T_dense, rd * rd * batch_size);

We could convert append_buffer/buf_offsets to take checked element counts and also check the internal n_elem * sizeof(ValType) + ALIGN - 1 arithmetic, though I'd treat that as a follow up and not blocker for the PR.

@coderabbitai Please create a follow-up issue for this.

@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

repo="rapidsai/cuml"
pr_number="8171"
requester="csadorf"

# Try to locate the relevant PR comment URL for backlinking.
comment_url="$(
  gh api "repos/${repo}/issues/${pr_number}/comments" --paginate \
    --jq '.[] | select((.user.login == "csadorf") and (.body | contains("ARIMA backing-buffer sizing path"))) | .html_url' \
    | tail -n 1
)"

if [[ -z "${comment_url}" ]]; then
  comment_url="https://github.com/${repo}/pull/${pr_number}"
fi

title="Follow up: harden ARIMA backing-buffer sizing with checked arithmetic"

body="$(cat <<EOF
Requested by @${requester} as a follow-up from PR #${pr_number}.

## Summary

PR #${pr_number} introduces checked host-side arithmetic across many allocation, launch, span, and narrowing paths. One remaining ARIMA path still has unchecked integer products feeding backing-buffer offsets and final allocation sizing in:

- \`cpp/include/cuml/tsa/arima_common.h\`

Example pattern:

- \`append_buffer(T_dense, rd * rd * batch_size);\`

The follow-up should harden the ARIMA backing-buffer sizing path end-to-end.

## Rationale

Unchecked \`int\` products can overflow before being widened, which can produce incorrect buffer offsets or undersized final allocations. This undermines the safe-arithmetic goal for large-batch ARIMA workloads.

## Proposed work

- Convert \`append_buffer\` / \`buf_offsets\` sizing paths to take checked element counts.
- Replace unchecked products such as \`rd * rd * batch_size\` with checked arithmetic using the new helpers from \`cpp/include/cuml/common/checked_arithmetic.hpp\`.
- Check internal byte-size/alignment arithmetic, including \`n_elem * sizeof(ValType) + ALIGN - 1\`, for overflow.
- Preserve current behavior for valid workloads while failing deterministically with a clear error on overflow.

## Acceptance criteria

- ARIMA backing-buffer element counts are computed with checked arithmetic before being used for offsets or allocation sizes.
- Byte-size and alignment calculations inside the buffer sizing helper are overflow-checked.
- Existing ARIMA tests continue to pass.
- Add or update targeted tests if practical to cover overflow/failure behavior for the sizing helper.

## Backlinks

- PR: https://github.com/${repo}/pull/${pr_number}
- Discussion comment: ${comment_url}
EOF
)"

issue_url="$(gh issue create \
  --repo "${repo}" \
  --title "${title}" \
  --body "${body}" \
  --assignee "${requester}")"

printf '%s\n' "${issue_url}"

Length of output: 2653



@csadorf Created the follow-up issue: #8284

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

Labels

bug Something isn't working CMake CUDA/C++ Cython / Python Cython or Python issue non-breaking Non-breaking change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Harden host-side integer arithmetic

7 participants