Skip to content

Commit 23647d8

Browse files
committed
Fix VMM handle leaks in virtual memory paths
1 parent e4208ed commit 23647d8

2 files changed

Lines changed: 540 additions & 10 deletions

File tree

cuda_core/cuda/core/_memory/_virtual_memory_resource.py

Lines changed: 54 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -322,8 +322,17 @@ def _grow_allocation_fast_path(
322322
)
323323
res, new_handle = driver.cuMemCreate(aligned_additional_size, prop, 0)
324324
raise_if_driver_error(res)
325-
# Register undo for creation
326-
trans.append(lambda h=new_handle: raise_if_driver_error(driver.cuMemRelease(h)[0]))
325+
new_handle_released = False
326+
327+
def _release_new_handle() -> None:
328+
nonlocal new_handle_released
329+
if not new_handle_released:
330+
raise_if_driver_error(driver.cuMemRelease(new_handle)[0])
331+
new_handle_released = True
332+
333+
# Register undo for creation. Callback is conditional to avoid
334+
# double-release after an explicit successful release.
335+
trans.append(_release_new_handle)
327336

328337
# Map the new physical memory to the extended VA range
329338
(res,) = driver.cuMemMap(new_ptr, aligned_additional_size, 0, new_handle, 0)
@@ -339,6 +348,9 @@ def _grow_allocation_fast_path(
339348
(res,) = driver.cuMemSetAccess(new_ptr, aligned_additional_size, descs, len(descs))
340349
raise_if_driver_error(res)
341350

351+
# Release handle ownership now that mapping is stable.
352+
_release_new_handle()
353+
342354
# All succeeded, cancel undo actions
343355
trans.commit()
344356

@@ -389,8 +401,17 @@ def _grow_allocation_slow_path(
389401
# Get the old allocation handle for remapping
390402
result, old_handle = driver.cuMemRetainAllocationHandle(buf.handle)
391403
raise_if_driver_error(result)
392-
# Register undo for old_handle
393-
trans.append(lambda h=old_handle: raise_if_driver_error(driver.cuMemRelease(h)[0]))
404+
old_handle_released = False
405+
406+
def _release_old_handle() -> None:
407+
nonlocal old_handle_released
408+
if not old_handle_released:
409+
raise_if_driver_error(driver.cuMemRelease(old_handle)[0])
410+
old_handle_released = True
411+
412+
# Register undo for old handle. Callback is conditional to avoid
413+
# double-release after explicit success.
414+
trans.append(_release_old_handle)
394415

395416
# Unmap the old VA range (aligned previous size)
396417
aligned_prev_size = total_aligned_size - aligned_additional_size
@@ -419,8 +440,17 @@ def _remap_old() -> None:
419440
res, new_handle = driver.cuMemCreate(aligned_additional_size, prop, 0)
420441
raise_if_driver_error(res)
421442

422-
# Register undo for new physical memory
423-
trans.append(lambda h=new_handle: raise_if_driver_error(driver.cuMemRelease(h)[0]))
443+
new_handle_released = False
444+
445+
def _release_new_handle() -> None:
446+
nonlocal new_handle_released
447+
if not new_handle_released:
448+
raise_if_driver_error(driver.cuMemRelease(new_handle)[0])
449+
new_handle_released = True
450+
451+
# Register undo for new physical memory. Callback is conditional to
452+
# avoid double-release after explicit success.
453+
trans.append(_release_new_handle)
424454

425455
# Map the new physical memory to the extended portion (aligned offset)
426456
(res,) = driver.cuMemMap(int(new_ptr) + aligned_prev_size, aligned_additional_size, 0, new_handle, 0)
@@ -439,6 +469,10 @@ def _remap_old() -> None:
439469
(res,) = driver.cuMemSetAccess(new_ptr, total_aligned_size, descs, len(descs))
440470
raise_if_driver_error(res)
441471

472+
# Release handles once all operations that need them have completed.
473+
_release_new_handle()
474+
_release_old_handle()
475+
442476
# All succeeded, cancel undo actions
443477
trans.commit()
444478

@@ -542,8 +576,17 @@ def allocate(self, size: int, *, stream: Stream | GraphBuilder | None = None) ->
542576
# ---- Create physical memory ----
543577
res, handle = driver.cuMemCreate(aligned_size, prop, 0)
544578
raise_if_driver_error(res)
545-
# Register undo for physical memory
546-
trans.append(lambda h=handle: raise_if_driver_error(driver.cuMemRelease(h)[0]))
579+
handle_released = False
580+
581+
def _release_handle() -> None:
582+
nonlocal handle_released
583+
if not handle_released:
584+
raise_if_driver_error(driver.cuMemRelease(handle)[0])
585+
handle_released = True
586+
587+
# Register undo for physical memory. Callback is conditional to
588+
# avoid double-release after explicit success.
589+
trans.append(_release_handle)
547590

548591
# ---- Reserve VA space ----
549592
# Potentially, use a separate size for the VA reservation from the physical allocation size
@@ -563,6 +606,9 @@ def allocate(self, size: int, *, stream: Stream | GraphBuilder | None = None) ->
563606
(res,) = driver.cuMemSetAccess(ptr, aligned_size, descs, len(descs))
564607
raise_if_driver_error(res)
565608

609+
# Release handle ownership once map+access setup succeeded.
610+
_release_handle()
611+
566612
trans.commit()
567613

568614
# Done — return a Buffer that tracks this VA range

0 commit comments

Comments
 (0)