Reland "fuzzing: Add supportsErrorInjection option to DawnWireServerFuzzer"

This is a reland of f58f69f66b

The whitebox dawn_end2end_tests are updated to link statically against
libdawn_native. This is required because the test link against and use
libdawn_native as sources. It is an error with MSVC to both import and
export symbols from libdawn_native.

Original change's description:
> fuzzing: Add supportsErrorInjection option to DawnWireServerFuzzer
>
> This option will be used by backends that support error injection so
> that errors can be injected into a "clean" corpus to generate a seed
> corpus with good examples of injected error conditions.
>
> Bug: dawn:295
> Change-Id: I837acdde6dd4274adb56edf8e4307427f8d6333b
> Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/14681
> Reviewed-by: Corentin Wallez <cwallez@chromium.org>
> Commit-Queue: Austin Eng <enga@chromium.org>

Bug: dawn:295
Change-Id: Ifa092d28aa7ac57cfb197aa4daeb8408f8036d4a
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/14820
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
Commit-Queue: Austin Eng <enga@chromium.org>
This commit is contained in:
Austin Eng
2020-01-06 19:56:17 +00:00
committed by Commit Bot service account
parent d08a14b709
commit 470921fe46
17 changed files with 215 additions and 61 deletions

View File

@@ -45,6 +45,10 @@ namespace {
} // namespace
extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) {
return 0;
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
return DawnSPIRVCrossFuzzer::Run(data, size, GLSLFastFuzzTask);
}

View File

@@ -51,6 +51,10 @@ namespace {
} // namespace
extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) {
return 0;
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
return DawnSPIRVCrossFuzzer::Run(data, size, FuzzTask);
}

View File

@@ -44,6 +44,10 @@ namespace {
} // namespace
extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) {
return 0;
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
return DawnSPIRVCrossFuzzer::Run(data, size, FuzzTask);
}

View File

