mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-12-09 21:47:47 +00:00
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>
This commit is contained in:
committed by
Commit Bot service account
parent
d28b578b6b
commit
f58f69f66b
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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 */);
|
||||
}
|
||||
|
||||
@@ -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 */);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user