1// Copyright 2019 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "components/exo/wayland/fuzzer/harness.h" 6 7{% for protocol in protocol_names %} 8 {% if protocol == 'wayland' %} 9 #include <wayland-client-core.h> 10 #include <wayland-client-protocol.h> 11 {% else %} 12 #include <{{protocol | replace('_','-')}}-client-protocol.h> 13 {% endif %} 14{% endfor %} 15 16#include <cstring> 17#include <functional> 18 19#include "base/check.h" 20#include "components/exo/wayland/fuzzer/actions.pb.h" 21 22namespace exo { 23namespace wayland_fuzzer { 24 25{% for interface in interfaces if interface.has_listener and interface.name != 'wl_display' %} 26 namespace {{interface.name}} { 27 {% for event in interface.events %} 28 void {{event.name}}( 29 void* data, 30 {{interface.cpp_type}} receiver 31 {% for arg in event.args %} 32 , {{arg.cpp_type}} {{arg.name}} 33 {% endfor %} 34 ); 35 {% endfor %} 36 static const struct {{interface.name}}_listener kListener = { 37 {% for event in interface.events %} 38 {{event.name}}, 39 {% endfor %} 40 }; 41 } // namespace {{interface.name}} 42{% endfor %} 43 44 45{% for interface in interfaces if interface.name != 'wl_registry' %} 46 namespace {{interface.name}} { 47 {% for event in interface.events %} 48 void {{event.name}}( 49 void* data, 50 {{interface.cpp_type}} receiver 51 {% for arg in event.args %} 52 , {{arg.cpp_type}} {{arg.name}} 53 {% endfor %} 54 ){ 55 {% if event.is_constructor %} 56 Harness* harness = static_cast<Harness*>(data); 57 {% for arg in event.args if arg.type == 'new_id' %} 58 {{arg.cpp_type}} new_object = {{arg.name}}; 59 {% endfor %} 60 {% if event.constructed_has_listener %} 61 {{event.constructed}}_add_listener(new_object, &{{event.constructed}}::kListener, harness); 62 {% endif %} 63 harness->add_{{event.constructed}}(new_object); 64 {% endif %} 65 {% for arg in event.args if arg.type == 'fd' %} 66 FILE* _f = fdopen({{arg.name}}, "w"); 67 CHECK(_f); 68 fwrite(".", 1, 1, _f); 69 fclose(_f); 70 {% endfor %} 71 } 72 {% endfor %} 73 {% for request in interface.requests %} 74 void {{request.name}}(Harness* harness, 75 const actions::{{interface.name}}_{{request.name}}& action) { 76 {{interface.cpp_type}} receiver = harness->get_{{interface.name}}(action.receiver()); 77 if (!receiver) 78 return; 79 {% for arg in request.args %} 80 {% if arg.type == 'object' %} 81 {{arg.cpp_type}} {{arg.name}} = harness->get_{{arg.interface}}(action.{{arg.name}}()); 82 {% if not arg.nullable %} 83 if (!{{arg.name}}) 84 return; 85 {% endif %} 86 {% elif arg.type == 'fd' %} 87 int {{arg.name}} = harness->GetFileDescriptor(action.{{arg.name}}()); 88 {% elif arg.type == 'array' %} 89 wl_array {{arg.name}}{ 90 /*size=*/action.{{arg.name}}().size(), 91 /*alloc=*/action.{{arg.name}}().capacity(), 92 /*data=*/const_cast<char*>(action.{{arg.name}}().data()) 93 }; 94 {% endif %} 95 {% endfor %} 96 97 {% if request.is_constructor %} 98 struct {{request.constructed}}* new_object = 99 {% endif %} 100 // Invoke the wayland method. We need ::method_name in order to 101 // disambiguate methods which might collide with 102 // interface/namespace names. 103 ::{{interface.name}}_{{request.name}}(receiver 104 {% for arg in request.args %} 105 {% if arg.type == 'object' or arg.type == 'fd' %} 106 , {{arg.name}} 107 {% elif arg.type == 'array' %} 108 , &{{arg.name}} 109 {% elif arg.type != 'new_id' %} 110 , action.{{arg.name}}(){% if arg.type == 'string' %}.c_str(){% endif %} 111 {% endif %} 112 {% endfor %} 113 ); 114 {% if request.is_constructor %} 115 {% if request.constructed_has_listener %} 116 {{request.constructed}}_add_listener(new_object, &{{request.constructed}}::kListener, harness); 117 {% endif %} 118 harness->add_{{request.constructed}}(new_object); 119 {% elif request.is_destructor %} 120 harness->remove_{{interface.name}}(action.receiver()); 121 {% endif %} 122 } 123 {% endfor %} 124 } // namespace {{interface.name}} 125{% endfor %} 126 127namespace wl_registry { 128 129void bind(Harness* harness, const actions::wl_registry_bind& action) { 130 struct wl_registry* receiver = harness->get_wl_registry(action.receiver()); 131 if (!receiver) 132 return; 133 switch (action.global()) { 134 {% for interface in interfaces if interface.is_global %} 135 case actions::global::{{interface.name}}: { 136 if (harness->{{interface.name}}_globals_.empty()) 137 return; 138 void* new_object = wl_registry_bind( 139 receiver, harness->{{interface.name}}_globals_[0].first, 140 &{{interface.name}}_interface, harness->{{interface.name}}_globals_[0].second); 141 {% if interface.has_listener and interface.name != 'wl_display' %} 142 {{interface.name}}_add_listener(static_cast<::{{interface.name}}*>(new_object), &{{interface.name}}::kListener, harness); 143 {% endif %} 144 harness->add_{{interface.name}}( 145 static_cast<::{{interface.name}}*>(new_object)); 146 break; 147 } 148 {% endfor %} 149 case actions::global::global_INT_MIN_SENTINEL_DO_NOT_USE_: 150 case actions::global::global_INT_MAX_SENTINEL_DO_NOT_USE_: 151 case actions::global::GLOBAL_UNSPECIFIED: 152 break; 153 } 154} 155 156void global(void* data, 157 struct wl_registry* registry, 158 uint32_t name, 159 const char* interface, 160 uint32_t version) { 161 Harness* harness = static_cast<Harness*>(data); 162 {% for interface in interfaces if interface.is_global %} 163 if (strcmp(interface, "{{interface.name}}") == 0) { 164 harness->{{interface.name}}_globals_.emplace_back(name, version); 165 return; 166 } 167 {% endfor %} 168} 169 170} // namespace wl_registry 171 172void wl_registry::global_remove(void* data, struct wl_registry* registry, uint32_t name) {} 173 174Harness::Harness() { 175 wl_display_list_.emplace_back(wl_display_connect(nullptr)); 176 DCHECK(wl_display_list_.front()); 177} 178 179Harness::~Harness() { 180 {% for interface in interfaces %} 181 for (auto ifc : {{interface.name}}_list_) { 182 if (ifc) 183 {% if interface.name == "wl_display" %} 184 wl_display_disconnect(ifc); 185 {% else %} 186 free(ifc); 187 {% endif %} 188 } 189 {% endfor %} 190} 191 192void Harness::Run(const actions::actions& all_steps) { 193 for (const actions::action& current : all_steps.acts()) 194 Run(current); 195} 196 197void Harness::Run(const actions::action& current_step) { 198 switch (current_step.act_case()) { 199 case actions::action::ActCase::ACT_NOT_SET: 200 wl_display_roundtrip(wl_display_list_.front()); 201 break; 202 {% for interface in interfaces %} 203 {% for request in interface.requests %} 204 case actions::action::ActCase::kAct{{(interface.name + "_" + request.name) | replace('_', ' ') | title | replace(' ', '')}}: 205 {{interface.name}}::{{request.name}}(this, current_step.act_{{interface.name}}_{{request.name}}()); 206 break; 207 {% endfor %} 208 {% endfor %} 209 } 210} 211 212int Harness::GetFileDescriptor(int id) { 213 if (shared_memory_map_.count(id) == 0) { 214 base::UnsafeSharedMemoryRegion region = 215 base::UnsafeSharedMemoryRegion::Create(1); 216 shared_memory_map_.emplace(id, std::move(region)); 217 } 218 base::subtle::ScopedFDPair fd_pair = 219 base::UnsafeSharedMemoryRegion::TakeHandleForSerialization( 220 shared_memory_map_[id].Duplicate()).PassPlatformHandle(); 221 return fd_pair.fd.release(); 222} 223 224} // namespace wayland_fuzzer 225} // namespace exo 226