LCOV - code coverage report
Current view: top level - test/fuzzer - wasm-fuzzer-common.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 51 152 33.6 %
Date: 2019-02-19 Functions: 4 7 57.1 %

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

Generated by: LCOV version 1.10