1 // Copyright 2019 The Dawn Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "DawnWireServerFuzzer.h"
16
17 #include "common/Assert.h"
18 #include "common/Log.h"
19 #include "common/SystemUtils.h"
20 #include "dawn/dawn_proc.h"
21 #include "dawn/webgpu_cpp.h"
22 #include "dawn_native/DawnNative.h"
23 #include "dawn_wire/WireServer.h"
24 #include "utils/SystemUtils.h"
25
26 #include <fstream>
27 #include <vector>
28
29 namespace {
30
31 class DevNull : public dawn_wire::CommandSerializer {
32 public:
GetMaximumAllocationSize() const33 size_t GetMaximumAllocationSize() const override {
34 // Some fuzzer bots have a 2GB allocation limit. Pick a value reasonably below that.
35 return 1024 * 1024 * 1024;
36 }
GetCmdSpace(size_t size)37 void* GetCmdSpace(size_t size) override {
38 if (size > buf.size()) {
39 buf.resize(size);
40 }
41 return buf.data();
42 }
Flush()43 bool Flush() override {
44 return true;
45 }
46
47 private:
48 std::vector<char> buf;
49 };
50
51 std::unique_ptr<dawn_native::Instance> sInstance;
52 WGPUProcDeviceCreateSwapChain sOriginalDeviceCreateSwapChain = nullptr;
53
54 bool sCommandsComplete = false;
55
ErrorDeviceCreateSwapChain(WGPUDevice device,WGPUSurface surface,const WGPUSwapChainDescriptor *)56 WGPUSwapChain ErrorDeviceCreateSwapChain(WGPUDevice device,
57 WGPUSurface surface,
58 const WGPUSwapChainDescriptor*) {
59 WGPUSwapChainDescriptor desc = {};
60 // A 0 implementation will trigger a swapchain creation error.
61 desc.implementation = 0;
62 return sOriginalDeviceCreateSwapChain(device, surface, &desc);
63 }
64
65 } // namespace
66
Initialize(int * argc,char *** argv)67 int DawnWireServerFuzzer::Initialize(int* argc, char*** argv) {
68 // TODO(crbug.com/1038952): The Instance must be static because destructing the vkInstance with
69 // Swiftshader crashes libFuzzer. When this is fixed, move this into Run so that error injection
70 // for adapter discovery can be fuzzed.
71 sInstance = std::make_unique<dawn_native::Instance>();
72 sInstance->DiscoverDefaultAdapters();
73
74 return 0;
75 }
76
Run(const uint8_t * data,size_t size,MakeDeviceFn MakeDevice,bool supportsErrorInjection)77 int DawnWireServerFuzzer::Run(const uint8_t* data,
78 size_t size,
79 MakeDeviceFn MakeDevice,
80 bool supportsErrorInjection) {
81 // We require at least the injected error index.
82 if (size < sizeof(uint64_t)) {
83 return 0;
84 }
85
86 // Get and consume the injected error index.
87 uint64_t injectedErrorIndex = *reinterpret_cast<const uint64_t*>(data);
88 data += sizeof(uint64_t);
89 size -= sizeof(uint64_t);
90
91 if (supportsErrorInjection) {
92 dawn_native::EnableErrorInjector();
93
94 // Clear the error injector since it has the previous run's call counts.
95 dawn_native::ClearErrorInjector();
96
97 dawn_native::InjectErrorAt(injectedErrorIndex);
98 }
99
100 DawnProcTable procs = dawn_native::GetProcs();
101
102 // Swapchains receive a pointer to an implementation. The fuzzer will pass garbage in so we
103 // intercept calls to create swapchains and make sure they always return error swapchains.
104 // This is ok for fuzzing because embedders of dawn_wire would always define their own
105 // swapchain handling.
106 sOriginalDeviceCreateSwapChain = procs.deviceCreateSwapChain;
107 procs.deviceCreateSwapChain = ErrorDeviceCreateSwapChain;
108
109 dawnProcSetProcs(&procs);
110
111 wgpu::Device device = MakeDevice(sInstance.get());
112 if (!device) {
113 // We should only ever fail device creation if an error was injected.
114 ASSERT(supportsErrorInjection);
115 return 0;
116 }
117
118 DevNull devNull;
119 dawn_wire::WireServerDescriptor serverDesc = {};
120 serverDesc.procs = &procs;
121 serverDesc.serializer = &devNull;
122
123 std::unique_ptr<dawn_wire::WireServer> wireServer(new dawn_wire::WireServer(serverDesc));
124 wireServer->InjectDevice(device.Get(), 1, 0);
125
126 wireServer->HandleCommands(reinterpret_cast<const char*>(data), size);
127
128 // Wait for all previous commands before destroying the server.
129 // TODO(enga): Improve this when we improve/finalize how processing events happens.
130 {
131 device.GetQueue().OnSubmittedWorkDone(
132 0u, [](WGPUQueueWorkDoneStatus, void*) { sCommandsComplete = true; }, nullptr);
133 while (!sCommandsComplete) {
134 device.Tick();
135 utils::USleep(100);
136 }
137 }
138
139 wireServer = nullptr;
140 return 0;
141 }
142