LCOV - code coverage report
Current view: top level - test/fuzzer - wasm-fuzzer-common.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 46 144 31.9 %
Date: 2019-04-19 Functions: 3 6 50.0 %

          Line data    Source code
       1             : // Copyright 2016 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 "test/fuzzer/wasm-fuzzer-common.h"
       6             : 
       7             : #include <ctime>
       8             : 
       9             : #include "include/v8.h"
      10             : #include "src/isolate.h"
      11             : #include "src/objects-inl.h"
      12             : #include "src/ostreams.h"
      13             : #include "src/wasm/wasm-engine.h"
      14             : #include "src/wasm/wasm-module-builder.h"
      15             : #include "src/wasm/wasm-module.h"
      16             : #include "src/wasm/wasm-objects-inl.h"
      17             : #include "src/zone/accounting-allocator.h"
      18             : #include "src/zone/zone.h"
      19             : #include "test/common/wasm/flag-utils.h"
      20             : #include "test/common/wasm/wasm-module-runner.h"
      21             : #include "test/fuzzer/fuzzer-support.h"
      22             : 
      23             : namespace v8 {
      24             : namespace internal {
      25             : namespace wasm {
      26             : namespace fuzzer {
      27             : 
      28           2 : void InterpretAndExecuteModule(i::Isolate* isolate,
      29             :                                Handle<WasmModuleObject> module_object) {
      30             :   // We do not instantiate the module if there is a start function, because a
      31             :   // start function can contain an infinite loop which we cannot handle.
      32           6 :   if (module_object->module()->start_function_index >= 0) return;
      33             : 
      34           0 :   ErrorThrower thrower(isolate, "WebAssembly Instantiation");
      35             :   MaybeHandle<WasmInstanceObject> maybe_instance;
      36             :   Handle<WasmInstanceObject> instance;
      37             : 
      38             :   // Try to instantiate and interpret the module_object.
      39             :   maybe_instance = isolate->wasm_engine()->SyncInstantiate(
      40             :       isolate, &thrower, module_object,
      41             :       Handle<JSReceiver>::null(),     // imports
      42           4 :       MaybeHandle<JSArrayBuffer>());  // memory
      43           2 :   if (!maybe_instance.ToHandle(&instance)) {
      44             :     isolate->clear_pending_exception();
      45           1 :     thrower.Reset();  // Ignore errors.
      46           1 :     return;
      47             :   }
      48           1 :   if (!testing::InterpretWasmModuleForTesting(isolate, instance, "main", 0,
      49             :                                               nullptr)) {
      50             :     isolate->clear_pending_exception();
      51             :     return;
      52             :   }
      53             : 
      54             :   // Try to instantiate and execute the module_object.
      55             :   maybe_instance = isolate->wasm_engine()->SyncInstantiate(
      56             :       isolate, &thrower, module_object,
      57             :       Handle<JSReceiver>::null(),     // imports
      58           0 :       MaybeHandle<JSArrayBuffer>());  // memory
      59           0 :   if (!maybe_instance.ToHandle(&instance)) {
      60             :     isolate->clear_pending_exception();
      61           0 :     thrower.Reset();  // Ignore errors.
      62           0 :     return;
      63             :   }
      64           0 :   if (testing::RunWasmModuleForTesting(isolate, instance, 0, nullptr) < 0) {
      65             :     isolate->clear_pending_exception();
      66             :     return;
      67             :   }
      68             : }
      69             : 
      70             : namespace {
      71           0 : struct PrintSig {
      72             :   const size_t num;
      73             :   const std::function<ValueType(size_t)> getter;
      74             : };
      75             : PrintSig PrintParameters(const FunctionSig* sig) {
      76           0 :   return {sig->parameter_count(), [=](size_t i) { return sig->GetParam(i); }};
      77             : }
      78             : PrintSig PrintReturns(const FunctionSig* sig) {
      79           0 :   return {sig->return_count(), [=](size_t i) { return sig->GetReturn(i); }};
      80             : }
      81           0 : const char* ValueTypeToConstantName(ValueType type) {
      82           0 :   switch (type) {
      83             :     case kWasmI32:
      84             :       return "kWasmI32";
      85             :     case kWasmI64:
      86           0 :       return "kWasmI64";
      87             :     case kWasmF32:
      88           0 :       return "kWasmF32";
      89             :     case kWasmF64:
      90           0 :       return "kWasmF64";
      91             :     default:
      92           0 :       UNREACHABLE();
      93             :   }
      94             : }
      95           0 : std::ostream& operator<<(std::ostream& os, const PrintSig& print) {
      96           0 :   os << "[";
      97           0 :   for (size_t i = 0; i < print.num; ++i) {
      98           0 :     os << (i == 0 ? "" : ", ") << ValueTypeToConstantName(print.getter(i));
      99             :   }
     100           0 :   return os << "]";
     101             : }
     102             : 
     103             : struct PrintName {
     104             :   WasmName name;
     105             :   PrintName(ModuleWireBytes wire_bytes, WireBytesRef ref)
     106           0 :       : name(wire_bytes.GetNameOrNull(ref)) {}
     107             : };
     108             : std::ostream& operator<<(std::ostream& os, const PrintName& name) {
     109           0 :   return os.write(name.name.start(), name.name.size());
     110             : }
     111             : }  // namespace
     112             : 
     113           0 : void GenerateTestCase(Isolate* isolate, ModuleWireBytes wire_bytes,
     114             :                       bool compiles) {
     115             :   constexpr bool kVerifyFunctions = false;
     116           0 :   auto enabled_features = i::wasm::WasmFeaturesFromIsolate(isolate);
     117           0 :   ModuleResult module_res = DecodeWasmModule(
     118             :       enabled_features, wire_bytes.start(), wire_bytes.end(), kVerifyFunctions,
     119             :       ModuleOrigin::kWasmOrigin, isolate->counters(),
     120           0 :       isolate->wasm_engine()->allocator());
     121           0 :   CHECK(module_res.ok());
     122             :   WasmModule* module = module_res.value().get();
     123           0 :   CHECK_NOT_NULL(module);
     124             : 
     125           0 :   StdoutStream os;
     126             : 
     127           0 :   tzset();
     128           0 :   time_t current_time = time(nullptr);
     129             :   struct tm current_localtime;
     130             : #ifdef V8_OS_WIN
     131             :   localtime_s(&current_localtime, &current_time);
     132             : #else
     133           0 :   localtime_r(&current_time, &current_localtime);
     134             : #endif
     135           0 :   int year = 1900 + current_localtime.tm_year;
     136             : 
     137           0 :   os << "// Copyright " << year
     138             :      << " the V8 project authors. All rights reserved.\n"
     139             :         "// Use of this source code is governed by a BSD-style license that "
     140             :         "can be\n"
     141             :         "// found in the LICENSE file.\n"
     142             :         "\n"
     143             :         "load('test/mjsunit/wasm/wasm-module-builder.js');\n"
     144             :         "\n"
     145             :         "(function() {\n"
     146           0 :         "  const builder = new WasmModuleBuilder();\n";
     147             : 
     148           0 :   if (module->has_memory) {
     149           0 :     os << "  builder.addMemory(" << module->initial_pages;
     150           0 :     if (module->has_maximum_pages) {
     151           0 :       os << ", " << module->maximum_pages;
     152             :     } else {
     153           0 :       os << ", undefined";
     154             :     }
     155           0 :     os << ", " << (module->mem_export ? "true" : "false");
     156           0 :     if (module->has_shared_memory) {
     157           0 :       os << ", true";
     158             :     }
     159           0 :     os << ");\n";
     160             :   }
     161             : 
     162           0 :   for (WasmGlobal& glob : module->globals) {
     163           0 :     os << "  builder.addGlobal(" << ValueTypeToConstantName(glob.type) << ", "
     164           0 :        << glob.mutability << ");\n";
     165             :   }
     166             : 
     167           0 :   for (const FunctionSig* sig : module->signatures) {
     168           0 :     os << "  builder.addType(makeSig(" << PrintParameters(sig) << ", "
     169           0 :        << PrintReturns(sig) << "));\n";
     170             :   }
     171             : 
     172           0 :   Zone tmp_zone(isolate->allocator(), ZONE_NAME);
     173             : 
     174             :   // There currently cannot be more than one table.
     175             :   DCHECK_GE(1, module->tables.size());
     176           0 :   for (const WasmTable& table : module->tables) {
     177           0 :     os << "  builder.setTableBounds(" << table.initial_size << ", ";
     178           0 :     if (table.has_maximum_size) {
     179           0 :       os << table.maximum_size << ");\n";
     180             :     } else {
     181           0 :       os << "undefined);\n";
     182             :     }
     183             :   }
     184           0 :   for (const WasmElemSegment& elem_segment : module->elem_segments) {
     185           0 :     os << "  builder.addElementSegment(";
     186           0 :     switch (elem_segment.offset.kind) {
     187             :       case WasmInitExpr::kGlobalIndex:
     188           0 :         os << elem_segment.offset.val.global_index << ", true";
     189           0 :         break;
     190             :       case WasmInitExpr::kI32Const:
     191           0 :         os << elem_segment.offset.val.i32_const << ", false";
     192           0 :         break;
     193             :       default:
     194           0 :         UNREACHABLE();
     195             :     }
     196           0 :     os << ", " << PrintCollection(elem_segment.entries) << ");\n";
     197             :   }
     198             : 
     199           0 :   for (const WasmFunction& func : module->functions) {
     200             :     Vector<const uint8_t> func_code = wire_bytes.GetFunctionBytes(&func);
     201           0 :     os << "  // Generate function " << (func.func_index + 1) << " (out of "
     202           0 :        << module->functions.size() << ").\n";
     203             : 
     204             :     // Add function.
     205           0 :     os << "  builder.addFunction(undefined, " << func.sig_index
     206           0 :        << " /* sig */)\n";
     207             : 
     208             :     // Add locals.
     209             :     BodyLocalDecls decls(&tmp_zone);
     210             :     DecodeLocalDecls(enabled_features, &decls, func_code.start(),
     211           0 :                      func_code.end());
     212           0 :     if (!decls.type_list.empty()) {
     213           0 :       os << "    ";
     214           0 :       for (size_t pos = 0, count = 1, locals = decls.type_list.size();
     215           0 :            pos < locals; pos += count, count = 1) {
     216           0 :         ValueType type = decls.type_list[pos];
     217           0 :         while (pos + count < locals && decls.type_list[pos + count] == type)
     218           0 :           ++count;
     219           0 :         os << ".addLocals({" << ValueTypes::TypeName(type)
     220           0 :            << "_count: " << count << "})";
     221             :       }
     222           0 :       os << "\n";
     223             :     }
     224             : 
     225             :     // Add body.
     226           0 :     os << "    .addBodyWithEnd([\n";
     227             : 
     228           0 :     FunctionBody func_body(func.sig, func.code.offset(), func_code.start(),
     229             :                            func_code.end());
     230           0 :     PrintRawWasmCode(isolate->allocator(), func_body, module, kOmitLocals);
     231           0 :     os << "            ]);\n";
     232             :   }
     233             : 
     234           0 :   for (WasmExport& exp : module->export_table) {
     235           0 :     if (exp.kind != kExternalFunction) continue;
     236           0 :     os << "  builder.addExport('" << PrintName(wire_bytes, exp.name) << "', "
     237           0 :        << exp.index << ");\n";
     238             :   }
     239             : 
     240           0 :   if (compiles) {
     241             :     os << "  const instance = builder.instantiate();\n"
     242           0 :           "  print(instance.exports.main(1, 2, 3));\n";
     243             :   } else {
     244             :     os << "  assertThrows(function() { builder.instantiate(); }, "
     245           0 :           "WebAssembly.CompileError);\n";
     246             :   }
     247           0 :   os << "})();\n";
     248           0 : }
     249             : 
     250           2 : void WasmExecutionFuzzer::FuzzWasmModule(Vector<const uint8_t> data,
     251             :                                          bool require_valid) {
     252             :   // Strictly enforce the input size limit. Note that setting "max_len" on the
     253             :   // fuzzer target is not enough, since different fuzzers are used and not all
     254             :   // respect that limit.
     255           3 :   if (data.size() > max_input_size()) return;
     256             : 
     257           2 :   v8_fuzzer::FuzzerSupport* support = v8_fuzzer::FuzzerSupport::Get();
     258             :   v8::Isolate* isolate = support->GetIsolate();
     259             :   i::Isolate* i_isolate = reinterpret_cast<Isolate*>(isolate);
     260             : 
     261             :   // Clear any pending exceptions from a prior run.
     262             :   i_isolate->clear_pending_exception();
     263             : 
     264             :   v8::Isolate::Scope isolate_scope(isolate);
     265           3 :   v8::HandleScope handle_scope(isolate);
     266           2 :   v8::Context::Scope context_scope(support->GetContext());
     267           3 :   v8::TryCatch try_catch(isolate);
     268             :   HandleScope scope(i_isolate);
     269             : 
     270           3 :   AccountingAllocator allocator;
     271           3 :   Zone zone(&allocator, ZONE_NAME);
     272             : 
     273             :   ZoneBuffer buffer(&zone);
     274           2 :   int32_t num_args = 0;
     275           2 :   std::unique_ptr<WasmValue[]> interpreter_args;
     276           2 :   std::unique_ptr<Handle<Object>[]> compiler_args;
     277             :   // The first byte builds the bitmask to control which function will be
     278             :   // compiled with Turbofan and which one with Liftoff.
     279           2 :   uint8_t tier_mask = data.empty() ? 0 : data[0];
     280           2 :   if (!data.empty()) data += 1;
     281           2 :   if (!GenerateModule(i_isolate, &zone, data, buffer, num_args,
     282           2 :                       interpreter_args, compiler_args)) {
     283             :     return;
     284             :   }
     285             : 
     286           2 :   testing::SetupIsolateForWasmModule(i_isolate);
     287             : 
     288           1 :   ErrorThrower interpreter_thrower(i_isolate, "Interpreter");
     289             :   ModuleWireBytes wire_bytes(buffer.begin(), buffer.end());
     290             : 
     291             :   // Compile with Turbofan here. Liftoff will be tested later.
     292           2 :   auto enabled_features = i::wasm::WasmFeaturesFromIsolate(i_isolate);
     293             :   MaybeHandle<WasmModuleObject> compiled_module;
     294             :   {
     295             :     // Explicitly enable Liftoff, disable tiering and set the tier_mask. This
     296             :     // way, we deterministically test a combination of Liftoff and Turbofan.
     297             :     FlagScope<bool> liftoff(&FLAG_liftoff, true);
     298             :     FlagScope<bool> no_tier_up(&FLAG_wasm_tier_up, false);
     299           2 :     FlagScope<int> tier_mask_scope(&FLAG_wasm_tier_mask_for_testing, tier_mask);
     300             :     compiled_module = i_isolate->wasm_engine()->SyncCompile(
     301           2 :         i_isolate, enabled_features, &interpreter_thrower, wire_bytes);
     302             :   }
     303           2 :   bool compiles = !compiled_module.is_null();
     304             : 
     305           2 :   if (FLAG_wasm_fuzzer_gen_test) {
     306           0 :     GenerateTestCase(i_isolate, wire_bytes, compiles);
     307             :   }
     308             : 
     309             :   bool validates = i_isolate->wasm_engine()->SyncValidate(
     310           2 :       i_isolate, enabled_features, wire_bytes);
     311             : 
     312           2 :   CHECK_EQ(compiles, validates);
     313           2 :   CHECK_IMPLIES(require_valid, validates);
     314             : 
     315           3 :   if (!compiles) return;
     316             : 
     317             :   MaybeHandle<WasmInstanceObject> interpreter_instance =
     318             :       i_isolate->wasm_engine()->SyncInstantiate(
     319             :           i_isolate, &interpreter_thrower, compiled_module.ToHandleChecked(),
     320           2 :           MaybeHandle<JSReceiver>(), MaybeHandle<JSArrayBuffer>());
     321             : 
     322             :   // Ignore instantiation failure.
     323           1 :   if (interpreter_thrower.error()) return;
     324             : 
     325             :   testing::WasmInterpretationResult interpreter_result =
     326             :       testing::InterpretWasmModule(i_isolate,
     327             :                                    interpreter_instance.ToHandleChecked(), 0,
     328           1 :                                    interpreter_args.get());
     329             : 
     330             :   // Do not execute the generated code if the interpreter did not finished after
     331             :   // a bounded number of steps.
     332           1 :   if (interpreter_result.stopped()) return;
     333             : 
     334             :   // The WebAssembly spec allows the sign bit of NaN to be non-deterministic.
     335             :   // This sign bit can make the difference between an infinite loop and
     336             :   // terminating code. With possible non-determinism we cannot guarantee that
     337             :   // the generated code will not go into an infinite loop and cause a timeout in
     338             :   // Clusterfuzz. Therefore we do not execute the generated code if the result
     339             :   // may be non-deterministic.
     340           1 :   if (interpreter_result.possible_nondeterminism()) return;
     341             : 
     342             :   int32_t result_compiled;
     343             :   {
     344           1 :     ErrorThrower compiler_thrower(i_isolate, "Compile");
     345             :     MaybeHandle<WasmInstanceObject> compiled_instance =
     346             :         i_isolate->wasm_engine()->SyncInstantiate(
     347             :             i_isolate, &compiler_thrower, compiled_module.ToHandleChecked(),
     348           2 :             MaybeHandle<JSReceiver>(), MaybeHandle<JSArrayBuffer>());
     349             : 
     350             :     DCHECK(!compiler_thrower.error());
     351           1 :     result_compiled = testing::CallWasmFunctionForTesting(
     352             :         i_isolate, compiled_instance.ToHandleChecked(), &compiler_thrower,
     353           1 :         "main", num_args, compiler_args.get());
     354             :   }
     355             : 
     356           1 :   if (interpreter_result.trapped() != i_isolate->has_pending_exception()) {
     357           0 :     const char* exception_text[] = {"no exception", "exception"};
     358           0 :     FATAL("interpreter: %s; compiled: %s",
     359             :           exception_text[interpreter_result.trapped()],
     360           0 :           exception_text[i_isolate->has_pending_exception()]);
     361             :   }
     362             : 
     363           1 :   if (!interpreter_result.trapped()) {
     364           1 :     CHECK_EQ(interpreter_result.result(), result_compiled);
     365             :   }
     366             : 
     367             :   // Cleanup any pending exception.
     368             :   i_isolate->clear_pending_exception();
     369             : }
     370             : 
     371             : }  // namespace fuzzer
     372             : }  // namespace wasm
     373             : }  // namespace internal
     374          12 : }  // namespace v8

Generated by: LCOV version 1.10