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

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

Issue 2658723006: [wasm] Syntax- and Type-aware Fuzzer (Closed)
Patch Set: Adding support for more binops, etc. 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 <utility>
10
11 #include "include/v8.h"
12 #include "src/isolate.h"
13 #include "src/objects.h"
14 #include "src/ostreams.h"
15 #include "src/wasm/wasm-interpreter.h"
16 #include "src/wasm/wasm-module-builder.h"
17 #include "src/wasm/wasm-module.h"
18 #include "test/common/wasm/test-signatures.h"
19 #include "test/common/wasm/wasm-module-runner.h"
20 #include "test/fuzzer/fuzzer-support.h"
21
22 #define WASM_CODE_FUZZER_HASH_SEED 83
23
24 typedef uint8_t byte;
25
26 using namespace v8::internal::wasm;
27
28 namespace {
29
30 class DataRange {
31 const uint8_t* data_;
32 uint32_t size_;
33
34 public:
35 DataRange(const uint8_t* data, uint32_t size) : data_(data), size_(size) {}
36
37 uint32_t size() const { return size_; }
38
39 std::pair<DataRange, DataRange> split(uint32_t index) const {
40 return std::make_pair(DataRange(data_, index),
41 DataRange(data_ + index, size() - index));
42 }
43
44 std::pair<DataRange, DataRange> split() {
45 uint32_t index = get<uint32_t>();
ahaas 2017/02/06 14:36:21 I think even a uint8_t index would be sufficient h
Eric Holk 2017/02/16 22:05:31 I have an associated Chromium CL at https://chromi
ahaas 2017/02/17 09:03:52 sounds good
46 if (size() > 0) {
47 index = index % size();
48 } else {
49 index = 0;
50 }
51 return split(index);
52 }
53
54 template <typename T>
55 T get() {
56 if (sizeof(T) > size()) {
57 return T();
58 } else {
59 T result;
60 memcpy(&result, data_, sizeof(result));
61 data_ += sizeof(result);
62 size_ -= sizeof(result);
63 return result;
64 }
65 }
66 };
67
68 class WasmGenerator {
69 template <WasmOpcode Op, ValueType... Args>
ahaas 2017/02/06 14:36:21 Is it worth to have a copy of this code for every
70 std::function<void(DataRange)> op() {
71 return [this](DataRange data) {
ahaas 2017/02/06 14:36:21 I'm surprised that you have all these template fun
Eric Holk 2017/02/16 22:05:31 I'll admit I was probably overly clever/ambitious
ahaas 2017/02/17 09:03:52 I guess as long it is fast enough it is okay.
72 Generate<Args...>(data);
73 fn_->Emit(Op);
74 };
75 }
76
77 template <ValueType T>
78 std::function<void(DataRange)> block() {
79 return [this](DataRange data) {
80 blocks_.push_back(T);
81 fn_->EmitWithU8(kExprBlock,
82 static_cast<uint8_t>(WasmOpcodes::ValueTypeCodeFor(T)));
83 Generate<T>(data);
84 fn_->Emit(kExprEnd);
85 blocks_.pop_back();
86 };
87 }
88
89 template <ValueType T>
90 std::function<void(DataRange)> block_br() {
91 return [this](DataRange data) {
92 blocks_.push_back(T);
93 fn_->EmitWithU8(kExprBlock,
94 static_cast<uint8_t>(WasmOpcodes::ValueTypeCodeFor(T)));
95
96 const uint32_t target_block = data.get<uint32_t>() % blocks_.size();
97 const ValueType break_type = blocks_[target_block];
98
99 Generate(break_type, data);
100 fn_->EmitWithVarInt(kExprBr, target_block);
101 fn_->Emit(kExprEnd);
102 blocks_.pop_back();
103 };
104 }
105
106 public:
107 WasmGenerator(v8::internal::wasm::WasmFunctionBuilder* fn) : fn_(fn) {}
108
109 void Generate(ValueType type, DataRange data);
110
111 template <ValueType T>
112 void Generate(DataRange data);
113
114 template <ValueType T1, ValueType T2, ValueType... Ts>
115 void Generate(DataRange data) {
116 const auto parts = data.split();
117 Generate<T1>(parts.first);
118 Generate<T2, Ts...>(parts.second);
119 }
120
121 private:
122 v8::internal::wasm::WasmFunctionBuilder* fn_;
ahaas 2017/02/06 14:36:21 I think the code would be easier to read if the Wa
Eric Holk 2017/02/16 22:05:31 Done.
123 std::vector<ValueType> blocks_;
124 };
125
126 template <>
127 void WasmGenerator::Generate<kWasmI32>(DataRange data) {
128 if (data.size() <= sizeof(uint32_t)) {
129 uint32_t i = 0;
ahaas 2017/02/06 14:36:21 I think you could extract this const construction
Eric Holk 2017/02/16 22:05:31 Yeah, this is actually basically what DataRange::g
130 while (data.size() > 0) {
131 i <<= 8;
132 i |= data.get<uint8_t>();
133 }
134 fn_->EmitI32Const(i);
135 } else {
136 const std::function<void(DataRange)> alternates[] = {
137 op<kExprI32Eqz, kWasmI32>(), op<kExprI32Eq, kWasmI32, kWasmI32>(),
ahaas 2017/02/06 14:36:21 I think with // -- you could avoid two operations
Eric Holk 2017/02/16 22:05:31 Thanks for the tip! Done.
138 op<kExprI32Ne, kWasmI32, kWasmI32>(),
139 op<kExprI32LtS, kWasmI32, kWasmI32>(),
140 op<kExprI32LtU, kWasmI32, kWasmI32>(),
141 op<kExprI32GeS, kWasmI32, kWasmI32>(),
142 op<kExprI32GeU, kWasmI32, kWasmI32>(),
143
144 op<kExprI64Eqz, kWasmI64>(), op<kExprI64Eq, kWasmI64, kWasmI64>(),
145 op<kExprI64Ne, kWasmI64, kWasmI64>(),
146 op<kExprI64LtS, kWasmI64, kWasmI64>(),
147 op<kExprI64LtU, kWasmI64, kWasmI64>(),
148 op<kExprI64GeS, kWasmI64, kWasmI64>(),
149 op<kExprI64GeU, kWasmI64, kWasmI64>(),
150
151 op<kExprF32Eq, kWasmF32, kWasmF32>(),
152 op<kExprF32Ne, kWasmF32, kWasmF32>(),
153 op<kExprF32Lt, kWasmF32, kWasmF32>(),
154 op<kExprF32Ge, kWasmF32, kWasmF32>(),
155
156 op<kExprF64Eq, kWasmF64, kWasmF64>(),
157 op<kExprF64Ne, kWasmF64, kWasmF64>(),
158 op<kExprF64Lt, kWasmF64, kWasmF64>(),
159 op<kExprF64Ge, kWasmF64, kWasmF64>(),
160
161 op<kExprI32Add, kWasmI32, kWasmI32>(),
162 op<kExprI32Sub, kWasmI32, kWasmI32>(),
163 op<kExprI32Mul, kWasmI32, kWasmI32>(),
164
165 op<kExprI32DivS, kWasmI32, kWasmI32>(),
166 op<kExprI32DivU, kWasmI32, kWasmI32>(),
167 op<kExprI32RemS, kWasmI32, kWasmI32>(),
168 op<kExprI32RemU, kWasmI32, kWasmI32>(),
169
170 op<kExprI32And, kWasmI32, kWasmI32>(),
171 op<kExprI32Ior, kWasmI32, kWasmI32>(),
172 op<kExprI32Xor, kWasmI32, kWasmI32>(),
173 op<kExprI32Shl, kWasmI32, kWasmI32>(),
174 op<kExprI32ShrU, kWasmI32, kWasmI32>(),
175 op<kExprI32ShrS, kWasmI32, kWasmI32>(),
176 op<kExprI32Ror, kWasmI32, kWasmI32>(),
177 op<kExprI32Rol, kWasmI32, kWasmI32>(),
178
179 op<kExprI32Clz, kWasmI32>(), op<kExprI32Ctz, kWasmI32>(),
180 op<kExprI32Popcnt, kWasmI32>(),
181
182 op<kExprI32ConvertI64, kWasmI64>(), op<kExprI32SConvertF32, kWasmF32>(),
183 op<kExprI32UConvertF32, kWasmF32>(),
184 op<kExprI32SConvertF64, kWasmF64>(),
185 op<kExprI32UConvertF64, kWasmF64>(),
186 // TODO(ahaas): Uncomment this once the nondeterminism detection is
187 // working again.
188 //
189 // op<kExprI32ReinterpretF32, kWasmF32>(),
190
191 block<kWasmI32>(), block_br<kWasmI32>()};
192
193 const auto which = data.get<uint8_t>();
194
195 alternates[which % arraysize(alternates)](data);
196 }
197 }
198
199 template <>
200 void WasmGenerator::Generate<kWasmI64>(DataRange data) {
201 if (data.size() <= sizeof(uint64_t)) {
202 // TODO (eholk): generate full 64-bit constants
203 uint64_t i = 0;
204 while (data.size() > 0) {
205 i <<= 8;
206 i |= data.get<uint8_t>();
207 }
208 const uint8_t bytes[] = {WASM_I64V(i)};
209 fn_->EmitCode(bytes, arraysize(bytes));
210 } else {
211 const std::function<void(DataRange)> alternates[] = {
ahaas 2017/02/06 14:36:21 What is the reason why you do not use the definiti
Eric Holk 2017/02/16 22:05:31 When I first wrote this I wasn't quite ready for a
Eric Holk 2017/02/16 23:57:28 Actually, I just looked at using those macros agai
Eric Holk 2017/02/17 00:06:33 And also, the opcodes aren't grouped by return typ
ahaas 2017/02/17 09:03:52 Eventually we could think about refactoring wasm-o
Eric Holk 2017/02/17 16:42:09 I think this is a good idea. We should also do som
212 op<kExprI64Add, kWasmI64, kWasmI64>(),
213 op<kExprI64Sub, kWasmI64, kWasmI64>(),
214 op<kExprI64Mul, kWasmI64, kWasmI64>(),
215
216 op<kExprI64DivS, kWasmI64, kWasmI64>(),
217 op<kExprI64DivU, kWasmI64, kWasmI64>(),
218 op<kExprI64RemS, kWasmI64, kWasmI64>(),
219 op<kExprI64RemU, kWasmI64, kWasmI64>(),
220
221 op<kExprI64And, kWasmI64, kWasmI64>(),
222 op<kExprI64Ior, kWasmI64, kWasmI64>(),
223 op<kExprI64Xor, kWasmI64, kWasmI64>(),
224 op<kExprI64Shl, kWasmI64, kWasmI64>(),
225 op<kExprI64ShrU, kWasmI64, kWasmI64>(),
226 op<kExprI64ShrS, kWasmI64, kWasmI64>(),
227 op<kExprI64Ror, kWasmI64, kWasmI64>(),
228 op<kExprI64Rol, kWasmI64, kWasmI64>(),
229
230 op<kExprI64Clz, kWasmI64>(),
231 op<kExprI64Ctz, kWasmI64>(),
232 op<kExprI64Popcnt, kWasmI64>(),
233
234 block<kWasmI64>(),
235 block_br<kWasmI64>()};
236
237 const auto which = data.get<uint8_t>();
238
239 alternates[which % arraysize(alternates)](data);
240 }
241 }
242
243 template <>
244 void WasmGenerator::Generate<kWasmF32>(DataRange data) {
245 if (data.size() <= sizeof(uint32_t)) {
246 // TODO (eholk): generate full 64-bit constants
247 uint32_t i = 0;
248 while (data.size() > 0) {
249 i <<= 8;
250 i |= data.get<uint8_t>();
251 }
252 fn_->Emit(kExprF32Const);
253 fn_->EmitCode(reinterpret_cast<uint8_t*>(&i), sizeof(i));
254 } else {
255 const std::function<void(DataRange)> alternates[] = {
256 op<kExprF32Add, kWasmF32, kWasmF32>(),
ahaas 2017/02/06 14:36:20 Why did you not add the other float instructions?
Eric Holk 2017/02/16 22:05:31 I just wanted to get a few in as a proof of concep
257 op<kExprF32Sub, kWasmF32, kWasmF32>(),
258 op<kExprF32Mul, kWasmF32, kWasmF32>(),
259
260 block<kWasmF32>(), block_br<kWasmF32>()};
261
262 const auto which = data.get<uint8_t>();
263
264 alternates[which % arraysize(alternates)](data);
265 }
266 }
267
268 template <>
269 void WasmGenerator::Generate<kWasmF64>(DataRange data) {
270 if (data.size() <= sizeof(uint64_t)) {
271 // TODO (eholk): generate full 64-bit constants
272 uint64_t i = 0;
273 while (data.size() > 0) {
274 i <<= 8;
275 i |= data.get<uint8_t>();
276 }
277 fn_->Emit(kExprF64Const);
278 fn_->EmitCode(reinterpret_cast<uint8_t*>(&i), sizeof(i));
279 } else {
280 const std::function<void(DataRange)> alternates[] = {
281 op<kExprF64Add, kWasmF64, kWasmF64>(),
282 op<kExprF64Sub, kWasmF64, kWasmF64>(),
283 op<kExprF64Mul, kWasmF64, kWasmF64>(),
284
285 block<kWasmF64>(), block_br<kWasmF64>()};
286
287 const auto which = data.get<uint8_t>();
288
289 alternates[which % arraysize(alternates)](data);
290 }
291 }
292
293 void WasmGenerator::Generate(ValueType type, DataRange data) {
294 switch (type) {
295 case kWasmI32:
296 return Generate<kWasmI32>(data);
297 case kWasmI64:
298 return Generate<kWasmI64>(data);
299 case kWasmF32:
300 return Generate<kWasmF32>(data);
301 case kWasmF64:
302 return Generate<kWasmF64>(data);
303 default:
304 abort();
ahaas 2017/02/06 14:36:21 I think we typically use UNREACHABLE(); in V8 and
Eric Holk 2017/02/16 22:05:31 Done.
305 }
306 }
307 }
308
309 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
310 // Save the flag so that we can change it and restore it later.
311 bool generate_test = v8::internal::FLAG_wasm_code_fuzzer_gen_test;
312 if (generate_test) {
313 v8::internal::OFStream os(stdout);
314
315 os << "// Copyright 2017 the V8 project authors. All rights reserved."
ahaas 2017/02/06 14:36:21 Does the test generation work for your fuzzer? Tha
Eric Holk 2017/02/16 22:05:31 Yup, it works! The ast-fuzz tests I included in th
316 << std::endl;
317 os << "// Use of this source code is governed by a BSD-style license that "
318 "can be"
319 << std::endl;
320 os << "// found in the LICENSE file." << std::endl;
321 os << std::endl;
322 os << "load(\"test/mjsunit/wasm/wasm-constants.js\");" << std::endl;
323 os << "load(\"test/mjsunit/wasm/wasm-module-builder.js\");" << std::endl;
324 os << std::endl;
325 os << "(function() {" << std::endl;
326 os << " var builder = new WasmModuleBuilder();" << std::endl;
327 os << " builder.addMemory(32, 32, false);" << std::endl;
ahaas 2017/02/06 14:36:21 In the WasmModuleBuilder the initial memory size i
Eric Holk 2017/02/16 22:05:31 Done. We should probably factor the common code b
328 os << " builder.addFunction(\"test\", kSig_i_iii)" << std::endl;
329 os << " .addBodyWithEnd([" << std::endl;
330 }
331 v8_fuzzer::FuzzerSupport* support = v8_fuzzer::FuzzerSupport::Get();
332 v8::Isolate* isolate = support->GetIsolate();
333 v8::internal::Isolate* i_isolate =
334 reinterpret_cast<v8::internal::Isolate*>(isolate);
335
336 // Clear any pending exceptions from a prior run.
337 if (i_isolate->has_pending_exception()) {
338 i_isolate->clear_pending_exception();
339 }
340
341 v8::Isolate::Scope isolate_scope(isolate);
342 v8::HandleScope handle_scope(isolate);
343 v8::Context::Scope context_scope(support->GetContext());
344 v8::TryCatch try_catch(isolate);
345
346 v8::internal::AccountingAllocator allocator;
347 v8::internal::Zone zone(&allocator, ZONE_NAME);
348
349 TestSignatures sigs;
350
351 WasmModuleBuilder builder(&zone);
352
353 v8::internal::wasm::WasmFunctionBuilder* f =
354 builder.AddFunction(sigs.i_iii());
ahaas 2017/02/06 14:36:21 Do you use the 3 Parameters?
Eric Holk 2017/02/16 22:05:31 Not yet, but it's an improvement I'd like to add.
355
356 WasmGenerator gen(f);
357 gen.Generate<kWasmI32>(DataRange(data, static_cast<uint32_t>(size)));
358
359 uint8_t end_opcode = kExprEnd;
360 f->EmitCode(&end_opcode, 1);
361 f->ExportAs(v8::internal::CStrVector("main"));
362
363 ZoneBuffer buffer(&zone);
364 builder.WriteTo(buffer);
365
366 v8::internal::wasm::testing::SetupIsolateForWasmModule(i_isolate);
367
368 v8::internal::HandleScope scope(i_isolate);
369
370 ErrorThrower interpreter_thrower(i_isolate, "Interpreter");
371 std::unique_ptr<const WasmModule> module(testing::DecodeWasmModuleForTesting(
372 i_isolate, &interpreter_thrower, buffer.begin(), buffer.end(),
373 v8::internal::wasm::ModuleOrigin::kWasmOrigin, true));
374
375 // Clear the flag so that the WebAssembly code is not printed twice.
376 v8::internal::FLAG_wasm_code_fuzzer_gen_test = false;
377 if (module == nullptr) {
378 if (generate_test) {
379 v8::internal::OFStream os(stdout);
380 os << " ])" << std::endl;
381 os << " .exportFunc();" << std::endl;
382 os << " assertThrows(function() { builder.instantiate(); });"
383 << std::endl;
384 os << "})();" << std::endl;
385 }
386 return 0;
387 }
388 if (generate_test) {
389 v8::internal::OFStream os(stdout);
390 os << " ])" << std::endl;
391 os << " .exportFunc();" << std::endl;
392 os << " var module = builder.instantiate();" << std::endl;
393 os << " module.exports.test(1, 2, 3);" << std::endl;
394 os << "})();" << std::endl;
395 }
396
397 ModuleWireBytes wire_bytes(buffer.begin(), buffer.end());
398 int32_t result_interpreted;
399 bool possible_nondeterminism = false;
400 {
401 WasmVal args[] = {WasmVal(1), WasmVal(2), WasmVal(3)};
402 result_interpreted = testing::InterpretWasmModule(
403 i_isolate, &interpreter_thrower, module.get(), wire_bytes, 0, args,
404 &possible_nondeterminism);
405 }
406
407 ErrorThrower compiler_thrower(i_isolate, "Compiler");
408 v8::internal::Handle<v8::internal::JSObject> instance =
409 testing::InstantiateModuleForTesting(i_isolate, &compiler_thrower,
410 module.get(), wire_bytes);
411 // Restore the flag.
412 v8::internal::FLAG_wasm_code_fuzzer_gen_test = generate_test;
413 if (!interpreter_thrower.error()) {
414 CHECK(!instance.is_null());
415 } else {
416 return 0;
417 }
418 int32_t result_compiled;
419 {
420 v8::internal::Handle<v8::internal::Object> arguments[] = {
421 v8::internal::handle(v8::internal::Smi::FromInt(1), i_isolate),
422 v8::internal::handle(v8::internal::Smi::FromInt(2), i_isolate),
423 v8::internal::handle(v8::internal::Smi::FromInt(3), i_isolate)};
424 result_compiled = testing::CallWasmFunctionForTesting(
425 i_isolate, instance, &compiler_thrower, "main", arraysize(arguments),
426 arguments, v8::internal::wasm::ModuleOrigin::kWasmOrigin);
427 }
428 if (result_interpreted == bit_cast<int32_t>(0xdeadbeef)) {
ahaas 2017/02/06 14:36:21 Do not check for the exception if there may be non
ahaas 2017/02/06 14:36:21 Do not check for the exception if there may be non
Eric Holk 2017/02/16 22:05:31 Done.
429 CHECK(i_isolate->has_pending_exception());
430 i_isolate->clear_pending_exception();
431 } else {
432 // The WebAssembly spec allows the sign bit of NaN to be non-deterministic.
433 // This sign bit may cause result_interpreted to be different than
434 // result_compiled. Therefore we do not check the equality of the results
435 // if the execution may have produced a NaN at some point.
436 if (!possible_nondeterminism && (result_interpreted != result_compiled)) {
437 V8_Fatal(__FILE__, __LINE__, "WasmCodeFuzzerHash=%x",
438 v8::internal::StringHasher::HashSequentialString(
439 data, static_cast<int>(size), WASM_CODE_FUZZER_HASH_SEED));
440 }
441 }
442 return 0;
443 }
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