@@ -18,6 +18,10 @@
#include "spvc/spvc.hpp"
extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) {
return 0;
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
shaderc_spvc::Context context;
if (!context.IsValid()) {

View File

@@ -18,6 +18,10 @@
#include "spvc/spvc.hpp"
extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) {
return 0;
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
shaderc_spvc::Context context;
if (!context.IsValid()) {

View File

@@ -18,6 +18,10 @@
#include "spvc/spvc.hpp"
extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) {
return 0;
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
shaderc_spvc::Context context;
if (!context.IsValid()) {

View File

@@ -17,21 +17,28 @@
#include "common/Assert.h"
#include "dawn_native/DawnNative.h"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
return DawnWireServerFuzzer::Run(data, size, [](dawn_native::Instance* instance) {
instance->DiscoverDefaultAdapters();
std::vector<dawn_native::Adapter> adapters = instance->GetAdapters();
wgpu::Device nullDevice;
for (dawn_native::Adapter adapter : adapters) {
if (adapter.GetBackendType() == dawn_native::BackendType::Null) {
nullDevice = wgpu::Device::Acquire(adapter.CreateDevice());
break;
}
}
ASSERT(nullDevice.Get() != nullptr);
return nullDevice;
});
extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) {
return DawnWireServerFuzzer::Initialize(argc, argv);
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
return DawnWireServerFuzzer::Run(
data, size,
[](dawn_native::Instance* instance) {
instance->DiscoverDefaultAdapters();
std::vector<dawn_native::Adapter> adapters = instance->GetAdapters();
wgpu::Device nullDevice;
for (dawn_native::Adapter adapter : adapters) {
if (adapter.GetBackendType() == dawn_native::BackendType::Null) {
nullDevice = wgpu::Device::Acquire(adapter.CreateDevice());
break;
}
}
ASSERT(nullDevice.Get() != nullptr);
return nullDevice;
},
false /* supportsErrorInjection */);
}

View File

@@ -14,25 +14,29 @@
#include "DawnWireServerFuzzer.h"
#include "common/Assert.h"
#include "dawn_native/DawnNative.h"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
return DawnWireServerFuzzer::Run(data, size, [](dawn_native::Instance* instance) {
instance->DiscoverDefaultAdapters();
std::vector<dawn_native::Adapter> adapters = instance->GetAdapters();
wgpu::Device device;
for (dawn_native::Adapter adapter : adapters) {
if (adapter.GetBackendType() == dawn_native::BackendType::Vulkan &&
adapter.GetDeviceType() == dawn_native::DeviceType::CPU) {
device = wgpu::Device::Acquire(adapter.CreateDevice());
break;
}
}
ASSERT(device.Get() != nullptr);
return device;
});
extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) {
return DawnWireServerFuzzer::Initialize(argc, argv);
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
return DawnWireServerFuzzer::Run(
data, size,
[](dawn_native::Instance* instance) {
instance->DiscoverDefaultAdapters();
std::vector<dawn_native::Adapter> adapters = instance->GetAdapters();
wgpu::Device device;
for (dawn_native::Adapter adapter : adapters) {
if (adapter.GetBackendType() == dawn_native::BackendType::Vulkan &&
adapter.GetDeviceType() == dawn_native::DeviceType::CPU) {
device = wgpu::Device::Acquire(adapter.CreateDevice());
break;
}
}
return device;
},
true /* supportsErrorInjection */);
}

View File

@@ -15,11 +15,14 @@
#include "DawnWireServerFuzzer.h"
#include "common/Assert.h"
#include "common/Log.h"
#include "common/SystemUtils.h"
#include "dawn/dawn_proc.h"
#include "dawn/webgpu_cpp.h"
#include "dawn_native/DawnNative.h"
#include "dawn_wire/WireServer.h"
#include <fstream>
#include <vector>
namespace {
@@ -42,6 +45,9 @@ namespace {
WGPUProcDeviceCreateSwapChain sOriginalDeviceCreateSwapChain = nullptr;
std::string sInjectedErrorTestcaseOutDir;
uint64_t sOutputFileNumber = 0;
WGPUSwapChain ErrorDeviceCreateSwapChain(WGPUDevice device, const WGPUSwapChainDescriptor*) {
WGPUSwapChainDescriptor desc;
desc.nextInChain = nullptr;
@@ -53,7 +59,61 @@ namespace {
} // namespace
int DawnWireServerFuzzer::Run(const uint8_t* data, size_t size, MakeDeviceFn MakeDevice) {
int DawnWireServerFuzzer::Initialize(int* argc, char*** argv) {
ASSERT(argc != nullptr && argv != nullptr);
// The first argument (the fuzzer binary) always stays the same.
int argcOut = 1;
for (int i = 1; i < *argc; ++i) {
constexpr const char kInjectedErrorTestcaseDirArg[] = "--injected-error-testcase-dir=";
if (strstr((*argv)[i], kInjectedErrorTestcaseDirArg) == (*argv)[i]) {
sInjectedErrorTestcaseOutDir = (*argv)[i] + strlen(kInjectedErrorTestcaseDirArg);
const char* sep = GetPathSeparator();
if (sInjectedErrorTestcaseOutDir.back() != *sep) {
sInjectedErrorTestcaseOutDir += sep;
}
// Log so that it's clear the fuzzer found the argument.
dawn::InfoLog() << "Generating injected errors, output dir is: \""
<< sInjectedErrorTestcaseOutDir << "\"";
continue;
}
// Move any unconsumed arguments to the next slot in the output array.
(*argv)[argcOut++] = (*argv)[i];
}
// Write the argument count
*argc = argcOut;
return 0;
}
int DawnWireServerFuzzer::Run(const uint8_t* data,
size_t size,
MakeDeviceFn MakeDevice,
bool supportsErrorInjection) {
bool didInjectError = false;
if (supportsErrorInjection) {
dawn_native::EnableErrorInjector();
// Clear the error injector since it has the previous run's call counts.
dawn_native::ClearErrorInjector();
// If we're outputing testcases with injected errors, we run the fuzzer on the original
// input data, and prepend injected errors to it. In the case, where we're NOT outputing,
// we use the first bytes as the injected error index.
if (sInjectedErrorTestcaseOutDir.empty() && size >= sizeof(uint64_t)) {
// Otherwise, use the first bytes as the injected error index.
dawn_native::InjectErrorAt(*reinterpret_cast<const uint64_t*>(data));
didInjectError = true;
data += sizeof(uint64_t);
size -= sizeof(uint64_t);
}
}
DawnProcTable procs = dawn_native::GetProcs();
// Swapchains receive a pointer to an implementation. The fuzzer will pass garbage in so we
@@ -67,7 +127,11 @@ int DawnWireServerFuzzer::Run(const uint8_t* data, size_t size, MakeDeviceFn Mak
std::unique_ptr<dawn_native::Instance> instance = std::make_unique<dawn_native::Instance>();
wgpu::Device device = MakeDevice(instance.get());
ASSERT(device);
if (!device) {
// We should only ever fail device creation if an error was injected.
ASSERT(didInjectError);
return 0;
}
DevNull devNull;
dawn_wire::WireServerDescriptor serverDesc = {};
@@ -87,5 +151,27 @@ int DawnWireServerFuzzer::Run(const uint8_t* data, size_t size, MakeDeviceFn Mak
device = nullptr;
instance = nullptr;
// If we support error injection, and an output directory was provided, output copies of the
// original testcase data, prepended with the injected error index.
if (supportsErrorInjection && !sInjectedErrorTestcaseOutDir.empty()) {
const uint64_t injectedCallCount = dawn_native::AcquireErrorInjectorCallCount();
auto WriteTestcase = [&](uint64_t i) {
std::ofstream outFile(
sInjectedErrorTestcaseOutDir + "injected_error_testcase_" +
std::to_string(sOutputFileNumber++),
std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
outFile.write(reinterpret_cast<const char*>(&i), sizeof(i));
outFile.write(reinterpret_cast<const char*>(data), size);
};
for (uint64_t i = 0; i < injectedCallCount; ++i) {
WriteTestcase(i);
}
// Also add a testcase where the injected error is so large no errors should occur.
WriteTestcase(std::numeric_limits<uint64_t>::max());
}
return 0;
}

View File

@@ -27,6 +27,8 @@ namespace DawnWireServerFuzzer {
using MakeDeviceFn = std::function<wgpu::Device(dawn_native::Instance*)>;
int Run(const uint8_t* data, size_t size, MakeDeviceFn MakeDevice);
int Initialize(int* argc, char*** argv);
int Run(const uint8_t* data, size_t size, MakeDeviceFn MakeDevice, bool supportsErrorInjection);
} // namespace DawnWireServerFuzzer

View File

@@ -17,11 +17,17 @@
#include <iostream>
#include <vector>
extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv);
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size);
int main(int argc, char** argv) {
if (LLVMFuzzerInitialize(&argc, &argv)) {
std::cerr << "Failed to initialize fuzzer target" << std::endl;
return 1;
}
if (argc != 2) {
std::cout << "Usage: <standalone reproducer> FILE" << std::endl;
std::cout << "Usage: <standalone reproducer> [options] FILE" << std::endl;
return 1;
}