// Copyright 2017 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include #include #include #include #include "include/v8.h" #include "src/isolate.h" #include "src/objects-inl.h" #include "src/objects.h" #include "src/ostreams.h" #include "src/wasm/wasm-interpreter.h" #include "src/wasm/wasm-module-builder.h" #include "src/wasm/wasm-module.h" #include "test/common/wasm/test-signatures.h" #include "test/common/wasm/wasm-module-runner.h" #include "test/fuzzer/fuzzer-support.h" #include "test/fuzzer/wasm-fuzzer-common.h" typedef uint8_t byte; using namespace v8::internal; using namespace v8::internal::wasm; using namespace v8::internal::wasm::fuzzer; namespace { class DataRange { const uint8_t* data_; size_t size_; public: DataRange(const uint8_t* data, size_t size) : data_(data), size_(size) {} size_t size() const { return size_; } std::pair split(uint32_t index) const { return std::make_pair(DataRange(data_, index), DataRange(data_ + index, size() - index)); } std::pair split() { uint16_t index = get(); if (size() > 0) { index = index % size(); } else { index = 0; } return split(index); } template T get() { if (size() == 0) { return T(); } else { // We want to support the case where we have less than sizeof(T) bytes // remaining in the slice. For example, if we emit an i32 constant, it's // okay if we don't have a full four bytes available, we'll just use what // we have. We aren't concerned about endianness because we are generating // arbitrary expressions. const size_t num_bytes = std::min(sizeof(T), size()); T result = T(); memcpy(&result, data_, num_bytes); data_ += num_bytes; size_ -= num_bytes; return result; } } }; class WasmGenerator { template std::function op() { return [this](DataRange data) { Generate(data); builder_->Emit(Op); }; } template std::function block() { return [this](DataRange data) { blocks_.push_back(T); builder_->EmitWithU8( kExprBlock, static_cast(WasmOpcodes::ValueTypeCodeFor(T))); Generate(data); builder_->Emit(kExprEnd); blocks_.pop_back(); }; } template std::function block_br() { return [this](DataRange data) { blocks_.push_back(T); builder_->EmitWithU8( kExprBlock, static_cast(WasmOpcodes::ValueTypeCodeFor(T))); const uint32_t target_block = data.get() % blocks_.size(); const ValueType break_type = blocks_[target_block]; Generate(break_type, data); builder_->EmitWithI32V(kExprBr, target_block); builder_->Emit(kExprEnd); blocks_.pop_back(); }; } public: WasmGenerator(v8::internal::wasm::WasmFunctionBuilder* fn) : builder_(fn) {} void Generate(ValueType type, DataRange data); template void Generate(DataRange data); template void Generate(DataRange data) { const auto parts = data.split(); Generate(parts.first); Generate(parts.second); } private: v8::internal::wasm::WasmFunctionBuilder* builder_; std::vector blocks_; }; template <> void WasmGenerator::Generate(DataRange data) { if (data.size() <= sizeof(uint32_t)) { builder_->EmitI32Const(data.get()); } else { const std::function alternates[] = { op(), // op(), op(), op(), op(), op(), op(), op(), // op(), op(), op(), op(), op(), op(), op(), op(), op(), op(), op(), op(), op(), op(), op(), op(), op(), op(), op(), op(), op(), op(), op(), op(), op(), op(), op(), op(), op(), op(), // op(), // op(), op(), // op(), op(), op(), op(), op(), block(), block_br()}; static_assert(arraysize(alternates) < std::numeric_limits::max(), "Too many alternates. Replace with a bigger type if needed."); const auto which = data.get(); alternates[which % arraysize(alternates)](data); } } template <> void WasmGenerator::Generate(DataRange data) { if (data.size() <= sizeof(uint64_t)) { builder_->EmitI64Const(data.get()); } else { const std::function alternates[] = { op(), op(), op(), op(), op(), op(), op(), op(), op(), op(), op(), op(), op(), op(), op(), op(), op(), op(), block(), block_br()}; static_assert(arraysize(alternates) < std::numeric_limits::max(), "Too many alternates. Replace with a bigger type if needed."); const auto which = data.get(); alternates[which % arraysize(alternates)](data); } } template <> void WasmGenerator::Generate(DataRange data) { if (data.size() <= sizeof(float)) { builder_->EmitF32Const(data.get()); } else { const std::function alternates[] = { op(), op(), op(), block(), block_br()}; static_assert(arraysize(alternates) < std::numeric_limits::max(), "Too many alternates. Replace with a bigger type if needed."); const auto which = data.get(); alternates[which % arraysize(alternates)](data); } } template <> void WasmGenerator::Generate(DataRange data) { if (data.size() <= sizeof(double)) { builder_->EmitF64Const(data.get()); } else { const std::function alternates[] = { op(), op(), op(), block(), block_br()}; static_assert(arraysize(alternates) < std::numeric_limits::max(), "Too many alternates. Replace with a bigger type if needed."); const auto which = data.get(); alternates[which % arraysize(alternates)](data); } } void WasmGenerator::Generate(ValueType type, DataRange data) { switch (type) { case kWasmI32: return Generate(data); case kWasmI64: return Generate(data); case kWasmF32: return Generate(data); case kWasmF64: return Generate(data); default: UNREACHABLE(); } } } class WasmCompileFuzzer : public WasmExecutionFuzzer { virtual bool GenerateModule( Isolate* isolate, Zone* zone, const uint8_t* data, size_t size, ZoneBuffer& buffer, int32_t& num_args, std::unique_ptr& interpreter_args, std::unique_ptr[]>& compiler_args) override { TestSignatures sigs; WasmModuleBuilder builder(zone); v8::internal::wasm::WasmFunctionBuilder* f = builder.AddFunction(sigs.i_iii()); WasmGenerator gen(f); gen.Generate(DataRange(data, static_cast(size))); uint8_t end_opcode = kExprEnd; f->EmitCode(&end_opcode, 1); builder.AddExport(v8::internal::CStrVector("main"), f); builder.WriteTo(buffer); num_args = 3; interpreter_args.reset( new WasmValue[3]{WasmValue(1), WasmValue(2), WasmValue(3)}); compiler_args.reset(new Handle[3]{ handle(Smi::FromInt(1), isolate), handle(Smi::FromInt(1), isolate), handle(Smi::FromInt(1), isolate)}); return true; } }; extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { return WasmCompileFuzzer().FuzzWasmModule(data, size); }