NVIDIA / NVIDIA/cuda-python

cuda.core: GraphBuilder.join leaves forked builders in a state that segfaults at garbage collection if it raises midway

オープン
#2,776 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

triage
主要言語
Cython
スター
3.4k
フォーク
329
平均マージ
1日 21時間
マージ済み PR(30日)
113

説明

Summary

If GraphBuilder.join raises partway through, the forked builders it has not yet closed are left open with capturing streams. The test that hit this failed cleanly, but the process then segfaulted during gc.collect() in the init_cuda fixture teardown while those abandoned objects were destroyed. An exception inside join should not be able to crash the interpreter later.

What was observed

On PR #2750, a transient bug made the temporary ordering event in Stream.wait fail to be created. join calls root_bdr.stream.wait(builder.stream) and then builder.close() for each non-root builder; the wait raised on the first builder, so no forked builder was closed. Every GPU test job then crashed with:

Fatal Python error: Segmentation fault
Current thread ... (most recent call first):
  File ".../cuda_core/tests/conftest.py", line 192 in init_cuda

Line 192 is the gc.collect() in the fixture's finally. The first tests to fail were test_graph_complete_after_close_forked and test_graph_definition_raises_for_forked in tests/graph/test_graph_builder.py, both of which go through split and join.
https://github.com/NVIDIA/cuda-python/actions/runs/33911046695/job/101149369210

The event-creation bug is fixed in that PR, so the crash is no longer reachable through this route. The teardown fragility remains: any exception in join (or an interrupted split/join sequence) leaves builders in the same state.

Suggested direction

  • Make join exception-safe: on failure, close or otherwise neutralize the forked builders that were not joined, rather than leaving them mid-capture.
  • Make the forked-builder destructor tolerant of an abandoned capture, so destruction of a never-joined fork cannot dereference an invalid handle or end capture on a stream that is no longer valid. Identifying the exact dereference is part of this issue; the Python traceback stops at gc.collect().

Reproduction sketch

gb = Device().create_graph_builder().begin_building()
left, right = gb.split(2)
# force root_bdr.stream.wait(...) to raise inside join, e.g. by monkeypatching Stream.wait
with pytest.raises(Exception):
    GraphBuilder.join(left, right)
del left, right, gb
gc.collect()   # crashes today

コントリビューションガイド

コントリビューションガイドを開く

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

調査の方向性

GraphBuilder.join と tests/graph/test_graph_builder.py のテストで実行される forked-builder のクリーンアップパスから始め、次に cuda_core/tests/conftest.py の 192 行目にある init_cuda の teardown を調べます。再現スケッチを使って Stream.wait に例外を発生させ、名前が指定された 2 つの graph-builder テストを実行し、失敗した join が放棄された capture を残さないこと、および gc.collect() が segfault を起こさなくなったことを確認します。

索引モデルが issue の本文から書いたものです。

評価

技術スタック
python
領域
hpc
issue の種類
バグ
難易度
4/5
見積もり時間
3〜5日
活発さ
活発
明瞭さ
おおむね明確
初心者へのやさしさ
48/100

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。