Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions scripts/fuzz_opt.py
Original file line number Diff line number Diff line change
Expand Up @@ -2689,12 +2689,12 @@ def get_random_opts():
# We can't do this if a.wasm doesn't exist, which can be the
# case if we failed to even generate the wasm.
if not os.path.exists('a.wasm'):
print('''\
print(f'''\
================================================================================
You found a bug in the fuzzer itself! It failed to generate a valid wasm file
from the random input. Please report it with
seed: %(seed)d
seed: {seed}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

drive-by fix: this was not logging the number before.

and the exact version of Binaryen you found it on, plus the exact Python
version (hopefully deterministic random numbers will be identical).
Expand Down
17 changes: 17 additions & 0 deletions scripts/fuzz_shell.js
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,19 @@ function logValue(x, y) {
console.log('[LoggingExternalInterface logging ' + printed(x, y) + ']');
}

function logRef(ref) {
// Look for VM bugs by using the reference in an API (note: we cannot do
// +ref or ref+'' as those trap).
JSON.stringify(ref);
// If not null, try to read a property, which might exercise an
// interesting code path.
if (ref) {
ref.foobar;
}
// Finally, log normally as with all other loggers.
logValue(ref);
}

// Track the exports in a map (similar to the Exports object from wasm, i.e.,
// whose keys are strings and whose values are the corresponding exports).
var exports = {};
Expand Down Expand Up @@ -290,6 +303,10 @@ var imports = {
// we could avoid running JS on code with SIMD in it, but it is useful to
// fuzz such code as much as we can.)
'log-v128': logValue,
'log-anyref': logRef,
'log-funcref': logRef,
'log-contref': logRef,
'log-externref': logRef,

// Throw an exception from JS.
'throw': (which) => {
Expand Down
86 changes: 44 additions & 42 deletions src/tools/execution-results.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,47 @@ Tag& getJsTag() {
return tag;
}

void printValue(Literal value) {
// Unwrap an externalized GC value to get the actual value, but not strings,
// which are normally a subtype of ext.
if (Type::isSubType(value.type, Type(HeapType::ext, Nullable)) &&
!value.type.isString()) {
value = value.internalize();
}

// An anyref literal is a string.
if (value.type.isRef() &&
value.type.getHeapType().isMaybeShared(HeapType::any)) {
value = value.externalize();
}

// Don't print most reference values, as e.g. funcref(N) contains an index,
// which is not guaranteed to remain identical after optimizations. Do not
// print the type in detail (as even that may change due to closed-world
// optimizations); just print a simple type like JS does, 'object' or
// 'function', but also print null for a null (so a null function does not
// get printed as object, as in JS we have typeof null == 'object').
//
// The only references we print in full are strings and i31s, which have
// simple and stable internal structures that optimizations will not alter.
auto type = value.type;
if (type.isRef()) {
if (type.isString() || type.getHeapType().isMaybeShared(HeapType::i31)) {
std::cout << value;
} else if (value.isNull()) {
std::cout << "null";
} else if (type.isFunction()) {
std::cout << "function";
} else {
std::cout << "object";
}
return;
}

// Non-references can be printed in full.
std::cout << value;
}

} // namespace

// Logs every relevant import call parameter.
Expand Down Expand Up @@ -127,7 +168,8 @@ struct LoggingExternalInterface : public ShellExternalInterface {
std::cout << ' ' << high;
loggings.push_back(high);
} else {
std::cout << ' ' << argument;
std::cout << ' ';
printValue(argument);
loggings.push_back(argument);
}
}
Expand Down Expand Up @@ -416,53 +458,13 @@ struct ExecutionResults {
std::cout << "[fuzz-exec] note result: " << exp->name << " => ";
for (auto value : *values) {
printValue(value);
std::cout << '\n';
}
}
}
}
}

void printValue(Literal value) {
// Unwrap an externalized GC value to get the actual value, but not strings,
// which are normally a subtype of ext.
if (Type::isSubType(value.type, Type(HeapType::ext, Nullable)) &&
!value.type.isString()) {
value = value.internalize();
}

// An anyref literal is a string.
if (value.type.isRef() &&
value.type.getHeapType().isMaybeShared(HeapType::any)) {
value = value.externalize();
}

// Don't print most reference values, as e.g. funcref(N) contains an index,
// which is not guaranteed to remain identical after optimizations. Do not
// print the type in detail (as even that may change due to closed-world
// optimizations); just print a simple type like JS does, 'object' or
// 'function', but also print null for a null (so a null function does not
// get printed as object, as in JS we have typeof null == 'object').
//
// The only references we print in full are strings and i31s, which have
// simple and stable internal structures that optimizations will not alter.
auto type = value.type;
if (type.isRef()) {
if (type.isString() || type.getHeapType().isMaybeShared(HeapType::i31)) {
std::cout << value << '\n';
} else if (value.isNull()) {
std::cout << "null\n";
} else if (type.isFunction()) {
std::cout << "function\n";
} else {
std::cout << "object\n";
}
return;
}

// Non-references can be printed in full.
std::cout << value << '\n';
}

