[wasm] Support EH in the compile fuzzer

R=ahaas@chromium.org

Bug: v8:8091
Change-Id: Ie3450c2a55d2fd272efc6c69632cf52a9aede597
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2699259
Commit-Queue: Thibaud Michaud <thibaudm@chromium.org>
Reviewed-by: Andreas Haas <ahaas@chromium.org>
Cr-Commit-Position: refs/heads/master@{#72817}
This commit is contained in:
Thibaud Michaud 2021-02-17 15:24:38 +01:00 committed by Commit Bot
parent e7195170d5
commit 19fa611494
3 changed files with 133 additions and 11 deletions

View File

@ -248,6 +248,7 @@ WasmModuleBuilder::WasmModuleBuilder(Zone* zone)
data_segments_(zone),
indirect_functions_(zone),
globals_(zone),
exceptions_(zone),
signature_map_(zone),
start_function_index_(-1),
min_memory_size_(16),
@ -280,6 +281,14 @@ uint32_t WasmModuleBuilder::AddSignature(FunctionSig* sig) {
return index;
}
uint32_t WasmModuleBuilder::AddException(FunctionSig* type) {
DCHECK_EQ(0, type->return_count());
int type_index = AddSignature(type);
uint32_t except_index = static_cast<uint32_t>(exceptions_.size());
exceptions_.push_back(type_index);
return except_index;
}
uint32_t WasmModuleBuilder::AddStructType(StructType* type) {
uint32_t index = static_cast<uint32_t>(types_.size());
types_.push_back(Type(type));
@ -623,6 +632,17 @@ void WasmModuleBuilder::WriteTo(ZoneBuffer* buffer) const {
FixupSection(buffer, start);
}
// Emit event section.
if (exceptions_.size() > 0) {
size_t start = EmitSection(kExceptionSectionCode, buffer);
buffer->write_size(exceptions_.size());
for (int type : exceptions_) {
buffer->write_u32v(kExceptionAttribute);
buffer->write_u32v(type);
}
FixupSection(buffer, start);
}
// == Emit globals ===========================================================
if (globals_.size() > 0) {
size_t start = EmitSection(kGlobalSectionCode, buffer);

View File

@ -250,6 +250,7 @@ class V8_EXPORT_PRIVATE WasmModuleBuilder : public ZoneObject {
bool mutability, Vector<const char> module = {});
void AddDataSegment(const byte* data, uint32_t size, uint32_t dest);
uint32_t AddSignature(FunctionSig* sig);
uint32_t AddException(FunctionSig* type);
uint32_t AddStructType(StructType* type);
uint32_t AddArrayType(ArrayType* type);
// In the current implementation, it's supported to have uninitialized slots
@ -284,6 +285,12 @@ class V8_EXPORT_PRIVATE WasmModuleBuilder : public ZoneObject {
return types_[index].sig;
}
int NumExceptions() { return static_cast<int>(exceptions_.size()); }
FunctionSig* GetExceptionType(int index) {
return types_[exceptions_[index]].sig;
}
private:
struct Type {
enum Kind { kFunctionSig, kStructType, kArrayType };
@ -351,6 +358,7 @@ class V8_EXPORT_PRIVATE WasmModuleBuilder : public ZoneObject {
ZoneVector<WasmDataSegment> data_segments_;
ZoneVector<uint32_t> indirect_functions_;
ZoneVector<WasmGlobal> globals_;
ZoneVector<int> exceptions_;
ZoneUnorderedMap<FunctionSig, uint32_t> signature_map_;
int start_function_index_;
uint32_t max_table_size_ = 0;

View File

@ -33,6 +33,7 @@ constexpr int kMaxFunctions = 4;
constexpr int kMaxGlobals = 64;
constexpr int kMaxParameters = 15;
constexpr int kMaxReturns = 15;
constexpr int kMaxExceptions = 4;
class DataRange {
Vector<const uint8_t> data_;
@ -116,8 +117,8 @@ class WasmGenerator {
BlockScope(WasmGenerator* gen, WasmOpcode block_type,
Vector<const ValueType> param_types,
Vector<const ValueType> result_types,
Vector<const ValueType> br_types)
: gen_(gen) {
Vector<const ValueType> br_types, bool emit_end = true)
: gen_(gen), emit_end_(emit_end) {
gen->blocks_.emplace_back(br_types.begin(), br_types.end());
if (param_types.size() == 0 && result_types.size() == 0) {
gen->builder_->EmitWithU8(block_type, kWasmStmt.value_type_code());
@ -146,12 +147,13 @@ class WasmGenerator {
}
~BlockScope() {
gen_->builder_->Emit(kExprEnd);
if (emit_end_) gen_->builder_->Emit(kExprEnd);
gen_->blocks_.pop_back();
}
private:
WasmGenerator* const gen_;
bool emit_end_;
};
void block(Vector<const ValueType> param_types,
@ -204,6 +206,60 @@ class WasmGenerator {
type, data);
}
void try_block_helper(ValueType return_type, DataRange* data) {
bool has_catch_all = data->get<uint8_t>() % 2;
uint8_t num_catch =
data->get<uint8_t>() % (builder_->builder()->NumExceptions() + 1);
bool is_delegate =
num_catch == 0 && !has_catch_all && data->get<uint8_t>() % 2 == 0;
// Allow one more target than there are enclosing try blocks, for delegating
// to the caller.
uint8_t delegate_target = data->get<uint8_t>() % (try_blocks_.size() + 1);
bool is_unwind = num_catch == 0 && !has_catch_all && !is_delegate;
Vector<const ValueType> return_type_vec =
return_type.kind() == ValueType::kStmt ? Vector<ValueType>{}
: VectorOf({return_type});
BlockScope block_scope(this, kExprTry, {}, return_type_vec, return_type_vec,
!is_delegate);
int control_depth = static_cast<int>(blocks_.size()) - 1;
try_blocks_.push_back(control_depth);
Generate(return_type, data);
try_blocks_.pop_back();
catch_blocks_.push_back(control_depth);
for (int i = 0; i < num_catch; ++i) {
const FunctionSig* exception_type =
builder_->builder()->GetExceptionType(i);
auto exception_type_vec = VectorOf(exception_type->parameters().begin(),
exception_type->parameter_count());
builder_->EmitWithU32V(kExprCatch, i);
ConsumeAndGenerate(exception_type_vec, return_type_vec, data);
}
if (has_catch_all) {
builder_->Emit(kExprCatchAll);
Generate(return_type, data);
}
if (is_delegate) {
DCHECK_GT(blocks_.size(), try_blocks_.size());
// If {delegate_target == try_blocks_.size()}, delegate to the caller.
int delegate_depth = delegate_target == try_blocks_.size()
? static_cast<int>(blocks_.size()) - 2
: static_cast<int>(blocks_.size() - 2 -
try_blocks_[delegate_target]);
builder_->EmitWithU32V(kExprDelegate, delegate_depth);
}
catch_blocks_.pop_back();
if (is_unwind) {
builder_->Emit(kExprUnwind);
Generate(return_type, data);
}
}
template <ValueType::Kind T>
void try_block(DataRange* data) {
try_block_helper(ValueType::Primitive(T), data);
}
void any_block(Vector<const ValueType> param_types,
Vector<const ValueType> return_types, DataRange* data) {
uint8_t block_type = data->get<uint8_t>() % 4;
@ -652,6 +708,25 @@ class WasmGenerator {
void set_global(DataRange* data) { global_op<ValueType::kStmt>(data); }
void throw_or_rethrow(DataRange* data) {
bool rethrow = data->get<uint8_t>() % 2;
if (rethrow && !catch_blocks_.empty()) {
int control_depth = static_cast<int>(blocks_.size() - 1);
int catch_index =
data->get<uint8_t>() % static_cast<int>(catch_blocks_.size());
builder_->EmitWithU32V(kExprRethrow,
control_depth - catch_blocks_[catch_index]);
} else {
int tag = data->get<uint8_t>() % builder_->builder()->NumExceptions();
FunctionSig* exception_sig = builder_->builder()->GetExceptionType(tag);
Vector<const ValueType> exception_types(
exception_sig->parameters().begin(),
exception_sig->parameter_count());
Generate(exception_types, data);
builder_->EmitWithU32V(kExprThrow, tag);
}
}
template <ValueType::Kind... Types>
void sequence(DataRange* data) {
Generate<Types...>(data);
@ -736,6 +811,8 @@ class WasmGenerator {
std::vector<ValueType> globals_;
std::vector<uint8_t> mutable_globals_; // indexes into {globals_}.
uint32_t recursion_depth = 0;
std::vector<int> try_blocks_;
std::vector<int> catch_blocks_;
static constexpr uint32_t kMaxRecursionDepth = 64;
@ -806,7 +883,9 @@ void WasmGenerator::Generate<ValueType::kStmt>(DataRange* data) {
&WasmGenerator::call_indirect<ValueType::kStmt>,
&WasmGenerator::set_local,
&WasmGenerator::set_global};
&WasmGenerator::set_global,
&WasmGenerator::throw_or_rethrow,
&WasmGenerator::try_block<ValueType::kStmt>};
GenerateOneOf(alternatives, data);
}
@ -977,7 +1056,8 @@ void WasmGenerator::Generate<ValueType::kI32>(DataRange* data) {
&WasmGenerator::select_with_type<ValueType::kI32>,
&WasmGenerator::call<ValueType::kI32>,
&WasmGenerator::call_indirect<ValueType::kI32>};
&WasmGenerator::call_indirect<ValueType::kI32>,
&WasmGenerator::try_block<ValueType::kI32>};
GenerateOneOf(alternatives, data);
}
@ -1119,7 +1199,8 @@ void WasmGenerator::Generate<ValueType::kI64>(DataRange* data) {
&WasmGenerator::select_with_type<ValueType::kI64>,
&WasmGenerator::call<ValueType::kI64>,
&WasmGenerator::call_indirect<ValueType::kI64>};
&WasmGenerator::call_indirect<ValueType::kI64>,
&WasmGenerator::try_block<ValueType::kI64>};
GenerateOneOf(alternatives, data);
}
@ -1177,7 +1258,8 @@ void WasmGenerator::Generate<ValueType::kF32>(DataRange* data) {
&WasmGenerator::select_with_type<ValueType::kF32>,
&WasmGenerator::call<ValueType::kF32>,
&WasmGenerator::call_indirect<ValueType::kF32>};
&WasmGenerator::call_indirect<ValueType::kF32>,
&WasmGenerator::try_block<ValueType::kF32>};
GenerateOneOf(alternatives, data);
}
@ -1235,7 +1317,8 @@ void WasmGenerator::Generate<ValueType::kF64>(DataRange* data) {
&WasmGenerator::select_with_type<ValueType::kF64>,
&WasmGenerator::call<ValueType::kF64>,
&WasmGenerator::call_indirect<ValueType::kF64>};
&WasmGenerator::call_indirect<ValueType::kF64>,
&WasmGenerator::try_block<ValueType::kF64>};
GenerateOneOf(alternatives, data);
}
@ -1737,10 +1820,14 @@ void WasmGenerator::ConsumeAndGenerate(Vector<const ValueType> param_types,
}
}
FunctionSig* GenerateSig(Zone* zone, DataRange* data) {
enum SigKind { kFunctionSig, kExceptionSig };
FunctionSig* GenerateSig(Zone* zone, DataRange* data, SigKind sig_kind) {
// Generate enough parameters to spill some to the stack.
int num_params = int{data->get<uint8_t>()} % (kMaxParameters + 1);
int num_returns = int{data->get<uint8_t>()} % (kMaxReturns + 1);
int num_returns = sig_kind == kFunctionSig
? int{data->get<uint8_t>()} % (kMaxReturns + 1)
: 0;
FunctionSig::Builder builder(zone, num_returns, num_params);
for (int i = 0; i < num_returns; ++i) builder.AddReturn(GetValueType(data));
@ -1768,7 +1855,7 @@ class WasmCompileFuzzer : public WasmExecutionFuzzer {
int num_functions = 1 + (range.get<uint8_t>() % kMaxFunctions);
for (int i = 1; i < num_functions; ++i) {
FunctionSig* sig = GenerateSig(zone, &range);
FunctionSig* sig = GenerateSig(zone, &range, kFunctionSig);
uint32_t signature_index = builder.AddSignature(sig);
function_signatures.push_back(signature_index);
}
@ -1779,6 +1866,12 @@ class WasmCompileFuzzer : public WasmExecutionFuzzer {
globals.reserve(num_globals);
mutable_globals.reserve(num_globals);
int num_exceptions = 1 + (range.get<uint8_t>() % kMaxExceptions);
for (int i = 0; i < num_exceptions; ++i) {
FunctionSig* sig = GenerateSig(zone, &range, kExceptionSig);
builder.AddException(sig);
}
for (int i = 0; i < num_globals; ++i) {
ValueType type = GetValueType(&range);
// 1/8 of globals are immutable.
@ -1830,6 +1923,7 @@ class WasmCompileFuzzer : public WasmExecutionFuzzer {
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
constexpr bool require_valid = true;
EXPERIMENTAL_FLAG_SCOPE(reftypes);
EXPERIMENTAL_FLAG_SCOPE(eh);
WasmCompileFuzzer().FuzzWasmModule({data, size}, require_valid);
return 0;
}