// 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 <stddef.h> #include <stdint.h> #include <stdlib.h> #include <algorithm> #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; #if __clang__ // TODO(mostynb@opera.com): remove the using statements and these pragmas. #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wheader-hygiene" #endif using namespace v8::internal; using namespace v8::internal::wasm; using namespace v8::internal::wasm::fuzzer; #if __clang__ // TODO(mostynb@opera.com): remove the using statements and these pragmas. #pragma clang diagnostic pop #endif 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<DataRange, DataRange> split(uint32_t index) const { return std::make_pair(DataRange(data_, index), DataRange(data_ + index, size() - index)); } std::pair<DataRange, DataRange> split() { uint16_t index = get<uint16_t>(); if (size() > 0) { index = index % size(); } else { index = 0; } return split(index); } template <typename T> 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 <WasmOpcode Op, ValueType... Args> std::function<void(DataRange)> op() { return [this](DataRange data) { Generate<Args...>(data); builder_->Emit(Op); }; } template <ValueType T> std::function<void(DataRange)> block() { return [this](DataRange data) { blocks_.push_back(T); builder_->EmitWithU8( kExprBlock, static_cast<uint8_t>(WasmOpcodes::ValueTypeCodeFor(T))); Generate<T>(data); builder_->Emit(kExprEnd); blocks_.pop_back(); }; } template <ValueType T> std::function<void(DataRange)> block_br() { return [this](DataRange data) { blocks_.push_back(T); builder_->EmitWithU8( kExprBlock, static_cast<uint8_t>(WasmOpcodes::ValueTypeCodeFor(T))); const uint32_t target_block = data.get<uint32_t>() % 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 <ValueType T> void Generate(DataRange data); template <ValueType T1, ValueType T2, ValueType... Ts> void Generate(DataRange data) { const auto parts = data.split(); Generate<T1>(parts.first); Generate<T2, Ts...>(parts.second); } private: v8::internal::wasm::WasmFunctionBuilder* builder_; std::vector<ValueType> blocks_; }; template <> void WasmGenerator::Generate<kWasmI32>(DataRange data) { if (data.size() <= sizeof(uint32_t)) { builder_->EmitI32Const(data.get<uint32_t>()); } else { const std::function<void(DataRange)> alternates[] = { op<kExprI32Eqz, kWasmI32>(), // op<kExprI32Eq, kWasmI32, kWasmI32>(), op<kExprI32Ne, kWasmI32, kWasmI32>(), op<kExprI32LtS, kWasmI32, kWasmI32>(), op<kExprI32LtU, kWasmI32, kWasmI32>(), op<kExprI32GeS, kWasmI32, kWasmI32>(), op<kExprI32GeU, kWasmI32, kWasmI32>(), op<kExprI64Eqz, kWasmI64>(), // op<kExprI64Eq, kWasmI64, kWasmI64>(), op<kExprI64Ne, kWasmI64, kWasmI64>(), op<kExprI64LtS, kWasmI64, kWasmI64>(), op<kExprI64LtU, kWasmI64, kWasmI64>(), op<kExprI64GeS, kWasmI64, kWasmI64>(), op<kExprI64GeU, kWasmI64, kWasmI64>(), op<kExprF32Eq, kWasmF32, kWasmF32>(), op<kExprF32Ne, kWasmF32, kWasmF32>(), op<kExprF32Lt, kWasmF32, kWasmF32>(), op<kExprF32Ge, kWasmF32, kWasmF32>(), op<kExprF64Eq, kWasmF64, kWasmF64>(), op<kExprF64Ne, kWasmF64, kWasmF64>(), op<kExprF64Lt, kWasmF64, kWasmF64>(), op<kExprF64Ge, kWasmF64, kWasmF64>(), op<kExprI32Add, kWasmI32, kWasmI32>(), op<kExprI32Sub, kWasmI32, kWasmI32>(), op<kExprI32Mul, kWasmI32, kWasmI32>(), op<kExprI32DivS, kWasmI32, kWasmI32>(), op<kExprI32DivU, kWasmI32, kWasmI32>(), op<kExprI32RemS, kWasmI32, kWasmI32>(), op<kExprI32RemU, kWasmI32, kWasmI32>(), op<kExprI32And, kWasmI32, kWasmI32>(), op<kExprI32Ior, kWasmI32, kWasmI32>(), op<kExprI32Xor, kWasmI32, kWasmI32>(), op<kExprI32Shl, kWasmI32, kWasmI32>(), op<kExprI32ShrU, kWasmI32, kWasmI32>(), op<kExprI32ShrS, kWasmI32, kWasmI32>(), op<kExprI32Ror, kWasmI32, kWasmI32>(), op<kExprI32Rol, kWasmI32, kWasmI32>(), op<kExprI32Clz, kWasmI32>(), // op<kExprI32Ctz, kWasmI32>(), // op<kExprI32Popcnt, kWasmI32>(), op<kExprI32ConvertI64, kWasmI64>(), // op<kExprI32SConvertF32, kWasmF32>(), op<kExprI32UConvertF32, kWasmF32>(), op<kExprI32SConvertF64, kWasmF64>(), op<kExprI32UConvertF64, kWasmF64>(), op<kExprI32ReinterpretF32, kWasmF32>(), block<kWasmI32>(), block_br<kWasmI32>()}; static_assert(arraysize(alternates) < std::numeric_limits<uint8_t>::max(), "Too many alternates. Replace with a bigger type if needed."); const auto which = data.get<uint8_t>(); alternates[which % arraysize(alternates)](data); } } template <> void WasmGenerator::Generate<kWasmI64>(DataRange data) { if (data.size() <= sizeof(uint64_t)) { builder_->EmitI64Const(data.get<int64_t>()); } else { const std::function<void(DataRange)> alternates[] = { op<kExprI64Add, kWasmI64, kWasmI64>(), op<kExprI64Sub, kWasmI64, kWasmI64>(), op<kExprI64Mul, kWasmI64, kWasmI64>(), op<kExprI64DivS, kWasmI64, kWasmI64>(), op<kExprI64DivU, kWasmI64, kWasmI64>(), op<kExprI64RemS, kWasmI64, kWasmI64>(), op<kExprI64RemU, kWasmI64, kWasmI64>(), op<kExprI64And, kWasmI64, kWasmI64>(), op<kExprI64Ior, kWasmI64, kWasmI64>(), op<kExprI64Xor, kWasmI64, kWasmI64>(), op<kExprI64Shl, kWasmI64, kWasmI64>(), op<kExprI64ShrU, kWasmI64, kWasmI64>(), op<kExprI64ShrS, kWasmI64, kWasmI64>(), op<kExprI64Ror, kWasmI64, kWasmI64>(), op<kExprI64Rol, kWasmI64, kWasmI64>(), op<kExprI64Clz, kWasmI64>(), op<kExprI64Ctz, kWasmI64>(), op<kExprI64Popcnt, kWasmI64>(), block<kWasmI64>(), block_br<kWasmI64>()}; static_assert(arraysize(alternates) < std::numeric_limits<uint8_t>::max(), "Too many alternates. Replace with a bigger type if needed."); const auto which = data.get<uint8_t>(); alternates[which % arraysize(alternates)](data); } } template <> void WasmGenerator::Generate<kWasmF32>(DataRange data) { if (data.size() <= sizeof(float)) { builder_->EmitF32Const(data.get<float>()); } else { const std::function<void(DataRange)> alternates[] = { op<kExprF32Add, kWasmF32, kWasmF32>(), op<kExprF32Sub, kWasmF32, kWasmF32>(), op<kExprF32Mul, kWasmF32, kWasmF32>(), block<kWasmF32>(), block_br<kWasmF32>()}; static_assert(arraysize(alternates) < std::numeric_limits<uint8_t>::max(), "Too many alternates. Replace with a bigger type if needed."); const auto which = data.get<uint8_t>(); alternates[which % arraysize(alternates)](data); } } template <> void WasmGenerator::Generate<kWasmF64>(DataRange data) { if (data.size() <= sizeof(double)) { builder_->EmitF64Const(data.get<double>()); } else { const std::function<void(DataRange)> alternates[] = { op<kExprF64Add, kWasmF64, kWasmF64>(), op<kExprF64Sub, kWasmF64, kWasmF64>(), op<kExprF64Mul, kWasmF64, kWasmF64>(), block<kWasmF64>(), block_br<kWasmF64>()}; static_assert(arraysize(alternates) < std::numeric_limits<uint8_t>::max(), "Too many alternates. Replace with a bigger type if needed."); const auto which = data.get<uint8_t>(); alternates[which % arraysize(alternates)](data); } } void WasmGenerator::Generate(ValueType type, DataRange data) { switch (type) { case kWasmI32: return Generate<kWasmI32>(data); case kWasmI64: return Generate<kWasmI64>(data); case kWasmF32: return Generate<kWasmF32>(data); case kWasmF64: return Generate<kWasmF64>(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<WasmValue[]>& interpreter_args, std::unique_ptr<Handle<Object>[]>& compiler_args) override { TestSignatures sigs; WasmModuleBuilder builder(zone); v8::internal::wasm::WasmFunctionBuilder* f = builder.AddFunction(sigs.i_iii()); WasmGenerator gen(f); gen.Generate<kWasmI32>(DataRange(data, static_cast<uint32_t>(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<Object>[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); }