// get current results and check them against previous ones
void check(Module& wasm) {
ExecutionResults optimizedResults;
Expand Down
15 changes: 11 additions & 4 deletions src/tools/fuzzing/fuzzing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,21 @@ TranslateToFuzzReader::TranslateToFuzzReader(Module& wasm,

haveInitialFunctions = !wasm.functions.empty();

// - funcref cannot be logged because referenced functions can be inlined or
// removed during optimization
// - there's no point in logging anyref because it is opaque
// - don't bother logging tuples
loggableTypes = {Type::i32, Type::i64, Type::f32, Type::f64};
if (wasm.features.hasSIMD()) {
loggableTypes.push_back(Type::v128);
}
if (wasm.features.hasReferenceTypes()) {
if (wasm.features.hasGC()) {
loggableTypes.push_back(Type(HeapType::any, Nullable));
loggableTypes.push_back(Type(HeapType::func, Nullable));
loggableTypes.push_back(Type(HeapType::ext, Nullable));
}
if (wasm.features.hasStackSwitching()) {
loggableTypes.push_back(Type(HeapType::cont, Nullable));
}
// Note: exnref traps on the JS boundary, so we cannot try to log it.
}

// Setup params. Start with the defaults.
globalParams = std::make_unique<FuzzParamsContext>(*this);
Expand Down
95 changes: 95 additions & 0 deletions test/lit/exec/fuzzing-api.wast
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
(module
(import "fuzzing-support" "log-i32" (func $log-i32 (param i32)))
(import "fuzzing-support" "log-f64" (func $log-f64 (param f64)))
(import "fuzzing-support" "log-anyref" (func $log-anyref (param anyref)))
(import "fuzzing-support" "log-funcref" (func $log-funcref (param funcref)))
(import "fuzzing-support" "log-contref" (func $log-contref (param contref)))
(import "fuzzing-support" "log-externref" (func $log-externref (param externref)))

(import "fuzzing-support" "throw" (func $throw (param i32)))

Expand All @@ -24,6 +28,8 @@
(import "fuzzing-support" "wasmtag" (tag $imported-wasm-tag (param i32)))
(import "fuzzing-support" "jstag" (tag $imported-js-tag (param externref)))

(type $i32 (struct i32))

(table $table 10 20 funcref)

;; Note that the exported table appears first here, but in the binary and in
Expand All @@ -33,13 +39,37 @@
;; CHECK: [fuzz-exec] calling logging
;; CHECK-NEXT: [LoggingExternalInterface logging 42]
;; CHECK-NEXT: [LoggingExternalInterface logging 3.14159]
;; CHECK-NEXT: [LoggingExternalInterface logging null]
;; CHECK-NEXT: [LoggingExternalInterface logging object]
;; CHECK-NEXT: [LoggingExternalInterface logging function]
;; CHECK-NEXT: [LoggingExternalInterface logging null]
;; CHECK-NEXT: [LoggingExternalInterface logging null]
(func $logging (export "logging")
(call $log-i32
(i32.const 42)
)
(call $log-f64
(f64.const 3.14159)
)
(call $log-anyref
(ref.null any)
)
;; struct values and func names are not logged, due to differences between
;; VMs and changes due to optimizations, that make comparisons hard.
(call $log-anyref
(struct.new $i32
(i32.const 42)
)
)
(call $log-funcref
(ref.func $logging)
)
(call $log-contref
(ref.null cont)
)
(call $log-externref
(ref.null extern)
)
)

;; CHECK: [fuzz-exec] calling throwing
Expand Down Expand Up @@ -105,6 +135,11 @@
;; CHECK: [fuzz-exec] calling export.calling
;; CHECK-NEXT: [LoggingExternalInterface logging 42]
;; CHECK-NEXT: [LoggingExternalInterface logging 3.14159]
;; CHECK-NEXT: [LoggingExternalInterface logging null]
;; CHECK-NEXT: [LoggingExternalInterface logging object]
;; CHECK-NEXT: [LoggingExternalInterface logging function]
;; CHECK-NEXT: [LoggingExternalInterface logging null]
;; CHECK-NEXT: [LoggingExternalInterface logging null]
;; CHECK-NEXT: [exception thrown: imported-js-tag externref]
(func $export.calling (export "export.calling")
;; At index 0 in the exports we have $logging, so we will do those loggings.
Expand All @@ -123,6 +158,11 @@
;; CHECK: [fuzz-exec] calling export.calling.rethrow
;; CHECK-NEXT: [LoggingExternalInterface logging 42]
;; CHECK-NEXT: [LoggingExternalInterface logging 3.14159]
;; CHECK-NEXT: [LoggingExternalInterface logging null]
;; CHECK-NEXT: [LoggingExternalInterface logging object]
;; CHECK-NEXT: [LoggingExternalInterface logging function]
;; CHECK-NEXT: [LoggingExternalInterface logging null]
;; CHECK-NEXT: [LoggingExternalInterface logging null]
;; CHECK-NEXT: [exception thrown: imported-js-tag externref]
(func $export.calling.rethrow (export "export.calling.rethrow")
;; As above, but the second param is different.
Expand All @@ -142,6 +182,11 @@
;; CHECK: [fuzz-exec] calling export.calling.catching
;; CHECK-NEXT: [LoggingExternalInterface logging 42]
;; CHECK-NEXT: [LoggingExternalInterface logging 3.14159]
;; CHECK-NEXT: [LoggingExternalInterface logging null]
;; CHECK-NEXT: [LoggingExternalInterface logging object]
;; CHECK-NEXT: [LoggingExternalInterface logging function]
;; CHECK-NEXT: [LoggingExternalInterface logging null]
;; CHECK-NEXT: [LoggingExternalInterface logging null]
;; CHECK-NEXT: [LoggingExternalInterface logging 0]
;; CHECK-NEXT: [LoggingExternalInterface logging 1]
(func $export.calling.catching (export "export.calling.catching")
Expand All @@ -163,6 +208,11 @@
;; CHECK: [fuzz-exec] calling ref.calling
;; CHECK-NEXT: [LoggingExternalInterface logging 42]
;; CHECK-NEXT: [LoggingExternalInterface logging 3.14159]
;; CHECK-NEXT: [LoggingExternalInterface logging null]
;; CHECK-NEXT: [LoggingExternalInterface logging object]
;; CHECK-NEXT: [LoggingExternalInterface logging function]
;; CHECK-NEXT: [LoggingExternalInterface logging null]
;; CHECK-NEXT: [LoggingExternalInterface logging null]
;; CHECK-NEXT: [exception thrown: imported-js-tag externref]
(func $ref.calling (export "ref.calling")
;; This will emit some logging.
Expand All @@ -181,6 +231,11 @@
;; CHECK: [fuzz-exec] calling ref.calling.rethrow
;; CHECK-NEXT: [LoggingExternalInterface logging 42]
;; CHECK-NEXT: [LoggingExternalInterface logging 3.14159]
;; CHECK-NEXT: [LoggingExternalInterface logging null]
;; CHECK-NEXT: [LoggingExternalInterface logging object]
;; CHECK-NEXT: [LoggingExternalInterface logging function]
;; CHECK-NEXT: [LoggingExternalInterface logging null]
;; CHECK-NEXT: [LoggingExternalInterface logging null]
;; CHECK-NEXT: [exception thrown: imported-js-tag externref]
(func $ref.calling.rethrow (export "ref.calling.rethrow")
;; As with calling an export, when we set the flags to 1 exceptions are
Expand All @@ -199,6 +254,11 @@
;; CHECK: [fuzz-exec] calling ref.calling.catching
;; CHECK-NEXT: [LoggingExternalInterface logging 42]
;; CHECK-NEXT: [LoggingExternalInterface logging 3.14159]
;; CHECK-NEXT: [LoggingExternalInterface logging null]
;; CHECK-NEXT: [LoggingExternalInterface logging object]
;; CHECK-NEXT: [LoggingExternalInterface logging function]
;; CHECK-NEXT: [LoggingExternalInterface logging null]
;; CHECK-NEXT: [LoggingExternalInterface logging null]
;; CHECK-NEXT: [LoggingExternalInterface logging 0]
;; CHECK-NEXT: [LoggingExternalInterface logging 1]
(func $ref.calling.catching (export "ref.calling.catching")
Expand Down Expand Up @@ -409,6 +469,11 @@
;; CHECK: [fuzz-exec] calling logging
;; CHECK-NEXT: [LoggingExternalInterface logging 42]
;; CHECK-NEXT: [LoggingExternalInterface logging 3.14159]
;; CHECK-NEXT: [LoggingExternalInterface logging null]
;; CHECK-NEXT: [LoggingExternalInterface logging object]
;; CHECK-NEXT: [LoggingExternalInterface logging function]
;; CHECK-NEXT: [LoggingExternalInterface logging null]
;; CHECK-NEXT: [LoggingExternalInterface logging null]

;; CHECK: [fuzz-exec] calling throwing
;; CHECK-NEXT: [exception thrown: imported-js-tag externref]
Expand All @@ -427,32 +492,62 @@
;; CHECK: [fuzz-exec] calling export.calling
;; CHECK-NEXT: [LoggingExternalInterface logging 42]
;; CHECK-NEXT: [LoggingExternalInterface logging 3.14159]
;; CHECK-NEXT: [LoggingExternalInterface logging null]
;; CHECK-NEXT: [LoggingExternalInterface logging object]
;; CHECK-NEXT: [LoggingExternalInterface logging function]
;; CHECK-NEXT: [LoggingExternalInterface logging null]
;; CHECK-NEXT: [LoggingExternalInterface logging null]
;; CHECK-NEXT: [exception thrown: imported-js-tag externref]

;; CHECK: [fuzz-exec] calling export.calling.rethrow
;; CHECK-NEXT: [LoggingExternalInterface logging 42]
;; CHECK-NEXT: [LoggingExternalInterface logging 3.14159]
;; CHECK-NEXT: [LoggingExternalInterface logging null]
;; CHECK-NEXT: [LoggingExternalInterface logging object]
;; CHECK-NEXT: [LoggingExternalInterface logging function]
;; CHECK-NEXT: [LoggingExternalInterface logging null]
;; CHECK-NEXT: [LoggingExternalInterface logging null]
;; CHECK-NEXT: [exception thrown: imported-js-tag externref]

;; CHECK: [fuzz-exec] calling export.calling.catching
;; CHECK-NEXT: [LoggingExternalInterface logging 42]
;; CHECK-NEXT: [LoggingExternalInterface logging 3.14159]
;; CHECK-NEXT: [LoggingExternalInterface logging null]
;; CHECK-NEXT: [LoggingExternalInterface logging object]
;; CHECK-NEXT: [LoggingExternalInterface logging function]
;; CHECK-NEXT: [LoggingExternalInterface logging null]
;; CHECK-NEXT: [LoggingExternalInterface logging null]
;; CHECK-NEXT: [LoggingExternalInterface logging 0]
;; CHECK-NEXT: [LoggingExternalInterface logging 1]

;; CHECK: [fuzz-exec] calling ref.calling
;; CHECK-NEXT: [LoggingExternalInterface logging 42]
;; CHECK-NEXT: [LoggingExternalInterface logging 3.14159]
;; CHECK-NEXT: [LoggingExternalInterface logging null]
;; CHECK-NEXT: [LoggingExternalInterface logging object]
;; CHECK-NEXT: [LoggingExternalInterface logging function]
;; CHECK-NEXT: [LoggingExternalInterface logging null]
;; CHECK-NEXT: [LoggingExternalInterface logging null]
;; CHECK-NEXT: [exception thrown: imported-js-tag externref]

;; CHECK: [fuzz-exec] calling ref.calling.rethrow
;; CHECK-NEXT: [LoggingExternalInterface logging 42]
;; CHECK-NEXT: [LoggingExternalInterface logging 3.14159]
;; CHECK-NEXT: [LoggingExternalInterface logging null]
;; CHECK-NEXT: [LoggingExternalInterface logging object]
;; CHECK-NEXT: [LoggingExternalInterface logging function]
;; CHECK-NEXT: [LoggingExternalInterface logging null]
;; CHECK-NEXT: [LoggingExternalInterface logging null]
;; CHECK-NEXT: [exception thrown: imported-js-tag externref]

;; CHECK: [fuzz-exec] calling ref.calling.catching
;; CHECK-NEXT: [LoggingExternalInterface logging 42]
;; CHECK-NEXT: [LoggingExternalInterface logging 3.14159]
;; CHECK-NEXT: [LoggingExternalInterface logging null]
;; CHECK-NEXT: [LoggingExternalInterface logging object]
;; CHECK-NEXT: [LoggingExternalInterface logging function]
;; CHECK-NEXT: [LoggingExternalInterface logging null]
;; CHECK-NEXT: [LoggingExternalInterface logging null]
;; CHECK-NEXT: [LoggingExternalInterface logging 0]
;; CHECK-NEXT: [LoggingExternalInterface logging 1]

Expand Down
Loading
Loading