[UR][L0] Fix potential memory leaks - #22938
Conversation
There was a problem hiding this comment.
Pull request overview
This PR addresses potential memory leaks in the Unified Runtime Level Zero adapter by introducing RAII guards around allocations that can be bypassed by early-return macros (e.g., ZE2UR_CALL / UR_CALL).
Changes:
- Add a guard in
urMemBufferCreateto ensure partially-createdur_bufferinstances are freed on error paths. - Refactor buffer partition creation to use
std::unique_ptr/std::make_uniqueand release ownership on success. - Add an RAII guard for
waitlist.ZeEventListinur_buffer::getBufferZeHandleso it’s deleted on all exit paths.
Suppressed comments (1)
unified-runtime/source/adapters/level_zero/memory.cpp:1916
- This function retains the context and inserts into
Context->MemAllocsbefore executing severalUR_CALL/ZE2UR_CALLoperations that can early-return. On those error paths, the retained context andMemAllocsentry will not be rolled back, causing leaks/state pollution even if theur_bufferis RAII-managed. Consider deferring the retain/MemAllocs.emplace(...)until after all fallible operations succeed (right before returning), or add a scope rollback guard that erases the map entry and releases the retained context on failure.
*Mem = v1_cast(static_cast<ur_mem_handle_t_ *>(Buffer.release()));
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
c9ed844 to
35b1ad6
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 1 out of 1 changed files in this pull request and generated no new comments.
Suppressed comments (1)
unified-runtime/source/adapters/level_zero/memory.cpp:1864
ur_buffer's destructor does not callfree()(seeur_buffer::~ur_buffer()), so usingstd::unique_ptr<ur_buffer>here means any early-return after the buffer is created (e.g. fromUR_CALL(...)/ZE2UR_CALL(...)later in this function) willdeletethe object without releasing allocations or an owned native handle. This can introduce leaks on error paths. Use the same guard pattern asurMemBufferCreate(custom deleter callingfree()beforedelete) so cleanup happens on all returns.
std::unique_ptr<ur_buffer> Buffer;
try {
Buffer = std::make_unique<ur_buffer>(
Context, Size, Device, ur_cast<char *>(NativeMem), OwnNativeHandle);
} catch (const std::bad_alloc &) {
35b1ad6 to
31a1988
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 1 out of 1 changed files in this pull request and generated 1 comment.
Suppressed comments (1)
unified-runtime/source/adapters/level_zero/memory.cpp:1680
- The guard is still null if
new ur_buffer(...)throws. WithUR_MEM_FLAG_USE_HOST_POINTER,maybeImportUSMhas already importedHost, so an allocation/constructor failure reaches these catches without releasing that import. Release the import in both failure paths (or guard it independently until the buffer assumes ownership).
Buffer = new ur_buffer(Context, Size, HostPtrOrNull, HostPtrImported);
bufferGuard.reset(Buffer);
31a1988 to
e8998c2
Compare
e8998c2 to
42ce152
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 1 out of 1 changed files in this pull request and generated 1 comment.
Suppressed comments (1)
unified-runtime/source/adapters/level_zero/memory.cpp:1680
- The guard is armed only after
ur_bufferfinishes construction, butmaybeImportUSMhas already importedHostabove. If allocation of the object orAllocations[nullptr]in the constructor throws, these catch paths return without releasing that imported pointer, so the OOM path still leaks the resource this change is intended to protect. Release the import in both constructor-failure handlers (or guard it separately until ownership transfers tobufferGuard).
bufferGuard.reset(Buffer);
42ce152 to
09aed4f
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 1 out of 1 changed files in this pull request and generated no new comments.
Suppressed comments (1)
unified-runtime/source/adapters/level_zero/memory.cpp:1903
- Delay adopting the native allocation until creation has fully succeeded. Both
getZeHandleandzeCommandListAppendMemoryCopybelow can return an error; after this assignment, such a return runs the guard, whose deleter callsfree()and releasesNativeMem. The API specifies that ownership is transferred to the resultant object, so a failed call must not consume the caller's handle. Mark this allocationfree_nativeonly at the success/commit point, and defer or roll back the correspondingMemAllocsentry and context retain so failure cleanup still releases only adapter-created allocations.
if (OwnNativeHandle)
Buffer->Allocations[Device].ReleaseAction =
ur_buffer::allocation_t::free_native;
09aed4f to
0868ee1
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 1 out of 1 changed files in this pull request and generated no new comments.
Suppressed comments (1)
unified-runtime/source/adapters/level_zero/memory.cpp:1680
- This guard is armed only after
ur_bufferfinishes construction. WithUR_MEM_FLAG_USE_HOST_POINTERand USM import enabled,maybeImportUSMhas already registeredHost, while the constructor'sAllocations[nullptr]insertion can throwstd::bad_alloc. The catch then returns without an object whosefree()can release that registration, so the imported pointer still leaks. Add a separate import guard immediately aftermaybeImportUSMand dismiss it once the constructed buffer assumes theunimportrelease action.
bufferGuard.reset(Buffer);
0868ee1 to
25f8390
Compare
Fixes #18265