Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(27)

Side by Side Diff: test/fuzzer/wasm-compile.cc

Issue 2658723006: [wasm] Syntax- and Type-aware Fuzzer (Closed)
Patch Set: fixing nits Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « test/fuzzer/testcfg.py ('k') | test/fuzzer/wasm_compile/foo » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2017 the V8 project 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 <stddef.h>
6 #include <stdint.h>
7 #include <stdlib.h>
8
9 #include <algorithm>
10
11 #include "include/v8.h"
12 #include "src/isolate.h"
13 #include "src/objects-inl.h"
14 #include "src/objects.h"
15 #include "src/ostreams.h"
16 #include "src/wasm/wasm-interpreter.h"
17 #include "src/wasm/wasm-module-builder.h"
18 #include "src/wasm/wasm-module.h"
19 #include "test/common/wasm/test-signatures.h"
20 #include "test/common/wasm/wasm-module-runner.h"
21 #include "test/fuzzer/fuzzer-support.h"
22
23 #define WASM_CODE_FUZZER_HASH_SEED 83
24
25 typedef uint8_t byte;
26
27 using namespace v8::internal::wasm;
28
29 namespace {
30
31 class DataRange {
32 const uint8_t* data_;
33 size_t size_;
34
35 public:
36 DataRange(const uint8_t* data, size_t size) : data_(data), size_(size) {}
37
38 size_t size() const { return size_; }
39
40 std::pair<DataRange, DataRange> split(uint32_t index) const {
41 return std::make_pair(DataRange(data_, index),
42 DataRange(data_ + index, size() - index));
43 }
44
45 std::pair<DataRange, DataRange> split() {
46 uint16_t index = get<uint16_t>();
47 if (size() > 0) {
48 index = index % size();
49 } else {
50 index = 0;
51 }
52 return split(index);
53 }
54
55 template <typename T>
56 T get() {
57 if (size() == 0) {
58 return T();
59 } else {
60 const size_t num_bytes = std::min(sizeof(T), size());
61 T result;
62 memcpy(&result, data_, sizeof(num_bytes));
63 data_ += num_bytes;
64 size_ -= num_bytes;
65 return result;
66 }
67 }
68 };
69
70 class WasmGenerator {
71 template <WasmOpcode Op, ValueType... Args>
72 std::function<void(DataRange)> op() {
73 return [this](DataRange data) {
74 Generate<Args...>(data);
75 builder_->Emit(Op);
76 };
77 }
78
79 template <ValueType T>
80 std::function<void(DataRange)> block() {
81 return [this](DataRange data) {
82 blocks_.push_back(T);
83 builder_->EmitWithU8(
84 kExprBlock, static_cast<uint8_t>(WasmOpcodes::ValueTypeCodeFor(T)));
85 Generate<T>(data);
86 builder_->Emit(kExprEnd);
87 blocks_.pop_back();
88 };
89 }
90
91 template <ValueType T>
92 std::function<void(DataRange)> block_br() {
93 return [this](DataRange data) {
94 blocks_.push_back(T);
95 builder_->EmitWithU8(
96 kExprBlock, static_cast<uint8_t>(WasmOpcodes::ValueTypeCodeFor(T)));
97
98 const uint32_t target_block = data.get<uint32_t>() % blocks_.size();
99 const ValueType break_type = blocks_[target_block];
100
101 Generate(break_type, data);
102 builder_->EmitWithVarInt(kExprBr, target_block);
103 builder_->Emit(kExprEnd);
104 blocks_.pop_back();
105 };
106 }
107
108 public:
109 WasmGenerator(v8::internal::wasm::WasmFunctionBuilder* fn) : builder_(fn) {}
110
111 void Generate(ValueType type, DataRange data);
112
113 template <ValueType T>
114 void Generate(DataRange data);
115
116 template <ValueType T1, ValueType T2, ValueType... Ts>
117 void Generate(DataRange data) {
118 const auto parts = data.split();
119 Generate<T1>(parts.first);
120 Generate<T2, Ts...>(parts.second);
121 }
122
123 private:
124 v8::internal::wasm::WasmFunctionBuilder* builder_;
125 std::vector<ValueType> blocks_;
126 };
127
128 template <>
129 void WasmGenerator::Generate<kWasmI32>(DataRange data) {
130 if (data.size() <= sizeof(uint32_t)) {
131 builder_->EmitI32Const(data.get<uint32_t>());
132 } else {
133 const std::function<void(DataRange)> alternates[] = {
134 op<kExprI32Eqz, kWasmI32>(), //
135 op<kExprI32Eq, kWasmI32, kWasmI32>(),
136 op<kExprI32Ne, kWasmI32, kWasmI32>(),
137 op<kExprI32LtS, kWasmI32, kWasmI32>(),
138 op<kExprI32LtU, kWasmI32, kWasmI32>(),
139 op<kExprI32GeS, kWasmI32, kWasmI32>(),
140 op<kExprI32GeU, kWasmI32, kWasmI32>(),
141
142 op<kExprI64Eqz, kWasmI64>(), //
143 op<kExprI64Eq, kWasmI64, kWasmI64>(),
144 op<kExprI64Ne, kWasmI64, kWasmI64>(),
145 op<kExprI64LtS, kWasmI64, kWasmI64>(),
146 op<kExprI64LtU, kWasmI64, kWasmI64>(),
147 op<kExprI64GeS, kWasmI64, kWasmI64>(),
148 op<kExprI64GeU, kWasmI64, kWasmI64>(),
149
150 op<kExprF32Eq, kWasmF32, kWasmF32>(),
151 op<kExprF32Ne, kWasmF32, kWasmF32>(),
152 op<kExprF32Lt, kWasmF32, kWasmF32>(),
153 op<kExprF32Ge, kWasmF32, kWasmF32>(),
154
155 op<kExprF64Eq, kWasmF64, kWasmF64>(),
156 op<kExprF64Ne, kWasmF64, kWasmF64>(),
157 op<kExprF64Lt, kWasmF64, kWasmF64>(),
158 op<kExprF64Ge, kWasmF64, kWasmF64>(),
159
160 op<kExprI32Add, kWasmI32, kWasmI32>(),
161 op<kExprI32Sub, kWasmI32, kWasmI32>(),
162 op<kExprI32Mul, kWasmI32, kWasmI32>(),
163
164 op<kExprI32DivS, kWasmI32, kWasmI32>(),
165 op<kExprI32DivU, kWasmI32, kWasmI32>(),
166 op<kExprI32RemS, kWasmI32, kWasmI32>(),
167 op<kExprI32RemU, kWasmI32, kWasmI32>(),
168
169 op<kExprI32And, kWasmI32, kWasmI32>(),
170 op<kExprI32Ior, kWasmI32, kWasmI32>(),
171 op<kExprI32Xor, kWasmI32, kWasmI32>(),
172 op<kExprI32Shl, kWasmI32, kWasmI32>(),
173 op<kExprI32ShrU, kWasmI32, kWasmI32>(),
174 op<kExprI32ShrS, kWasmI32, kWasmI32>(),
175 op<kExprI32Ror, kWasmI32, kWasmI32>(),
176 op<kExprI32Rol, kWasmI32, kWasmI32>(),
177
178 op<kExprI32Clz, kWasmI32>(), //
179 op<kExprI32Ctz, kWasmI32>(), //
180 op<kExprI32Popcnt, kWasmI32>(),
181
182 op<kExprI32ConvertI64, kWasmI64>(), //
183 op<kExprI32SConvertF32, kWasmF32>(),
184 op<kExprI32UConvertF32, kWasmF32>(),
185 op<kExprI32SConvertF64, kWasmF64>(),
186 op<kExprI32UConvertF64, kWasmF64>(),
187 op<kExprI32ReinterpretF32, kWasmF32>(),
188
189 block<kWasmI32>(),
190 block_br<kWasmI32>()};
191
192 static_assert(arraysize(alternates) < std::numeric_limits<uint8_t>::max(),
193 "Too many alternates. Replace with a bigger type if needed.");
194 const auto which = data.get<uint8_t>();
195
196 alternates[which % arraysize(alternates)](data);
197 }
198 }
199
200 template <>
201 void WasmGenerator::Generate<kWasmI64>(DataRange data) {
202 if (data.size() <= sizeof(uint64_t)) {
203 const uint8_t bytes[] = {WASM_I64V(data.get<uint64_t>())};
204 builder_->EmitCode(bytes, arraysize(bytes));
205 } else {
206 const std::function<void(DataRange)> alternates[] = {
207 op<kExprI64Add, kWasmI64, kWasmI64>(),
208 op<kExprI64Sub, kWasmI64, kWasmI64>(),
209 op<kExprI64Mul, kWasmI64, kWasmI64>(),
210
211 op<kExprI64DivS, kWasmI64, kWasmI64>(),
212 op<kExprI64DivU, kWasmI64, kWasmI64>(),
213 op<kExprI64RemS, kWasmI64, kWasmI64>(),
214 op<kExprI64RemU, kWasmI64, kWasmI64>(),
215
216 op<kExprI64And, kWasmI64, kWasmI64>(),
217 op<kExprI64Ior, kWasmI64, kWasmI64>(),
218 op<kExprI64Xor, kWasmI64, kWasmI64>(),
219 op<kExprI64Shl, kWasmI64, kWasmI64>(),
220 op<kExprI64ShrU, kWasmI64, kWasmI64>(),
221 op<kExprI64ShrS, kWasmI64, kWasmI64>(),
222 op<kExprI64Ror, kWasmI64, kWasmI64>(),
223 op<kExprI64Rol, kWasmI64, kWasmI64>(),
224
225 op<kExprI64Clz, kWasmI64>(),
226 op<kExprI64Ctz, kWasmI64>(),
227 op<kExprI64Popcnt, kWasmI64>(),
228
229 block<kWasmI64>(),
230 block_br<kWasmI64>()};
231
232 static_assert(arraysize(alternates) < std::numeric_limits<uint8_t>::max(),
233 "Too many alternates. Replace with a bigger type if needed.");
234 const auto which = data.get<uint8_t>();
235
236 alternates[which % arraysize(alternates)](data);
237 }
238 }
239
240 template <>
241 void WasmGenerator::Generate<kWasmF32>(DataRange data) {
242 if (data.size() <= sizeof(uint32_t)) {
243 const uint32_t i = data.get<uint32_t>();
244 builder_->Emit(kExprF32Const);
245 builder_->EmitCode(reinterpret_cast<const uint8_t*>(&i), sizeof(i));
246 } else {
247 const std::function<void(DataRange)> alternates[] = {
248 op<kExprF32Add, kWasmF32, kWasmF32>(),
249 op<kExprF32Sub, kWasmF32, kWasmF32>(),
250 op<kExprF32Mul, kWasmF32, kWasmF32>(),
251
252 block<kWasmF32>(), block_br<kWasmF32>()};
253
254 static_assert(arraysize(alternates) < std::numeric_limits<uint8_t>::max(),
255 "Too many alternates. Replace with a bigger type if needed.");
256 const auto which = data.get<uint8_t>();
257
258 alternates[which % arraysize(alternates)](data);
259 }
260 }
261
262 template <>
263 void WasmGenerator::Generate<kWasmF64>(DataRange data) {
264 if (data.size() <= sizeof(uint64_t)) {
265 // TODO (eholk): generate full 64-bit constants
266 uint64_t i = 0;
267 while (data.size() > 0) {
268 i <<= 8;
269 i |= data.get<uint8_t>();
270 }
271 builder_->Emit(kExprF64Const);
272 builder_->EmitCode(reinterpret_cast<uint8_t*>(&i), sizeof(i));
273 } else {
274 const std::function<void(DataRange)> alternates[] = {
275 op<kExprF64Add, kWasmF64, kWasmF64>(),
276 op<kExprF64Sub, kWasmF64, kWasmF64>(),
277 op<kExprF64Mul, kWasmF64, kWasmF64>(),
278
279 block<kWasmF64>(), block_br<kWasmF64>()};
280
281 static_assert(arraysize(alternates) < std::numeric_limits<uint8_t>::max(),
282 "Too many alternates. Replace with a bigger type if needed.");
283 const auto which = data.get<uint8_t>();
284
285 alternates[which % arraysize(alternates)](data);
286 }
287 }
288
289 void WasmGenerator::Generate(ValueType type, DataRange data) {
290 switch (type) {
291 case kWasmI32:
292 return Generate<kWasmI32>(data);
293 case kWasmI64:
294 return Generate<kWasmI64>(data);
295 case kWasmF32:
296 return Generate<kWasmF32>(data);
297 case kWasmF64:
298 return Generate<kWasmF64>(data);
299 default:
300 UNREACHABLE();
301 }
302 }
303 }
304
305 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
306 // Save the flag so that we can change it and restore it later.
307 bool generate_test = v8::internal::FLAG_wasm_code_fuzzer_gen_test;
308 if (generate_test) {
309 v8::internal::OFStream os(stdout);
310
311 os << "// Copyright 2017 the V8 project authors. All rights reserved."
312 << std::endl;
313 os << "// Use of this source code is governed by a BSD-style license that "
314 "can be"
315 << std::endl;
316 os << "// found in the LICENSE file." << std::endl;
317 os << std::endl;
318 os << "load(\"test/mjsunit/wasm/wasm-constants.js\");" << std::endl;
319 os << "load(\"test/mjsunit/wasm/wasm-module-builder.js\");" << std::endl;
320 os << std::endl;
321 os << "(function() {" << std::endl;
322 os << " var builder = new WasmModuleBuilder();" << std::endl;
323 os << " builder.addMemory(16, 32, false);" << std::endl;
324 os << " builder.addFunction(\"test\", kSig_i_iii)" << std::endl;
325 os << " .addBodyWithEnd([" << std::endl;
326 }
327 v8_fuzzer::FuzzerSupport* support = v8_fuzzer::FuzzerSupport::Get();
328 v8::Isolate* isolate = support->GetIsolate();
329 v8::internal::Isolate* i_isolate =
330 reinterpret_cast<v8::internal::Isolate*>(isolate);
331
332 // Clear any pending exceptions from a prior run.
333 if (i_isolate->has_pending_exception()) {
334 i_isolate->clear_pending_exception();
335 }
336
337 v8::Isolate::Scope isolate_scope(isolate);
338 v8::HandleScope handle_scope(isolate);
339 v8::Context::Scope context_scope(support->GetContext());
340 v8::TryCatch try_catch(isolate);
341
342 v8::internal::AccountingAllocator allocator;
343 v8::internal::Zone zone(&allocator, ZONE_NAME);
344
345 TestSignatures sigs;
346
347 WasmModuleBuilder builder(&zone);
348
349 v8::internal::wasm::WasmFunctionBuilder* f =
350 builder.AddFunction(sigs.i_iii());
351
352 WasmGenerator gen(f);
353 gen.Generate<kWasmI32>(DataRange(data, static_cast<uint32_t>(size)));
354
355 uint8_t end_opcode = kExprEnd;
356 f->EmitCode(&end_opcode, 1);
357 f->ExportAs(v8::internal::CStrVector("main"));
358
359 ZoneBuffer buffer(&zone);
360 builder.WriteTo(buffer);
361
362 v8::internal::wasm::testing::SetupIsolateForWasmModule(i_isolate);
363
364 v8::internal::HandleScope scope(i_isolate);
365
366 ErrorThrower interpreter_thrower(i_isolate, "Interpreter");
367 std::unique_ptr<const WasmModule> module(testing::DecodeWasmModuleForTesting(
368 i_isolate, &interpreter_thrower, buffer.begin(), buffer.end(),
369 v8::internal::wasm::ModuleOrigin::kWasmOrigin, true));
370
371 // Clear the flag so that the WebAssembly code is not printed twice.
372 v8::internal::FLAG_wasm_code_fuzzer_gen_test = false;
373 if (module == nullptr) {
374 if (generate_test) {
375 v8::internal::OFStream os(stdout);
376 os << " ])" << std::endl;
377 os << " .exportFunc();" << std::endl;
378 os << " assertThrows(function() { builder.instantiate(); });"
379 << std::endl;
380 os << "})();" << std::endl;
381 }
382 return 0;
383 }
384 if (generate_test) {
385 v8::internal::OFStream os(stdout);
386 os << " ])" << std::endl;
387 os << " .exportFunc();" << std::endl;
388 os << " var module = builder.instantiate();" << std::endl;
389 os << " module.exports.test(1, 2, 3);" << std::endl;
390 os << "})();" << std::endl;
391 }
392
393 ModuleWireBytes wire_bytes(buffer.begin(), buffer.end());
394 int32_t result_interpreted;
395 bool possible_nondeterminism = false;
396 {
397 WasmVal args[] = {WasmVal(1), WasmVal(2), WasmVal(3)};
398 result_interpreted = testing::InterpretWasmModule(
399 i_isolate, &interpreter_thrower, module.get(), wire_bytes, 0, args,
400 &possible_nondeterminism);
401 }
402
403 ErrorThrower compiler_thrower(i_isolate, "Compiler");
404 v8::internal::Handle<v8::internal::JSObject> instance =
405 testing::InstantiateModuleForTesting(i_isolate, &compiler_thrower,
406 module.get(), wire_bytes);
407 // Restore the flag.
408 v8::internal::FLAG_wasm_code_fuzzer_gen_test = generate_test;
409 if (!interpreter_thrower.error()) {
410 CHECK(!instance.is_null());
411 } else {
412 return 0;
413 }
414 int32_t result_compiled;
415 {
416 v8::internal::Handle<v8::internal::Object> arguments[] = {
417 v8::internal::handle(v8::internal::Smi::FromInt(1), i_isolate),
418 v8::internal::handle(v8::internal::Smi::FromInt(2), i_isolate),
419 v8::internal::handle(v8::internal::Smi::FromInt(3), i_isolate)};
420 result_compiled = testing::CallWasmFunctionForTesting(
421 i_isolate, instance, &compiler_thrower, "main", arraysize(arguments),
422 arguments, v8::internal::wasm::ModuleOrigin::kWasmOrigin);
423 }
424 if (result_interpreted == bit_cast<int32_t>(0xdeadbeef) &&
425 !possible_nondeterminism) {
426 CHECK(i_isolate->has_pending_exception());
427 i_isolate->clear_pending_exception();
428 } else {
429 // The WebAssembly spec allows the sign bit of NaN to be non-deterministic.
430 // This sign bit may cause result_interpreted to be different than
431 // result_compiled. Therefore we do not check the equality of the results
432 // if the execution may have produced a NaN at some point.
433 if (!possible_nondeterminism && (result_interpreted != result_compiled)) {
434 printf("\nInterpreter returned 0x%x but compiled code returned 0x%x\n",
435 result_interpreted, result_compiled);
436 V8_Fatal(__FILE__, __LINE__, "WasmCodeFuzzerHash=%x",
437 v8::internal::StringHasher::HashSequentialString(
438 data, static_cast<int>(size), WASM_CODE_FUZZER_HASH_SEED));
439 }
440 }
441 return 0;
442 }
OLDNEW
« no previous file with comments | « test/fuzzer/testcfg.py ('k') | test/fuzzer/wasm_compile/foo » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698