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

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

Issue 2658723006: [wasm] Syntax- and Type-aware Fuzzer (Closed)
Patch Set: Code review feedback 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 // TODO(ahaas): Uncomment this once the nondeterminism detection is
188 // working again.
189 //
190 // op<kExprI32ReinterpretF32, kWasmF32>(),
ahaas 2017/02/17 09:03:52 Nit: I fixed this issue already.
Eric Holk 2017/02/17 16:42:09 Done.
191
192 block<kWasmI32>(), block_br<kWasmI32>()};
193
194 static_assert(arraysize(alternates) < std::numeric_limits<uint8_t>::max(),
195 "Too many alternates. Replace with a bigger type if needed.");
196 const auto which = data.get<uint8_t>();
197
198 alternates[which % arraysize(alternates)](data);
199 }
200 }
201
202 template <>
203 void WasmGenerator::Generate<kWasmI64>(DataRange data) {
204 if (data.size() <= sizeof(uint64_t)) {
205 const uint8_t bytes[] = {WASM_I64V(data.get<uint64_t>())};
206 builder_->EmitCode(bytes, arraysize(bytes));
207 } else {
208 const std::function<void(DataRange)> alternates[] = {
209 op<kExprI64Add, kWasmI64, kWasmI64>(),
210 op<kExprI64Sub, kWasmI64, kWasmI64>(),
211 op<kExprI64Mul, kWasmI64, kWasmI64>(),
212
213 op<kExprI64DivS, kWasmI64, kWasmI64>(),
214 op<kExprI64DivU, kWasmI64, kWasmI64>(),
215 op<kExprI64RemS, kWasmI64, kWasmI64>(),
216 op<kExprI64RemU, kWasmI64, kWasmI64>(),
217
218 op<kExprI64And, kWasmI64, kWasmI64>(),
219 op<kExprI64Ior, kWasmI64, kWasmI64>(),
220 op<kExprI64Xor, kWasmI64, kWasmI64>(),
221 op<kExprI64Shl, kWasmI64, kWasmI64>(),
222 op<kExprI64ShrU, kWasmI64, kWasmI64>(),
223 op<kExprI64ShrS, kWasmI64, kWasmI64>(),
224 op<kExprI64Ror, kWasmI64, kWasmI64>(),
225 op<kExprI64Rol, kWasmI64, kWasmI64>(),
226
227 op<kExprI64Clz, kWasmI64>(),
228 op<kExprI64Ctz, kWasmI64>(),
229 op<kExprI64Popcnt, kWasmI64>(),
230
231 block<kWasmI64>(),
232 block_br<kWasmI64>()};
233
234 static_assert(arraysize(alternates) < std::numeric_limits<uint8_t>::max(),
235 "Too many alternates. Replace with a bigger type if needed.");
236 const auto which = data.get<uint8_t>();
237
238 alternates[which % arraysize(alternates)](data);
239 }
240 }
241
242 template <>
243 void WasmGenerator::Generate<kWasmF32>(DataRange data) {
244 if (data.size() <= sizeof(uint32_t)) {
245 const uint32_t i = data.get<uint32_t>();
246 builder_->Emit(kExprF32Const);
247 builder_->EmitCode(reinterpret_cast<const uint8_t*>(&i), sizeof(i));
248 } else {
249 const std::function<void(DataRange)> alternates[] = {
250 op<kExprF32Add, kWasmF32, kWasmF32>(),
251 op<kExprF32Sub, kWasmF32, kWasmF32>(),
252 op<kExprF32Mul, kWasmF32, kWasmF32>(),
253
254 block<kWasmF32>(), block_br<kWasmF32>()};
255
256 static_assert(arraysize(alternates) < std::numeric_limits<uint8_t>::max(),
257 "Too many alternates. Replace with a bigger type if needed.");
258 const auto which = data.get<uint8_t>();
259
260 alternates[which % arraysize(alternates)](data);
261 }
262 }
263
264 template <>
265 void WasmGenerator::Generate<kWasmF64>(DataRange data) {
266 if (data.size() <= sizeof(uint64_t)) {
267 // TODO (eholk): generate full 64-bit constants
268 uint64_t i = 0;
269 while (data.size() > 0) {
270 i <<= 8;
271 i |= data.get<uint8_t>();
272 }
273 builder_->Emit(kExprF64Const);
274 builder_->EmitCode(reinterpret_cast<uint8_t*>(&i), sizeof(i));
275 } else {
276 const std::function<void(DataRange)> alternates[] = {
277 op<kExprF64Add, kWasmF64, kWasmF64>(),
278 op<kExprF64Sub, kWasmF64, kWasmF64>(),
279 op<kExprF64Mul, kWasmF64, kWasmF64>(),
280
281 block<kWasmF64>(), block_br<kWasmF64>()};
282
283 static_assert(arraysize(alternates) < std::numeric_limits<uint8_t>::max(),
284 "Too many alternates. Replace with a bigger type if needed.");
285 const auto which = data.get<uint8_t>();
286
287 alternates[which % arraysize(alternates)](data);
288 }
289 }
290
291 void WasmGenerator::Generate(ValueType type, DataRange data) {
292 switch (type) {
293 case kWasmI32:
294 return Generate<kWasmI32>(data);
295 case kWasmI64:
296 return Generate<kWasmI64>(data);
297 case kWasmF32:
298 return Generate<kWasmF32>(data);
299 case kWasmF64:
300 return Generate<kWasmF64>(data);
301 default:
302 UNREACHABLE();
303 }
304 }
305 }
306
307 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
308 // Save the flag so that we can change it and restore it later.
309 bool generate_test = v8::internal::FLAG_wasm_code_fuzzer_gen_test;
310 if (generate_test) {
311 v8::internal::OFStream os(stdout);
312
313 os << "// Copyright 2017 the V8 project authors. All rights reserved."
314 << std::endl;
315 os << "// Use of this source code is governed by a BSD-style license that "
316 "can be"
317 << std::endl;
318 os << "// found in the LICENSE file." << std::endl;
319 os << std::endl;
320 os << "load(\"test/mjsunit/wasm/wasm-constants.js\");" << std::endl;
321 os << "load(\"test/mjsunit/wasm/wasm-module-builder.js\");" << std::endl;
322 os << std::endl;
323 os << "(function() {" << std::endl;
324 os << " var builder = new WasmModuleBuilder();" << std::endl;
325 os << " builder.addMemory(16, 32, false);" << std::endl;
326 os << " builder.addFunction(\"test\", kSig_i_iii)" << std::endl;
327 os << " .addBodyWithEnd([" << std::endl;
328 }
329 v8_fuzzer::FuzzerSupport* support = v8_fuzzer::FuzzerSupport::Get();
330 v8::Isolate* isolate = support->GetIsolate();
331 v8::internal::Isolate* i_isolate =
332 reinterpret_cast<v8::internal::Isolate*>(isolate);
333
334 // Clear any pending exceptions from a prior run.
335 if (i_isolate->has_pending_exception()) {
336 i_isolate->clear_pending_exception();
337 }
338
339 v8::Isolate::Scope isolate_scope(isolate);
340 v8::HandleScope handle_scope(isolate);
341 v8::Context::Scope context_scope(support->GetContext());
342 v8::TryCatch try_catch(isolate);
343
344 v8::internal::AccountingAllocator allocator;
345 v8::internal::Zone zone(&allocator, ZONE_NAME);
346
347 TestSignatures sigs;
348
349 WasmModuleBuilder builder(&zone);
350
351 v8::internal::wasm::WasmFunctionBuilder* f =
352 builder.AddFunction(sigs.i_iii());
353
354 WasmGenerator gen(f);
355 gen.Generate<kWasmI32>(DataRange(data, static_cast<uint32_t>(size)));
356
357 uint8_t end_opcode = kExprEnd;
358 f->EmitCode(&end_opcode, 1);
359 f->ExportAs(v8::internal::CStrVector("main"));
360
361 ZoneBuffer buffer(&zone);
362 builder.WriteTo(buffer);
363
364 v8::internal::wasm::testing::SetupIsolateForWasmModule(i_isolate);
365
366 v8::internal::HandleScope scope(i_isolate);
367
368 ErrorThrower interpreter_thrower(i_isolate, "Interpreter");
369 std::unique_ptr<const WasmModule> module(testing::DecodeWasmModuleForTesting(
370 i_isolate, &interpreter_thrower, buffer.begin(), buffer.end(),
371 v8::internal::wasm::ModuleOrigin::kWasmOrigin, true));
372
373 // Clear the flag so that the WebAssembly code is not printed twice.
374 v8::internal::FLAG_wasm_code_fuzzer_gen_test = false;
375 if (module == nullptr) {
376 if (generate_test) {
377 v8::internal::OFStream os(stdout);
378 os << " ])" << std::endl;
379 os << " .exportFunc();" << std::endl;
380 os << " assertThrows(function() { builder.instantiate(); });"
381 << std::endl;
382 os << "})();" << std::endl;
383 }
384 return 0;
385 }
386 if (generate_test) {
387 v8::internal::OFStream os(stdout);
388 os << " ])" << std::endl;
389 os << " .exportFunc();" << std::endl;
390 os << " var module = builder.instantiate();" << std::endl;
391 os << " module.exports.test(1, 2, 3);" << std::endl;
392 os << "})();" << std::endl;
393 }
394
395 ModuleWireBytes wire_bytes(buffer.begin(), buffer.end());
396 int32_t result_interpreted;
397 bool possible_nondeterminism = false;
398 {
399 WasmVal args[] = {WasmVal(1), WasmVal(2), WasmVal(3)};
400 result_interpreted = testing::InterpretWasmModule(
401 i_isolate, &interpreter_thrower, module.get(), wire_bytes, 0, args,
402 &possible_nondeterminism);
403 }
404
405 ErrorThrower compiler_thrower(i_isolate, "Compiler");
406 v8::internal::Handle<v8::internal::JSObject> instance =
407 testing::InstantiateModuleForTesting(i_isolate, &compiler_thrower,
408 module.get(), wire_bytes);
409 // Restore the flag.
410 v8::internal::FLAG_wasm_code_fuzzer_gen_test = generate_test;
411 if (!interpreter_thrower.error()) {
412 CHECK(!instance.is_null());
413 } else {
414 return 0;
415 }
416 int32_t result_compiled;
417 {
418 v8::internal::Handle<v8::internal::Object> arguments[] = {
419 v8::internal::handle(v8::internal::Smi::FromInt(1), i_isolate),
420 v8::internal::handle(v8::internal::Smi::FromInt(2), i_isolate),
421 v8::internal::handle(v8::internal::Smi::FromInt(3), i_isolate)};
422 result_compiled = testing::CallWasmFunctionForTesting(
423 i_isolate, instance, &compiler_thrower, "main", arraysize(arguments),
424 arguments, v8::internal::wasm::ModuleOrigin::kWasmOrigin);
425 }
426 if (result_interpreted == bit_cast<int32_t>(0xdeadbeef) &&
427 !possible_nondeterminism) {
428 CHECK(i_isolate->has_pending_exception());
429 i_isolate->clear_pending_exception();
430 } else {
431 // The WebAssembly spec allows the sign bit of NaN to be non-deterministic.
432 // This sign bit may cause result_interpreted to be different than
433 // result_compiled. Therefore we do not check the equality of the results
434 // if the execution may have produced a NaN at some point.
435 if (!possible_nondeterminism && (result_interpreted != result_compiled)) {
436 printf("Interpreter returned 0x%x but compiled code returned 0x%x\n",
437 result_interpreted, result_compiled);
438 V8_Fatal(__FILE__, __LINE__, "WasmCodeFuzzerHash=%x",
439 v8::internal::StringHasher::HashSequentialString(
440 data, static_cast<int>(size), WASM_CODE_FUZZER_HASH_SEED));
441 }
442 }
443 return 0;
444 }
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