-
Notifications
You must be signed in to change notification settings - Fork 124
[DeviceSanitizer] Refactor the code to manage shadow memory #2127
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 3 commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
a99e798
[DeviceSanitizer] Refactor the code to manage shadow memory
zhaomaosu 9a435b2
Add virtual destructor
zhaomaosu a7fe634
formatting
zhaomaosu 0a5221a
Remove unnecessary virtual keyword
zhaomaosu 9592d2e
Fix init problem on platform with multi GPU devices
zhaomaosu 88f14f3
Merge branch 'sycl' into refactor-shadow
zhaomaosu fbf564b
Minor fix
zhaomaosu 87e7499
Change shadow memory object to singleton
zhaomaosu df0654f
Merge branch 'sycl' into refactor-shadow
zhaomaosu 757663d
Do enqueue write sanitizer globals in registerProgram function
zhaomaosu 6074336
Enable quarantine by default and set the size to 8MB
zhaomaosu 3081bb1
Merge branch 'sycl' into refactor-shadow
zhaomaosu 3ba2a78
Minor update
zhaomaosu ce3772e
Merge branch 'main' into refactor-shadow
zhaomaosu File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,232 @@ | ||
| /* | ||
| * | ||
| * Copyright (C) 2024 Intel Corporation | ||
| * | ||
| * Part of the Unified-Runtime Project, under the Apache License v2.0 with LLVM Exceptions. | ||
| * See LICENSE.TXT | ||
| * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
| * | ||
| * @file asan_shadow.cpp | ||
| * | ||
| */ | ||
|
|
||
| #include "asan_shadow.hpp" | ||
| #include "asan_interceptor.hpp" | ||
| #include "asan_libdevice.hpp" | ||
| #include "ur_sanitizer_layer.hpp" | ||
| #include "ur_sanitizer_utils.hpp" | ||
|
|
||
| namespace ur_sanitizer_layer { | ||
|
|
||
| ur_result_t ShadowMemoryCPU::Setup() { | ||
| static ur_result_t Result = [this]() { | ||
| size_t ShadowSize = GetShadowSize(); | ||
| ShadowBegin = MmapNoReserve(0, ShadowSize); | ||
| if (ShadowBegin == 0) { | ||
| return UR_RESULT_ERROR_OUT_OF_HOST_MEMORY; | ||
| } | ||
| DontCoredumpRange(ShadowBegin, ShadowSize); | ||
| ShadowEnd = ShadowBegin + ShadowSize; | ||
| IsShadowMemInited = true; | ||
| return UR_RESULT_SUCCESS; | ||
| }(); | ||
| return Result; | ||
| } | ||
|
|
||
| ur_result_t ShadowMemoryCPU::Destory() { | ||
| if (!IsShadowMemInited) { | ||
| return UR_RESULT_SUCCESS; | ||
| } | ||
| static ur_result_t Result = [this]() { | ||
| if (!Munmap(ShadowBegin, GetShadowSize())) { | ||
| return UR_RESULT_ERROR_UNKNOWN; | ||
| } | ||
| return UR_RESULT_SUCCESS; | ||
| }(); | ||
| return Result; | ||
| } | ||
|
|
||
| uptr ShadowMemoryCPU::MemToShadow(uptr Ptr) { | ||
| return ShadowBegin + (Ptr >> ASAN_SHADOW_SCALE); | ||
| } | ||
|
|
||
| ur_result_t ShadowMemoryCPU::EnqueuePoisonShadow(ur_queue_handle_t, uptr Ptr, | ||
| uptr Size, u8 Value) { | ||
| if (Size == 0) { | ||
| return UR_RESULT_SUCCESS; | ||
| } | ||
|
|
||
| uptr ShadowBegin = MemToShadow(Ptr); | ||
| uptr ShadowEnd = MemToShadow(Ptr + Size - 1); | ||
| assert(ShadowBegin <= ShadowEnd); | ||
| getContext()->logger.debug( | ||
| "EnqueuePoisonShadow(addr={}, count={}, value={})", (void *)ShadowBegin, | ||
| ShadowEnd - ShadowBegin + 1, (void *)(size_t)Value); | ||
| memset((void *)ShadowBegin, Value, ShadowEnd - ShadowBegin + 1); | ||
|
|
||
| return UR_RESULT_SUCCESS; | ||
| } | ||
|
|
||
| ur_result_t ShadowMemoryGPU::Setup() { | ||
| // Currently, Level-Zero doesn't create independent VAs for each contexts, if we reserve | ||
| // shadow memory for each contexts, this will cause out-of-resource error when user uses | ||
| // multiple contexts. Therefore, we just create one shadow memory here. | ||
| static ur_result_t Result = [this]() { | ||
| size_t ShadowSize = GetShadowSize(); | ||
| // TODO: Protect Bad Zone | ||
| auto Result = getContext()->urDdiTable.VirtualMem.pfnReserve( | ||
| Context, nullptr, ShadowSize, (void **)&ShadowBegin); | ||
| if (Result == UR_RESULT_SUCCESS) { | ||
| ShadowEnd = ShadowBegin + ShadowSize; | ||
| // Retain the context which reserves shadow memory | ||
| getContext()->urDdiTable.Context.pfnRetain(Context); | ||
| } | ||
| IsShadowMemInited = true; | ||
| return Result; | ||
| }(); | ||
| return Result; | ||
| } | ||
|
|
||
| ur_result_t ShadowMemoryGPU::Destory() { | ||
| if (!IsShadowMemInited) { | ||
| return UR_RESULT_SUCCESS; | ||
| } | ||
| static ur_result_t Result = [this]() { | ||
| auto Result = getContext()->urDdiTable.VirtualMem.pfnFree( | ||
| Context, (const void *)ShadowBegin, GetShadowSize()); | ||
| getContext()->urDdiTable.Context.pfnRelease(Context); | ||
| return Result; | ||
| }(); | ||
| return Result; | ||
| } | ||
|
|
||
| ur_result_t ShadowMemoryGPU::EnqueuePoisonShadow(ur_queue_handle_t Queue, | ||
| uptr Ptr, uptr Size, | ||
| u8 Value) { | ||
| if (Size == 0) { | ||
| return UR_RESULT_SUCCESS; | ||
| } | ||
|
|
||
| uptr ShadowBegin = MemToShadow(Ptr); | ||
| uptr ShadowEnd = MemToShadow(Ptr + Size - 1); | ||
| assert(ShadowBegin <= ShadowEnd); | ||
| { | ||
| static const size_t PageSize = | ||
| GetVirtualMemGranularity(Context, Device); | ||
|
|
||
| ur_physical_mem_properties_t Desc{ | ||
| UR_STRUCTURE_TYPE_PHYSICAL_MEM_PROPERTIES, nullptr, 0}; | ||
|
|
||
| // Make sure [Ptr, Ptr + Size] is mapped to physical memory | ||
| for (auto MappedPtr = RoundDownTo(ShadowBegin, PageSize); | ||
| MappedPtr <= ShadowEnd; MappedPtr += PageSize) { | ||
| std::scoped_lock<ur_mutex> Guard(VirtualMemMapsMutex); | ||
| if (VirtualMemMaps.find(MappedPtr) == VirtualMemMaps.end()) { | ||
| ur_physical_mem_handle_t PhysicalMem{}; | ||
| auto URes = getContext()->urDdiTable.PhysicalMem.pfnCreate( | ||
| Context, Device, PageSize, &Desc, &PhysicalMem); | ||
| if (URes != UR_RESULT_SUCCESS) { | ||
| getContext()->logger.error("urPhysicalMemCreate(): {}", | ||
| URes); | ||
| return URes; | ||
| } | ||
|
|
||
| URes = getContext()->urDdiTable.VirtualMem.pfnMap( | ||
| Context, (void *)MappedPtr, PageSize, PhysicalMem, 0, | ||
| UR_VIRTUAL_MEM_ACCESS_FLAG_READ_WRITE); | ||
| if (URes != UR_RESULT_SUCCESS) { | ||
| getContext()->logger.debug("urVirtualMemMap({}, {}): {}", | ||
| (void *)MappedPtr, PageSize, | ||
| URes); | ||
| return URes; | ||
| } | ||
|
|
||
| getContext()->logger.debug("urVirtualMemMap: {} ~ {}", | ||
| (void *)MappedPtr, | ||
| (void *)(MappedPtr + PageSize - 1)); | ||
|
|
||
| // Initialize to zero | ||
| URes = EnqueueUSMBlockingSet(Queue, (void *)MappedPtr, 0, | ||
| PageSize); | ||
| if (URes != UR_RESULT_SUCCESS) { | ||
| getContext()->logger.error("EnqueueUSMBlockingSet(): {}", | ||
| URes); | ||
| return URes; | ||
| } | ||
|
|
||
| VirtualMemMaps[MappedPtr].first = PhysicalMem; | ||
| } | ||
|
|
||
| // We don't need to record virtual memory map for null pointer. | ||
zhaomaosu marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| if (Ptr == 0) { | ||
| continue; | ||
| } | ||
|
|
||
| auto AllocInfoIt = | ||
| getContext()->interceptor->findAllocInfoByAddress(Ptr); | ||
| assert(AllocInfoIt); | ||
| VirtualMemMaps[MappedPtr].second.insert((*AllocInfoIt)->second); | ||
| } | ||
| } | ||
|
|
||
| auto URes = EnqueueUSMBlockingSet(Queue, (void *)ShadowBegin, Value, | ||
| ShadowEnd - ShadowBegin + 1); | ||
| getContext()->logger.debug( | ||
| "EnqueuePoisonShadow (addr={}, count={}, value={}): {}", | ||
| (void *)ShadowBegin, ShadowEnd - ShadowBegin + 1, (void *)(size_t)Value, | ||
| URes); | ||
| if (URes != UR_RESULT_SUCCESS) { | ||
| getContext()->logger.error("EnqueueUSMBlockingSet(): {}", URes); | ||
| return URes; | ||
| } | ||
|
|
||
| return UR_RESULT_SUCCESS; | ||
| } | ||
|
|
||
| ur_result_t ShadowMemoryGPU::ReleaseShadow(std::shared_ptr<AllocInfo> AI) { | ||
| uptr ShadowBegin = MemToShadow(AI->AllocBegin); | ||
| uptr ShadowEnd = MemToShadow(AI->AllocBegin + AI->AllocSize); | ||
| assert(ShadowBegin <= ShadowEnd); | ||
|
|
||
| static const size_t PageSize = GetVirtualMemGranularity(Context, Device); | ||
|
|
||
| for (auto MappedPtr = RoundDownTo(ShadowBegin, PageSize); | ||
| MappedPtr <= ShadowEnd; MappedPtr += PageSize) { | ||
| std::scoped_lock<ur_mutex> Guard(VirtualMemMapsMutex); | ||
| if (VirtualMemMaps.find(MappedPtr) == VirtualMemMaps.end()) { | ||
| continue; | ||
| } | ||
| VirtualMemMaps[MappedPtr].second.erase(AI); | ||
| if (VirtualMemMaps[MappedPtr].second.empty()) { | ||
| UR_CALL(getContext()->urDdiTable.VirtualMem.pfnUnmap( | ||
| Context, (void *)MappedPtr, PageSize)); | ||
| UR_CALL(getContext()->urDdiTable.PhysicalMem.pfnRelease( | ||
| VirtualMemMaps[MappedPtr].first)); | ||
| getContext()->logger.debug("urVirtualMemUnmap: {} ~ {}", | ||
| (void *)MappedPtr, | ||
| (void *)(MappedPtr + PageSize - 1)); | ||
| } | ||
| } | ||
|
|
||
| return UR_RESULT_SUCCESS; | ||
| } | ||
|
|
||
| uptr ShadowMemoryPVC::MemToShadow(uptr Ptr) { | ||
| if (Ptr & 0xFF00000000000000ULL) { // Device USM | ||
| return ShadowBegin + 0x80000000000ULL + | ||
| ((Ptr & 0xFFFFFFFFFFFFULL) >> ASAN_SHADOW_SCALE); | ||
| } else { // Only consider 47bit VA | ||
| return ShadowBegin + ((Ptr & 0x7FFFFFFFFFFFULL) >> ASAN_SHADOW_SCALE); | ||
| } | ||
| } | ||
|
|
||
| uptr ShadowMemoryDG2::MemToShadow(uptr Ptr) { | ||
| if (Ptr & 0xFFFF000000000000ULL) { // Device USM | ||
| return ShadowBegin + 0x80000000000ULL + | ||
| ((Ptr & 0x7FFFFFFFFFFFULL) >> ASAN_SHADOW_SCALE); | ||
| } else { // Host/Shared USM | ||
| return ShadowBegin + (Ptr >> ASAN_SHADOW_SCALE); | ||
| } | ||
| } | ||
|
|
||
| } // namespace ur_sanitizer_layer | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.