Skip to content

[BUG]: Correctness issues across the libcu++ random module #12614

Open

Description

@RAMitchell

Is this a duplicate?

Type of Bug

Something else

Component

libcu++

Describe the bug

Testing and verification of <cuda/std/random> and <cuda/random> found the following issues:

Note that these bugs are probably also in llvm.

  • generate_canonical can return 1, violating [0,1) for both float and double.
  • uniform_real_distribution can return its excluded upper bound, even when its canonical input is strictly below 1. For example, (1,2) can return 2.
  • bernoulli_distribution(1.0) can return false because of the canonical endpoint defect.
  • Binomial construction can overflow signed integers. binomial_distribution<int>(INT_MAX, 0.5) overflows in t + 1.
  • Gamma can fail to terminate for large finite shapes. For alpha = numeric_limits<T>::max() / 2, 3 * alpha - 0.75 overflows, preventing acceptance.
  • Fisher’s F can return NaN for large finite parameters. Examples include float(m=n=1e20) and double(m=n=1e200), where intermediate products produce infinity / infinity.
  • Student’s t equality ignores cached normal state. Objects can compare equal but produce different outputs from identical engines.
  • Student’s t serialization omits cached normal state, changing the sequence after restoration.
  • Normal and lognormal serialization loses precision. Fixed decimal formatting turns tiny positive standard deviations into zero and loses long double precision.
  • Bernoulli and binomial serialization loses probability precision and changes the caller’s fill character.
  • Poisson serialization loses mean precision.
  • Byte-sized uniform-integer and binomial states can fail serialization round-trips because numeric values are streamed as characters.
  • seed_seq traverses single-pass input twice. Constructing from stream iterators over "1 2 3 4 5" reproduced stored seeds [1,1,1,1,1].
  • PCG extraction changes engine state after encountering malformed input. Reading "123 x" sets failbit but commits a changed state.
  • Feistel operations evaluate undefined shifts at 64-bit sizes: 1ULL << 64 and a 32-bit right shift by 32.
  • random_bijection<signed char> can return negative indices. Narrowing precedes the range check; a ten-element mapping returned -127.
  • Check byte-state serialization when integrating [libcu++] linear_congruential_engine: fix m == 0, discard for large m, operator<< and overflow in next() #12532. The tested PR head streams unsigned-byte state as a character; state 9 becomes a tab and cannot round-trip.
  • Fix an out-of-bounds read in the discrete-distribution test helper. It evaluates samples[i + 1] before checking whether i is the final sample.

Verification combined source inspection, standalone CPU/GPU reproductions, differential testing, statistical checks, sanitizers, and selected formal models:

  • Z3 4.8.12, accessed from Python through ctypes, checked standalone SMT-LIB models of bit-vector arithmetic and IEEE-754 floating-point operations. Unconstrained symbolic searches independently found canonical-generation and uniform-scaling counterexamples for both float and double. The extended pass resolved 328 additional obligations, including proofs of the modeled gamma rejection failure and conditional Fisher NaN behavior.
  • GCC 13.3 and Clang 18, with UBSan, checked integer overflow, invalid shifts, and floating-point conversions. AddressSanitizer reproduced the test-helper heap-buffer-overflow.
  • CUDA 12.9.86, targeting sm_120, ran tests on two NVIDIA RTX PRO 6000 Blackwell Workstation Edition GPUs. Testing included native and fallback PCG implementations, 10,485,760 fallback-arithmetic comparisons, and 262,144 targeted Fisher samples, all of which returned NaN.
  • Python arbitrary-precision integers supplied independent PCG reference vectors. LCG checks used exact 128-bit reference arithmetic, while seed_seq was compared against libstdc++. The extended pass ran 1,130,496 LCG checks and 30,000 seed-sequence differential cases.
  • SciPy CDF comparisons screened 12,058,624 GPU samples across all 17 distribution families. Targeted edge tests exposed defects that these ordinary-parameter statistical checks missed.
  • State tests exercised serialization, equality, copying, reset, and explicit parameters. The existing 28 runtime test files were also attempted, with compiler-specific replays where necessary.

The primary tested snapshot was 7a915df33570ce6dd70cbaf15a5c3c53d5d8c5ec; LCG checks used the header from #12532 at 655408067f3efb949191b83fbe7ff547ad8b0a8d. The 11 newly implicated non-LCG headers were byte-for-byte identical to fetched main at the time of review.

The formal results verify manually translated expressions and selected failure paths. They do not constitute automatic verification of the complete C++ implementation, compiler lowering, or every distribution’s probability law.

How to Reproduce

.

Expected behavior

.

Reproduction link

No response

Operating System

No response

nvidia-smi output

No response

NVCC version

No response

Activity

  1. added theissue type on Nov 12, 2026
  2. roma5087 commented on Nov 12, 2026

    @roma5087

    Hi @RAMitchell, Could this issue be assigned to me?

    I'd split it into small PRs grouped by area, each with regression tests:

    1. UB and overflow: Feistel shifts, random_bijection, binomial INT_MAX, gamma and Fisher-F large parameters, and the test-helper out-of-bounds read
    2. seed_seq single-pass input and PCG extraction on malformed input
    3. Student's t cached state (equality and serialization)
    4. Serialization precision and byte-sized state round-trips
    5. generate_canonical and the uniform_real and bernoulli endpoint bugs

    Two questions before I start:

    1. For generate_canonical, do you have a preferred approach, or should I follow what libc++ or the current standard does?
    2. Should the [libcu++] linear_congruential_engine: fix m == 0, discard for large m, operator<< and overflow in next() #12532 byte-state item stay with that PR?
  3. RAMitchell commented on Nov 12, 2026

    @RAMitchell
    ContributorAuthor

    @roma5087 at this stage no, but you could go over to llvm and see if the same bugs exist and see if they want help with that.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    • Status
      Todo

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions