From 537f12ac8a10a5e088e4ba0d766389ec5d749081 Mon Sep 17 00:00:00 2001 From: Bryan Bernhart Date: Thu, 14 Apr 2022 09:50:37 -0700 Subject: [PATCH] Fuzz D3D12 resource allocations. --- .github/workflows/win_clang_rel_x64.yaml | 22 +++++ BUILD.gn | 5 +- DEPS | 5 ++ README.md | 19 ++++- src/fuzzers/BUILD.gn | 41 ++++++++++ src/fuzzers/D3D12ResourceAllocatorFuzzer.cpp | 62 ++++++++++++++ third_party/libFuzzer/BUILD.gn | 85 ++++++++++++++++++++ tools/memory/asan/blocklist_win.txt | 4 + 8 files changed, 239 insertions(+), 4 deletions(-) create mode 100644 src/fuzzers/BUILD.gn create mode 100644 src/fuzzers/D3D12ResourceAllocatorFuzzer.cpp create mode 100644 third_party/libFuzzer/BUILD.gn create mode 100644 tools/memory/asan/blocklist_win.txt diff --git a/.github/workflows/win_clang_rel_x64.yaml b/.github/workflows/win_clang_rel_x64.yaml index dcacbc9af..ad7ba9706 100644 --- a/.github/workflows/win_clang_rel_x64.yaml +++ b/.github/workflows/win_clang_rel_x64.yaml @@ -157,3 +157,25 @@ jobs: - name: Regression check capture replay tests run: | python test\scripts\regression_check.py ${{ github.workspace }}\..\baseline_capture_replay_tests.json ${{ github.workspace }}\..\test_capture_replay_tests.json + + - name: Generate fuzzer for main branch (with patch) + shell: cmd + run: | + set "PATH=%CD%\..\depot_tools;%PATH%" + set "DEPOT_TOOLS_WIN_TOOLCHAIN=0" + cd test + gn gen out\Fuzzer --args="is_debug=false use_libfuzzer=true is_asan=true gpgmm_enable_assert_on_warning=true gpgmm_enable_device_checks=true" + + - name: Build fuzzer for main branch (with patch) + shell: cmd + run: | + set "PATH=%CD%\..\depot_tools;%PATH%" + set "DEPOT_TOOLS_WIN_TOOLCHAIN=0" + cd test + ninja -C out\Fuzzer + + - name: Run gpgmm_d3d12_resource_allocator_fuzzer (with patch) + shell: cmd + run: | + cd test + out\Fuzzer\gpgmm_d3d12_resource_allocator_fuzzer.exe -max_total_time=120 diff --git a/BUILD.gn b/BUILD.gn index 7ee36f1ee..01b66e19a 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -18,7 +18,10 @@ import("build_overrides/gpgmm_overrides_with_defaults.gni") group("all") { testonly = true - deps = [ "src/tests:gpgmm_tests" ] + deps = [ + "src/fuzzers", + "src/tests:gpgmm_tests", + ] if (gpgmm_enable_dawn) { deps += [ "//third_party/dawn/src/dawn/tests:dawn_end2end_tests" ] diff --git a/DEPS b/DEPS index 77339a640..313fe96be 100644 --- a/DEPS +++ b/DEPS @@ -129,6 +129,11 @@ deps = { 'url': '{chromium_git}/external/github.com/open-source-parsers/jsoncpp@9059f5cad030ba11d37818847443a53918c327b1', 'condition': 'gpgmm_standalone', }, + # Fuzzing + 'third_party/libFuzzer/src': { + 'url': '{chromium_git}/chromium/llvm-project/compiler-rt/lib/fuzzer.git@debe7d2d1982e540fbd6bd78604bf001753f9e74', + 'condition': 'gpgmm_standalone', + }, } hooks = [ diff --git a/README.md b/README.md index 51b10e122..e862512ac 100644 --- a/README.md +++ b/README.md @@ -53,21 +53,34 @@ Then use `ninja -C out/Release` or `ninja -C out/Debug` to build. ### Run tests -Run unit tests: +#### Run unit tests: ```sh > out/Debug/gpgmm_unittests ``` -Run end2end tests: +Unit tests check the front-end code in isolation or without using backend GPU. + +#### Run end2end tests: ```sh > out/Debug/gpgmm_end2end_tests ``` -Run capture replay tests: +End2End tests check both the front AND backend code using a backend GPU. + +#### Run capture replay tests: ```sh > out/Debug/gpgmm_capture_replay_tests ``` +Capture replay tests checks using pre-recorded memory patterns using a backend GPU. + +#### Run fuzzing tests: +```sh +> out/Debug/gpgmm_*_fuzzer +``` + +Fuzzer checks using random memory patterns using a backend GPU. + ## How do I use it? To allocate, you create an allocator then create allocations from it: diff --git a/src/fuzzers/BUILD.gn b/src/fuzzers/BUILD.gn new file mode 100644 index 000000000..cd0f2d167 --- /dev/null +++ b/src/fuzzers/BUILD.gn @@ -0,0 +1,41 @@ +# 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. + +import("//build_overrides/build.gni") +import("../../build_overrides/gpgmm_overrides_with_defaults.gni") + +import("//testing/libfuzzer/fuzzer_test.gni") + +# if (build_with_chromium) { +# import("//testing/libfuzzer/fuzzer_test.gni") +# } else { +# import("//testing/test.gni") +# } + +if (is_win) { + fuzzer_test("gpgmm_d3d12_resource_allocator_fuzzer") { + sources = [ "D3D12ResourceAllocatorFuzzer.cpp" ] + + deps = [ "${gpgmm_root_dir}:gpgmm" ] + } +} + +group("fuzzers") { + testonly = true + deps = [] + + if (is_win) { + deps += [ ":gpgmm_d3d12_resource_allocator_fuzzer" ] + } +} diff --git a/src/fuzzers/D3D12ResourceAllocatorFuzzer.cpp b/src/fuzzers/D3D12ResourceAllocatorFuzzer.cpp new file mode 100644 index 000000000..3d80a94c8 --- /dev/null +++ b/src/fuzzers/D3D12ResourceAllocatorFuzzer.cpp @@ -0,0 +1,62 @@ +// 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 + +namespace { + + ComPtr gResourceAllocator; + +} // namespace + +extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) { + gpgmm::d3d12::ALLOCATOR_DESC desc = {}; + gpgmm::d3d12::ResourceAllocator::CreateAllocator(desc, &gResourceAllocator); + return 0; +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + if (size < 8) { + return 0; + } + + if (gResourceAllocator == nullptr) { + return 0; + } + + D3D12_RESOURCE_DESC resourceDesc = {}; + resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; + resourceDesc.Alignment = 0; + resourceDesc.Width = static_cast(data[0]); + 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); + return 0; +} diff --git a/third_party/libFuzzer/BUILD.gn b/third_party/libFuzzer/BUILD.gn new file mode 100644 index 000000000..eb14c5854 --- /dev/null +++ b/third_party/libFuzzer/BUILD.gn @@ -0,0 +1,85 @@ +# Copyright 2016 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("///build/config/sanitizers/sanitizers.gni") + +config("warnings") { + if (is_clang) { + cflags = [ + "-Wno-shadow", + + # See crbug.com/932188, libFuzzer does not check the result of write() + # when it does raw printing. + "-Wno-unused-result", + ] + } +} + +# Engine should be compiled without coverage (infinite loop in trace_cmp). +fuzzing_engine_remove_configs = [ + "//build/config/coverage:default_coverage", + "//build/config/sanitizers:default_sanitizer_flags", +] + +# Add any sanitizer flags back. In MSAN builds, instrumenting libfuzzer with +# MSAN is necessary since all parts of the binary need to be instrumented for it +# to work. ASAN builds are more subtle: libfuzzer depends on features from the +# C++ STL. If it were not instrumented, templates would be insantiated without +# ASAN from libfuzzer and with ASAN in other TUs. The linker might merge +# instrumented template instantiations with non-instrumented ones (which could +# have a different ABI) in the final binary, which is problematic for TUs +# expecting one particular ABI (https://crbug.com/915422). The other sanitizers +# are added back for the same reason. +fuzzing_engine_add_configs = [ + "//build/config/sanitizers:default_sanitizer_flags_but_coverage", + ":warnings", +] + +source_set("libfuzzer") { + sources = [ + "src/FuzzerCrossOver.cpp", + "src/FuzzerDataFlowTrace.cpp", + "src/FuzzerDriver.cpp", + "src/FuzzerExtFunctionsDlsym.cpp", + "src/FuzzerExtFunctionsWeak.cpp", + "src/FuzzerExtFunctionsWindows.cpp", + "src/FuzzerExtraCounters.cpp", + "src/FuzzerFork.cpp", + "src/FuzzerIO.cpp", + "src/FuzzerIOPosix.cpp", + "src/FuzzerIOWindows.cpp", + "src/FuzzerLoop.cpp", + "src/FuzzerMerge.cpp", + "src/FuzzerMutate.cpp", + "src/FuzzerSHA1.cpp", + "src/FuzzerTracePC.cpp", + "src/FuzzerUtil.cpp", + "src/FuzzerUtilDarwin.cpp", + "src/FuzzerUtilFuchsia.cpp", + "src/FuzzerUtilLinux.cpp", + "src/FuzzerUtilPosix.cpp", + "src/FuzzerUtilWindows.cpp", + ] + + if (!is_ios) { + sources += [ "src/FuzzerMain.cpp" ] + } + + configs -= fuzzing_engine_remove_configs + configs += fuzzing_engine_add_configs + + deps = [] + if (is_fuchsia) { + deps += [ "//third_party/fuchsia-sdk/sdk:fdio" ] + } +} + +if (use_afl) { + source_set("afl_driver") { + sources = [ "src/afl/afl_driver.cpp" ] + + configs -= fuzzing_engine_remove_configs + configs += fuzzing_engine_add_configs + } +} diff --git a/tools/memory/asan/blocklist_win.txt b/tools/memory/asan/blocklist_win.txt new file mode 100644 index 000000000..35fa055de --- /dev/null +++ b/tools/memory/asan/blocklist_win.txt @@ -0,0 +1,4 @@ +# The rules in this file are only applied at compile time. If you can modify the +# source in question, consider function attributes to disable instrumentation. +# +# Please think twice before you add or remove these rules.