diff --git a/.github/workflows/win_clang_rel_x64.yaml b/.github/workflows/win_clang_rel_x64.yaml index 10ad53b1e..756cc4237 100644 --- a/.github/workflows/win_clang_rel_x64.yaml +++ b/.github/workflows/win_clang_rel_x64.yaml @@ -161,7 +161,7 @@ jobs: set "PATH=%CD%\..\depot_tools;%PATH%" set "DEPOT_TOOLS_WIN_TOOLCHAIN=0" cd test - gn gen out\Fuzzer --args="is_debug=false gpgmm_use_fuzzing_engine=true use_libfuzzer=true is_asan=true gpgmm_enable_assert_on_warning=true gpgmm_enable_device_leak_checks=true" + gn gen out\Fuzzer --args="is_debug=false gpgmm_use_fuzzing_engine=true use_libfuzzer=true is_asan=true" - name: Build fuzzer for main branch (with patch) shell: cmd @@ -172,7 +172,22 @@ jobs: ninja -C out\Fuzzer - name: Run gpgmm_d3d12_resource_allocator_fuzzer (with patch) + timeout-minutes: 3 shell: cmd run: | cd test - out\Fuzzer\gpgmm_d3d12_resource_allocator_fuzzer.exe -max_total_time=120 + out\Fuzzer\gpgmm_d3d12_resource_allocator_fuzzer.exe -max_total_time=120 -timeout=10 + + # Workaround libFuzzer never exiting when max_total_time by using -runs instead. + # Step timeout may need to be increased to not expire before the process exits. + - name: Run gpgmm_d3d12_residency_manager_fuzzer (with patch) + timeout-minutes: 5 + shell: cmd + run: | + cd test + out\Fuzzer\gpgmm_d3d12_residency_manager_fuzzer.exe -runs=20000 -timeout=10 + + - uses: actions/upload-artifact@v3 + with: + name: fuzzer-test-reproducer + path: test/(crash|oom|leak|timeout)-.* diff --git a/src/fuzzers/BUILD.gn b/src/fuzzers/BUILD.gn index 0022910e1..28f8b1717 100644 --- a/src/fuzzers/BUILD.gn +++ b/src/fuzzers/BUILD.gn @@ -25,7 +25,26 @@ import("//testing/libfuzzer/fuzzer_test.gni") if (is_win) { fuzzer_test("gpgmm_d3d12_resource_allocator_fuzzer") { - sources = [ "D3D12ResourceAllocatorFuzzer.cpp" ] + sources = [ + "D3D12Fuzzer.cpp", + "D3D12Fuzzer.h", + "D3D12ResourceAllocatorFuzzer.cpp", + ] + + deps = [ "${gpgmm_root_dir}:gpgmm" ] + + libs = [ + "d3d12.lib", + "dxgi.lib", + ] + } + + fuzzer_test("gpgmm_d3d12_residency_manager_fuzzer") { + sources = [ + "D3D12Fuzzer.cpp", + "D3D12Fuzzer.h", + "D3D12ResidencyManagerFuzzer.cpp", + ] deps = [ "${gpgmm_root_dir}:gpgmm" ] @@ -41,6 +60,9 @@ group("fuzzers") { deps = [] if (is_win) { - deps += [ ":gpgmm_d3d12_resource_allocator_fuzzer" ] + deps += [ + ":gpgmm_d3d12_residency_manager_fuzzer", + ":gpgmm_d3d12_resource_allocator_fuzzer", + ] } } diff --git a/src/fuzzers/D3D12Fuzzer.cpp b/src/fuzzers/D3D12Fuzzer.cpp new file mode 100644 index 000000000..cb3d918d8 --- /dev/null +++ b/src/fuzzers/D3D12Fuzzer.cpp @@ -0,0 +1,84 @@ +// Copyright 2021 The GPGMM Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "D3D12Fuzzer.h" + +uint64_t UInt8ToUInt64(const uint8_t* src) { + uint64_t dst; + memcpy(&dst, src, sizeof(uint64_t)); + return dst; +} + +HRESULT CreateAllocatorDesc(gpgmm::d3d12::ALLOCATOR_DESC* allocatorDesc) { + gpgmm::d3d12::ALLOCATOR_DESC allocatorDescOut = {}; + + // Populate the device + ComPtr d3dDevice; + if (FAILED(D3D12CreateDevice(nullptr, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&d3dDevice)))) { + return E_FAIL; + } + + allocatorDescOut.Device = d3dDevice; + + // Populate the adapter + LUID adapterLUID = d3dDevice->GetAdapterLuid(); + ComPtr dxgiFactory; + if (FAILED(CreateDXGIFactory1(IID_PPV_ARGS(&dxgiFactory)))) { + return E_FAIL; + } + + ComPtr dxgiFactory4; + if (FAILED(dxgiFactory.As(&dxgiFactory4))) { + return E_FAIL; + } + + ComPtr dxgiAdapter; + if (FAILED(dxgiFactory4->EnumAdapterByLuid(adapterLUID, IID_PPV_ARGS(&dxgiAdapter)))) { + return E_FAIL; + } + + allocatorDescOut.Adapter = dxgiAdapter; + + // Configure options + D3D12_FEATURE_DATA_D3D12_OPTIONS options = {}; + if (FAILED(d3dDevice->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS, &options, + sizeof(options)))) { + return E_FAIL; + } + + allocatorDescOut.ResourceHeapTier = options.ResourceHeapTier; + allocatorDescOut.MinLogLevel = D3D12_MESSAGE_SEVERITY_MESSAGE; + + if (allocatorDesc != nullptr) { + *allocatorDesc = allocatorDescOut; + } + + return S_OK; +} + +D3D12_RESOURCE_DESC CreateBufferDesc(uint64_t width, uint64_t alignment) { + D3D12_RESOURCE_DESC resourceDesc = {}; + resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; + resourceDesc.Alignment = 0; + resourceDesc.Width = width; + resourceDesc.Height = 1; + resourceDesc.DepthOrArraySize = 1; + resourceDesc.MipLevels = 1; + resourceDesc.Format = DXGI_FORMAT_UNKNOWN; + resourceDesc.SampleDesc.Count = 1; + resourceDesc.SampleDesc.Quality = 0; + resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; + resourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE; + return resourceDesc; +} diff --git a/src/fuzzers/D3D12Fuzzer.h b/src/fuzzers/D3D12Fuzzer.h new file mode 100644 index 000000000..309703d3d --- /dev/null +++ b/src/fuzzers/D3D12Fuzzer.h @@ -0,0 +1,26 @@ +// Copyright 2021 The GPGMM Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef FUZZER_D3D12FUZZER_H_ +#define FUZZER_D3D12FUZZER_H_ + +#include + +uint64_t UInt8ToUInt64(const uint8_t* src); + +HRESULT CreateAllocatorDesc(gpgmm::d3d12::ALLOCATOR_DESC* allocatorDesc); + +D3D12_RESOURCE_DESC CreateBufferDesc(uint64_t width, uint64_t alignment = 0); + +#endif // FUZZER_D3D12FUZZER_H_ diff --git a/src/fuzzers/D3D12ResidencyManagerFuzzer.cpp b/src/fuzzers/D3D12ResidencyManagerFuzzer.cpp new file mode 100644 index 000000000..73750b337 --- /dev/null +++ b/src/fuzzers/D3D12ResidencyManagerFuzzer.cpp @@ -0,0 +1,115 @@ +// Copyright 2021 The GPGMM Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "testing/libfuzzer/libfuzzer_exports.h" + +#include "D3D12Fuzzer.h" +#include "gpgmm/common/SizeClass.h" + +namespace { + + ComPtr gResourceAllocator; + ComPtr gResidencyManager; + std::vector> gAllocationsBelowBudget = {}; + + uint64_t GetBudgetLeft(gpgmm::d3d12::ResidencyManager* const residencyManager, + const DXGI_MEMORY_SEGMENT_GROUP& memorySegmentGroup) { + if (residencyManager == nullptr) { + return 0; + } + DXGI_QUERY_VIDEO_MEMORY_INFO* segment = + gResidencyManager->GetVideoMemoryInfo(memorySegmentGroup); + return (segment->Budget > segment->CurrentUsage) ? (segment->Budget - segment->CurrentUsage) + : 0; + } + +} // namespace + +extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) { + gpgmm::d3d12::ALLOCATOR_DESC allocatorDesc = {}; + if (FAILED(CreateAllocatorDesc(&allocatorDesc))) { + return 0; + } + + allocatorDesc.Flags |= gpgmm::d3d12::ALLOCATOR_FLAG_ALWAYS_IN_BUDGET; + + gpgmm::d3d12::RESIDENCY_DESC residencyDesc = {}; + + ComPtr adapter3; + if (FAILED(allocatorDesc.Adapter.As(&adapter3))) { + return 0; + } + + residencyDesc.Adapter = adapter3; + residencyDesc.Device = allocatorDesc.Device; + residencyDesc.MinLogLevel = D3D12_MESSAGE_SEVERITY_MESSAGE; + + // Create ResidencyManager + D3D12_FEATURE_DATA_ARCHITECTURE arch = {}; + if (FAILED(residencyDesc.Device->CheckFeatureSupport(D3D12_FEATURE_ARCHITECTURE, &arch, + sizeof(arch)))) { + return 0; + } + + residencyDesc.IsUMA = arch.UMA; + + if (FAILED(gpgmm::d3d12::ResidencyManager::CreateResidencyManager(residencyDesc, + &gResidencyManager))) { + return 0; + } + + if (FAILED(gpgmm::d3d12::ResourceAllocator::CreateAllocator( + allocatorDesc, gResidencyManager.Get(), &gResourceAllocator))) { + return 0; + } + + gpgmm::d3d12::ALLOCATION_DESC allocationDesc = {}; + allocationDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT; + + const DXGI_MEMORY_SEGMENT_GROUP bufferMemorySegment = + gResidencyManager->GetMemorySegmentGroup(allocationDesc.HeapType); + + constexpr uint64_t kBufferMemorySize = GPGMM_MB_TO_BYTES(1); + const D3D12_RESOURCE_DESC bufferDesc = CreateBufferDesc(kBufferMemorySize); + + // Keep allocating until we reach the budget. + uint64_t memoryUnderBudget = GetBudgetLeft(gResidencyManager.Get(), bufferMemorySegment); + while (gResourceAllocator->GetInfo().UsedMemoryUsage + kBufferMemorySize < memoryUnderBudget) { + ComPtr allocation; + if (FAILED(gResourceAllocator->CreateResource({}, bufferDesc, D3D12_RESOURCE_STATE_COMMON, + nullptr, &allocation))) { + return 0; + } + + gAllocationsBelowBudget.push_back(std::move(allocation)); + } + + return 0; +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + if (size < 8) { + return 0; + } + + gpgmm::d3d12::ALLOCATION_DESC allocationDesc = {}; + allocationDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT; + + ComPtr allocationOverBudget; + gResourceAllocator->CreateResource(allocationDesc, CreateBufferDesc(UInt8ToUInt64(data)), + D3D12_RESOURCE_STATE_COMMON, nullptr, &allocationOverBudget); + return 0; +} diff --git a/src/fuzzers/D3D12ResourceAllocatorFuzzer.cpp b/src/fuzzers/D3D12ResourceAllocatorFuzzer.cpp index 1c6abc740..ac5e96fc5 100644 --- a/src/fuzzers/D3D12ResourceAllocatorFuzzer.cpp +++ b/src/fuzzers/D3D12ResourceAllocatorFuzzer.cpp @@ -16,59 +16,19 @@ #include "testing/libfuzzer/libfuzzer_exports.h" -#include +#include "D3D12Fuzzer.h" namespace { ComPtr gResourceAllocator; - uint64_t UInt8ToUInt64(const uint8_t* src) { - uint64_t dst; - memcpy(&dst, src, sizeof(uint64_t)); - return dst; - } - } // namespace extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) { gpgmm::d3d12::ALLOCATOR_DESC allocatorDesc = {}; - - // Populate the device - ComPtr d3dDevice; - if (FAILED(D3D12CreateDevice(nullptr, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&d3dDevice)))) { - return 0; - } - - allocatorDesc.Device = d3dDevice; - - // Populate the adapter - LUID adapterLUID = d3dDevice->GetAdapterLuid(); - ComPtr dxgiFactory; - if (FAILED(CreateDXGIFactory1(IID_PPV_ARGS(&dxgiFactory)))) { - return 0; - } - - ComPtr dxgiFactory4; - if (FAILED(dxgiFactory.As(&dxgiFactory4))) { - return 0; - } - - ComPtr dxgiAdapter; - if (FAILED(dxgiFactory4->EnumAdapterByLuid(adapterLUID, IID_PPV_ARGS(&dxgiAdapter)))) { - return 0; - } - - allocatorDesc.Adapter = dxgiAdapter; - - // Populate the options. - D3D12_FEATURE_DATA_D3D12_OPTIONS options = {}; - if (FAILED(d3dDevice->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS, &options, - sizeof(options)))) { + if (FAILED(CreateAllocatorDesc(&allocatorDesc))) { return 0; } - allocatorDesc.ResourceHeapTier = options.ResourceHeapTier; - - allocatorDesc.MinLogLevel = D3D12_MESSAGE_SEVERITY_MESSAGE; if (FAILED( gpgmm::d3d12::ResourceAllocator::CreateAllocator(allocatorDesc, &gResourceAllocator))) { @@ -83,28 +43,11 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { return 0; } - if (gResourceAllocator == nullptr) { - return 0; - } - - D3D12_RESOURCE_DESC resourceDesc = {}; - resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; - resourceDesc.Alignment = 0; - resourceDesc.Width = UInt8ToUInt64(data); - resourceDesc.Height = 1; - resourceDesc.DepthOrArraySize = 1; - resourceDesc.MipLevels = 1; - resourceDesc.Format = DXGI_FORMAT_UNKNOWN; - resourceDesc.SampleDesc.Count = 1; - resourceDesc.SampleDesc.Quality = 0; - resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; - resourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE; - gpgmm::d3d12::ALLOCATION_DESC allocationDesc = {}; allocationDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT; ComPtr allocation; - gResourceAllocator->CreateResource(allocationDesc, resourceDesc, D3D12_RESOURCE_STATE_COMMON, - nullptr, &allocation); + gResourceAllocator->CreateResource(allocationDesc, CreateBufferDesc(UInt8ToUInt64(data)), + D3D12_RESOURCE_STATE_COMMON, nullptr, &allocation); return 0; }