• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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