Skip to content

Fix OOM state-desync in generated array-member setters#2414

Open
rparolin wants to merge 1 commit into
NVIDIA:mainfrom
rparolin:fix/array-setter-oom-strong-guarantee
Open

Fix OOM state-desync in generated array-member setters#2414
rparolin wants to merge 1 commit into
NVIDIA:mainfrom
rparolin:fix/array-setter-oom-strong-guarantee

Conversation

@rparolin

@rparolin rparolin commented Jul 23, 2026

Copy link
Copy Markdown
Collaborator

Prepared with the assistance of an automated agent (Claude Code / Claude Opus 4.8). Please review carefully.

Summary

The generated setters for list-valued struct members (CUlaunchConfig.attrs, CUDA_MEM_ALLOC_NODE_PARAMS.accessDescs, and ~27 siblings) freed the old heap buffer before allocating the new one. On an allocation failure they raised MemoryError before updating the cached length and the C-struct pointer, leaving the wrapper with a freed buffer but a stale length and a dangling _pvt_ptr[0].<m>:

  • a later same-length assignment took the no-resize path and memcpy'd through the freed/NULL buffer -> near-NULL segfault, and
  • getPtr()-then-CUDA read the dangling pointer -> use-after-free.

Fix

Allocate and fill the new buffer first, and free/swap the old one only once the resize is known to succeed -- a strong exception guarantee. A failed (over)allocation now leaves the object completely unchanged.

Regeneration

The change is in the cybind generator template (legacy_cython_gen); this PR is the full regenerated output of:

python -m cybind --generate-module "cuda.*" --output-dir <checkout>/cuda_bindings --ctk-target-version 13.3

Besides the setter fix (18 driver + 11 runtime setters), the regen also picks up docstring-only cleanups that have landed in cybind since the last sync (e.g. nvml.pyx, runtime.pxd, cyruntime.pxd, runtime.rst). These are formatting-only -- no API, signature, or behavior changes -- and are included because they are part of what the canonical regeneration command produces.

Verification

  • Pre-fix build: reproducer SIGSEGVs on the same-length re-set after a failed resize.
  • Post-fix (regenerated) build: survives; object state intact.
  • New subprocess regression test test_array_setter_preserves_state_on_failed_resize forces the failed resize via an overflowing __len__ (so calloc overflows size_t -> NULL, no real host OOM needed). Passes; the two existing Fix a double-free bug #2112 array-setter regression tests still pass.

Context

Addresses NVBUG 6268871. Companion cybind template change is under review separately; this PR exists so the regenerated bindings get full CI.

@rparolin rparolin added bug Something isn't working P2 Low priority - Nice to have cuda.bindings Everything related to the cuda.bindings module labels Jul 23, 2026
@github-actions

Copy link
Copy Markdown

@rparolin
rparolin force-pushed the fix/array-setter-oom-strong-guarantee branch from 2ff5f72 to 1136fda Compare July 23, 2026 22:10
@rparolin rparolin changed the title Fix OOM state-desync in generated array-member setters (Glasswing V1.1) Fix OOM state-desync in generated array-member setters Jul 23, 2026
@rparolin
rparolin requested a review from mdboom July 23, 2026 22:15
The generated setters for list-valued struct members (CUlaunchConfig.attrs,
CUDA_MEM_ALLOC_NODE_PARAMS.accessDescs, ...) freed the old buffer, then
allocated the new one; on allocation failure they raised *before* updating
the cached length and the C-struct pointer. That left the wrapper with a
freed buffer but a stale length and a dangling _pvt_ptr[0].<m>:

  - a later same-length assignment took the no-resize path and memcpy'd
    through the freed/NULL buffer -> near-NULL segfault, and
  - getPtr()-then-CUDA read the dangling pointer -> use-after-free.

The setters now allocate and fill a new buffer first, and free/swap the old
one only once the resize is known to succeed, so a failed (over)allocation
leaves the object completely unchanged (strong exception guarantee).

The fix is made in the cybind generator template (legacy_cython_gen). These
files are the full regenerated output of

    python -m cybind --generate-module "cuda.*" \
        --output-dir <checkout>/cuda_bindings --ctk-target-version 13.3

so besides the setter change (18 driver + 11 runtime setters) the regen also
picks up accumulated docstring-only cleanups that have landed in cybind since
the last sync (no API/behavior change). Adds a subprocess regression test that
forces the failed resize via an overflowing __len__.

Addresses Glasswing finding V1.1 (NVBUG 6268871).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@rparolin
rparolin force-pushed the fix/array-setter-oom-strong-guarantee branch from 1136fda to 497d561 Compare July 23, 2026 22:17
@rparolin rparolin self-assigned this Jul 23, 2026
CUDA_EMULATION_STRATEGY_EAGER
ctypedef cudaEmulationStrategy_t cudaEmulationStrategy

cdef extern from 'library_types.h':

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.

Looks like maybe there is a non-deterministic ordering bug in the generator here. Not to fix now, but I will file a bug over there.

self._pvt_ptr[0].paramsArray = _paramsArray_new
else:
for idx in range(len(val)):
string.memcpy(&self._paramsArray[idx], (<cudaExternalSemaphoreWaitParams>val[idx])._pvt_ptr, sizeof(cyruntime.cudaExternalSemaphoreWaitParams))

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.

We really should refactor this out into a function to remove all this duplication, but the legacy generator makes that hard and probably not worth the effort for something that's being deprecated.

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

Labels

bug Something isn't working cuda.bindings Everything related to the cuda.bindings module P2 Low priority - Nice to have

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants