Skip to content

cudaPackages.cudnn-frontend, python3Packages.transformer-engine: backport fixes for crashes at process exit - #577950

Draft
ConnorBaker wants to merge 2 commits into
NixOS:masterfrom
ConnorBaker:cudnn-frontend-handle-map-teardown
Draft

ConnorBaker wants to merge 2 commits into
NixOS:masterfrom
ConnorBaker:cudnn-frontend-handle-map-teardown

Conversation

@ConnorBaker

@ConnorBaker ConnorBaker commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

cudnn-frontend 1.31.0, on master since 2026-10-09, keeps execution plans alive for the CUDA graphs recorded from them (NVIDIA/cudnn-frontend#1195). Those plans are released through get_cuda_symbol(), whose library handle map is a function-local static. A static constructed before that map is destroyed after it at exit, so when it releases such a plan, the lookup reads freed memory. TransformerEngine's fused-attention graph cache is one such static, and its processes can crash with SIGSEGV at exit (NVIDIA/cudnn-frontend#1486).

Upstream fixed this on develop in NVIDIA/cudnn-frontend#1565 (NVIDIA/cudnn-frontend@473a2c8), which never destroys the map or its mutex. No release has it yet; 1.31.0 is the latest. This applies that commit with fetchpatch.

python3Packages.nvidia-cudnn-frontend builds its extension from the same source and already reuses the package's src and postPatch, so it now takes patches as well. python3Packages.transformer-engine compiles against cudaPackages.cudnn-frontend's headers, so it gets the fix through them.

TransformerEngine has a second crash at exit, in its own code, which this PR also fixes. cuda_driver::call caches driver entry points in function-local statics. The static KernelManager that owns its NVRTC-compiled kernels (LayerNorm, RMSNorm, softmax and others) is usually constructed before those caches, so it's destroyed after them, and its destructor unloads the kernels' modules through the destroyed caches. The second commit applies both commits of NVIDIA/TransformerEngine#3733 (open) with fetchpatch. The first never destroys those caches. The second never destroys the KernelManager or the fused-attention graph caches, so neither calls CUDA at exit, and TransformerEngine no longer releases retained cuDNN plans at exit. Only the library changes are taken; the rest of those commits is tests and CI wiring.

This also adds cudaPackages.tests.cudnn-frontend.teardown, built with AddressSanitizer. It resolves a CUDA symbol through get_cuda_symbol() from the destructor of a static constructed before the map. Resolving a symbol only loads libcudart, so it needs no GPU.

Testing

  • cudaPackages.cudnn-frontend (all outputs, including samples and tests) and python3Packages.nvidia-cudnn-frontend build on x86_64-linux and aarch64-linux, and the installed cudnn_frontend_shim.h has the fix.
  • cudaPackages.tests.cudnn-frontend.teardown passes on x86_64-linux and aarch64-linux.
    • Against the same headers without the patch, it fails on both with AddressSanitizer: heap-use-after-free at cudnn_frontend_shim.h:163, the map lookup in get_cuda_symbol().
    • The read comes from the static's destructor during exit, on memory freed by the map's destructor.
  • On x86_64-linux with cudaSupport = true and cudaCapabilities = [ "8.9" ], I built python3Packages.transformer-engine from this branch, with JAX and PyTorch. That includes Torch 2.14.1, the JAX CUDA plugin and PJRT, onnxruntime and torchvision. I also built its passthru.tests: withCusolvermp, withMpi and withNvshmem; withJax, withPytorch and withOutNcclEp are the default derivation.
  • On an RTX 4090, all with the same Torch, I compared TransformerEngine built from three commits: the parent (3ec6363), the cudnn-frontend commit alone (2c6636c), and this branch.
    • A fused-attention script captured in a CUDA graph exited with 139 in 6 of 6 runs from the parent (3 plain, 3 with MALLOC_PERTURB_=165), and with 0 in 6 of 6 from both of the others. This crash is the cudnn-frontend one.
    • test_torch_compile.py exited with 139 in 4 of 4 runs (2 plain, 2 with MALLOC_PERTURB_=165) from both the parent and the cudnn-frontend commit alone, after all its tests passed. From this branch it exited with 0 in 4 of 4. All three ran 92 passed, 71 skipped, 1 xpassed. For the cudnn-frontend commit alone, gdb places the crash in transformer_engine::rtc::KernelManager::~KernelManager → rtc::Kernel::~Kernel, reading cuda_driver::call's symbol cache. That's the TransformerEngine one.
    • TransformerEngine's PyTorch L0 tests (20 files from 2.20.2's tests/pytorch): with the cudnn-frontend commit alone, 12 of 20 files exited with 139 after their tests finished. From this branch, none did. Both have the same failing tests: 72 test_numerics.py tolerance failures and CUDA out-of-memory errors in test_fused_rope.py on the 24 GiB card.
  • Earlier, to isolate the cudnn-frontend fix, I built it in a downstream overlay: the same commit applied to cudnn-frontend 1.31.0, and the first commit of [Common] Don't call CUDA from static destructors at exit NVIDIA/TransformerEngine#3733 (then its only one, NVIDIA/TransformerEngine@18e5b70), which leaves the graph caches alone, applied to both arms.
    • RTX 4090, TransformerEngine main at NVIDIA/TransformerEngine@c9c92255: the fused-attention script exited with 139 in 6 of 6 runs without the fix and with 0 in 6 of 6 with it.
    • test_torch_compile.py on the same machine: without the fix, it exited with 139 in 1 of 2 plain runs and in 2 of 2 runs with MALLOC_PERTURB_=165. With the fix, it exited with 0 in every run. Both arms had the same test counts: 277 passed, 148 skipped, 1 xpassed.
    • GB10 (aarch64): reading the freed map did not fault there. With the fix, gdb showed the map intact when it was read at exit, and 12 of 12 runs exited with 0.
  • treefmt, ci/nixpkgs-vet.sh and nix-build ci -A eval.baseline for x86_64-linux and aarch64-linux pass locally at this branch's head.

The changes, the test and this description were written with Claude Code (Claude Opus 5.5); the commits carry an Assisted-by: trailer.

Things done

🤖 Generated with Claude Code

@nixpkgs-ci nixpkgs-ci Bot added 10.rebuild-linux: 1-10 This PR causes between 1 and 10 packages to rebuild on Linux. 10.rebuild-darwin: 0 This PR does not cause any packages to rebuild on Darwin. 11.by: package-maintainer This PR was created by a maintainer of all the package it changes. llm-assisted Contributions that include code generation with LLMs 6.topic: python Python is a high-level, general-purpose programming language. 6.topic: cuda Parallel computing platform and API labels Oct 23, 2026
@ConnorBaker ConnorBaker self-assigned this Oct 23, 2026
@ConnorBaker ConnorBaker changed the title cudaPackages.cudnn-frontend: backport fix for a crash at process exit cudaPackages.cudnn-frontend, python3Packages.transformer-engine: backport fixes for crashes at process exit Oct 24, 2026
cudnn-frontend 1.31.0 keeps plans recorded into CUDA graphs alive and
releases them through get_cuda_symbol(), whose library handle map is a
function-local static. A static cache destroyed after that map at exit
then reads freed memory. TransformerEngine's fused-attention graph cache
does this, and its processes can crash with SIGSEGV at exit
(NVIDIA/cudnn-frontend#1486).

Apply the upstream fix from develop, which never destroys the map or its
mutex. python3Packages.nvidia-cudnn-frontend builds from the same source,
so it now takes the same patches.

cudaPackages.tests.cudnn-frontend.teardown resolves a CUDA symbol from
the destructor of a static constructed before the map, under
AddressSanitizer. Without the fix it reports a heap-use-after-free in
get_cuda_symbol(). It needs no GPU.

The patch is applied only while the version is 1.31.0; any other version
fails evaluation, so the next update has to check whether the release
contains the fix.

Assisted-by: Claude Code (Claude Opus 5.5)
@ConnorBaker
ConnorBaker force-pushed the cudnn-frontend-handle-map-teardown branch from 0821275 to eac8f36 Compare October 24, 2026 03:10
…cess exit

TransformerEngine calls CUDA from two static destructors at exit, and
processes can segfault there:

- The static KernelManager, which owns the NVRTC-compiled kernels
  (LayerNorm, RMSNorm, softmax and others), unloads their modules through
  cuda_driver::call, whose symbol caches are function-local statics.
  Those are usually constructed after the KernelManager, so they're
  destroyed first.
- The fused-attention graph caches destroy cuDNN plans retained by CUDA
  graphs. That resolves CUDA symbols through cudnn-frontend's
  get_cuda_symbol(), whose map can already be destroyed (fixed in
  cudnn-frontend by the previous commit).

Apply both commits of NVIDIA/TransformerEngine#3733.
The first never destroys cuda_driver::call's symbol caches; the second
never destroys the KernelManager or the graph caches. Only the library
changes are taken; the rest is test and CI changes.

The patches are applied only while the version is 2.20.2; any other
version fails evaluation, so the next update has to check whether the
release contains the fix.

Assisted-by: Claude Code (Claude Opus 5.5)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

6.topic: cuda Parallel computing platform and API 6.topic: python Python is a high-level, general-purpose programming language. 10.rebuild-darwin: 0 This PR does not cause any packages to rebuild on Darwin. 10.rebuild-linux: 1-10 This PR causes between 1 and 10 packages to rebuild on Linux. 11.by: package-maintainer This PR was created by a maintainer of all the package it changes. llm-assisted Contributions that include code generation with LLMs

Projects

Status: New

Development

Successfully merging this pull request may close these issues.

1 participant