diff options
-rwxr-xr-x | scripts/fuzz_opt.py | 201 | ||||
-rw-r--r-- | src/ir/names.h | 4 | ||||
-rw-r--r-- | src/tools/fuzzing.h | 243 | ||||
-rw-r--r-- | src/tools/wasm-opt.cpp | 22 | ||||
-rw-r--r-- | test/passes/fuzz_metrics_noprint.bin.txt | 50 | ||||
-rw-r--r-- | test/passes/translate-to-fuzz_all-features.txt | 1540 | ||||
-rw-r--r-- | test/unit/input/hello_world.wat | 11 | ||||
-rw-r--r-- | test/unit/input/random_data.txt | 1 | ||||
-rw-r--r-- | test/unit/test_initial_fuzz.py | 35 |
9 files changed, 1289 insertions, 818 deletions
diff --git a/scripts/fuzz_opt.py b/scripts/fuzz_opt.py index 0a0fd679b..3e4e42ab9 100755 --- a/scripts/fuzz_opt.py +++ b/scripts/fuzz_opt.py @@ -28,6 +28,8 @@ import time import traceback from test import shared +from test import support + assert sys.version_info.major == 3, 'requires Python 3!' @@ -68,9 +70,10 @@ def random_size(): return random.randint(INPUT_SIZE_MIN, 2 * INPUT_SIZE_MEAN - INPUT_SIZE_MIN) -def run(cmd): - print(' '.join(cmd)) - return subprocess.check_output(cmd, text=True) +def run(cmd, stderr=None, silent=False): + if not silent: + print(' '.join(cmd)) + return subprocess.check_output(cmd, stderr=stderr, text=True) def run_unchecked(cmd): @@ -115,15 +118,22 @@ def randomize_feature_opts(): print('randomized feature opts:', ' '.join(FEATURE_OPTS)) -FUZZ_OPTS = None -NANS = None -OOB = None -LEGALIZE = None ORIGINAL_V8_OPTS = shared.V8_OPTS[:] def randomize_fuzz_settings(): - global FUZZ_OPTS, NANS, OOB, LEGALIZE + # a list of the optimizations to run on the wasm + global FUZZ_OPTS + + # a boolean whether NaN values are allowed, or we de-NaN them + global NANS + + # a boolean whether out of bounds operations are allowed, or we bounds-enforce them + global OOB + + # a boolean whether we legalize the wasm for JS + global LEGALIZE + FUZZ_OPTS = [] if random.random() < 0.5: NANS = True @@ -159,6 +169,76 @@ def randomize_fuzz_settings(): print('randomized settings (NaNs, OOB, legalize, extra V8_OPTS):', NANS, OOB, LEGALIZE, extra_v8_opts) +def pick_initial_contents(): + # if we use an initial wasm file's contents as the basis for the + # fuzzing, then that filename, or None if we start entirely from scratch + global INITIAL_CONTENTS + + INITIAL_CONTENTS = None + # half the time don't use any initial contents + if random.random() < 0.5: + return + test_name = random.choice(all_tests) + print('initial contents:', test_name) + assert os.path.exists(test_name) + # tests that check validation errors are not helpful for us + if '.fail.' in test_name: + print('initial contents is just a .fail test') + return + if os.path.basename(test_name) in [ + # contains too many segments to run in a wasm VM + 'limit-segments_disable-bulk-memory.wast', + # https://github.com/WebAssembly/binaryen/issues/3203 + 'simd.wast', + # corner cases of escaping of names is not interesting + 'names.wast', + # huge amount of locals that make it extremely slow + 'too_much_for_liveness.wasm' + ]: + print('initial contents is disallowed') + return + + if test_name.endswith('.wast'): + # this can contain multiple modules, pick one + split_parts = support.split_wast(test_name) + if len(split_parts) > 1: + index = random.randint(0, len(split_parts) - 1) + chosen = split_parts[index] + module, asserts = chosen + if not module: + # there is no module in this choice (just asserts), ignore it + print('initial contents has no module') + return + test_name = 'initial.wat' + with open(test_name, 'w') as f: + f.write(module) + print(' picked submodule %d from multi-module wast' % index) + + global FEATURE_OPTS + FEATURE_OPTS += [ + # has not been enabled in the fuzzer yet + '--disable-exception-handling', + # has not been fuzzed in general yet + '--disable-memory64', + # DWARF is incompatible with multivalue atm; it's more important to + # fuzz multivalue since we aren't actually fuzzing DWARF here + '--strip-dwarf', + ] + + # the given wasm may not work with the chosen feature opts. for example, if + # we pick atomics.wast but want to run with --disable-atomics, then we'd + # error. test the wasm. + try: + run([in_bin('wasm-opt'), test_name] + FEATURE_OPTS, + stderr=subprocess.PIPE, + silent=True) + except Exception: + print('(initial contents not valid for features, ignoring)') + return + + INITIAL_CONTENTS = test_name + + # Test outputs we want to ignore are marked this way. IGNORE = '[binaryen-fuzzer-ignore]' @@ -334,19 +414,6 @@ class TestCaseHandler: return self.num_runs -# Run VMs and compare results - -class VM: - def __init__(self, name, run, can_compare_to_self, can_compare_to_others): - self.name = name - self.run = run - self.can_compare_to_self = can_compare_to_self - self.can_compare_to_others = can_compare_to_others - - def can_run(self, wasm): - return True - - # Fuzz the interpreter with --fuzz-exec. class FuzzExec(TestCaseHandler): frequency = 1 @@ -361,23 +428,45 @@ class CompareVMs(TestCaseHandler): def __init__(self): super(CompareVMs, self).__init__() - def byn_run(wasm): - return run_bynterp(wasm, ['--fuzz-exec-before']) + class BinaryenInterpreter: + name = 'binaryen interpreter' + + def run(self, wasm): + return run_bynterp(wasm, ['--fuzz-exec-before']) + + def can_run(self, wasm): + return True + + def can_compare_to_self(self): + return True + + def can_compare_to_others(self): + return True + + class D8: + name = 'd8' - def v8_run(wasm): - run([in_bin('wasm-opt'), wasm, '--emit-js-wrapper=' + wasm + '.js'] + FEATURE_OPTS) - return run_vm([shared.V8, wasm + '.js'] + shared.V8_OPTS + ['--', wasm]) + def run(self, wasm): + run([in_bin('wasm-opt'), wasm, '--emit-js-wrapper=' + wasm + '.js'] + FEATURE_OPTS) + return run_vm([shared.V8, wasm + '.js'] + shared.V8_OPTS + ['--', wasm]) - def yes(): - return True + def can_run(self, wasm): + # INITIAL_CONTENT is disallowed because some initial spec testcases + # have names that require mangling, see + # https://github.com/WebAssembly/binaryen/pull/3216 + return not INITIAL_CONTENTS - def if_legal_and_no_nans(): - return LEGALIZE and not NANS + def can_compare_to_self(self): + # With nans, VM differences can confuse us, so only very simple VMs + # can compare to themselves after opts in that case. + return not NANS - def if_no_nans(): - return not NANS + def can_compare_to_others(self): + # If not legalized, the JS will fail immediately, so no point to + # compare to others. + return LEGALIZE and not NANS - class Wasm2C(VM): + class Wasm2C: name = 'wasm2c' def __init__(self): @@ -467,14 +556,7 @@ class CompareVMs(TestCaseHandler): # NaNs can differ from wasm VMs return not NANS - self.vms = [ - VM('binaryen interpreter', byn_run, can_compare_to_self=yes, can_compare_to_others=yes), - # with nans, VM differences can confuse us, so only very simple VMs can compare to themselves after opts in that case. - # if not legalized, the JS will fail immediately, so no point to compare to others - VM('d8', v8_run, can_compare_to_self=if_no_nans, can_compare_to_others=if_legal_and_no_nans), - Wasm2C(), - Wasm2C2Wasm(), - ] + self.vms = [BinaryenInterpreter(), D8(), Wasm2C(), Wasm2C2Wasm()] def handle_pair(self, input, before_wasm, after_wasm, opts): before = self.run_vms(before_wasm) @@ -638,6 +720,13 @@ class Wasm2JS(TestCaseHandler): return run_vm([shared.NODEJS, js_file, 'a.wasm']) def can_run_on_feature_opts(self, feature_opts): + # TODO: properly handle memory growth. right now the wasm2js handler + # uses --emscripten which assumes the Memory is created before, and + # wasm2js.js just starts with a size of 1 and no limit. We should switch + # to non-emscripten mode or adding memory information, or check + # specifically for growth here + if INITIAL_CONTENTS: + return False return all([x in feature_opts for x in ['--disable-exception-handling', '--disable-simd', '--disable-threads', '--disable-bulk-memory', '--disable-nontrapping-float-to-int', '--disable-tail-call', '--disable-sign-ext', '--disable-reference-types', '--disable-multivalue', '--disable-gc']]) @@ -705,11 +794,25 @@ testcase_handlers = [ ] +test_suffixes = ['*.wasm', '*.wast', '*.wat'] +core_tests = shared.get_tests(shared.get_test_dir('.'), test_suffixes) +passes_tests = shared.get_tests(shared.get_test_dir('passes'), test_suffixes) +spec_tests = shared.get_tests(shared.get_test_dir('spec'), test_suffixes) +wasm2js_tests = shared.get_tests(shared.get_test_dir('wasm2js'), test_suffixes) +lld_tests = shared.get_tests(shared.get_test_dir('lld'), test_suffixes) +unit_tests = shared.get_tests(shared.get_test_dir(os.path.join('unit', 'input')), test_suffixes) +all_tests = core_tests + passes_tests + spec_tests + wasm2js_tests + lld_tests + unit_tests + + # Do one test, given an input file for -ttf and some optimizations to run -def test_one(random_input, opts, given_wasm): +def test_one(random_input, given_wasm): randomize_pass_debug() randomize_feature_opts() randomize_fuzz_settings() + pick_initial_contents() + + opts = randomize_opt_flags() + print('randomized opts:', ' '.join(opts)) print() if given_wasm: @@ -722,6 +825,8 @@ def test_one(random_input, opts, given_wasm): # emit the target features section so that reduction can work later, # without needing to specify the features generate_command = [in_bin('wasm-opt'), random_input, '-ttf', '-o', 'a.wasm', '--emit-target-features'] + FUZZ_OPTS + FEATURE_OPTS + if INITIAL_CONTENTS: + generate_command += ['--initial-fuzz=' + INITIAL_CONTENTS] if PRINT_WATS: printed = run(generate_command + ['--print']) with open('a.printed.wast', 'w') as f: @@ -843,10 +948,16 @@ def randomize_opt_flags(): # core opts while 1: choice = random.choice(opt_choices) - if '--flatten' in choice: + if '--flatten' in choice or '-O4' in choice: if has_flatten: print('avoiding multiple --flatten in a single command, due to exponential overhead') continue + if '--disable-exception-handling' not in FEATURE_OPTS: + print('avoiding --flatten due to exception catching which does not support it yet') + continue + if INITIAL_CONTENTS and os.path.getsize(INITIAL_CONTENTS) > 2000: + print('avoiding --flatten due using a large amount of initial contents, which may blow up') + continue else: has_flatten = True flag_groups.append(choice) @@ -928,15 +1039,13 @@ if __name__ == '__main__': with open(raw_input_data, 'wb') as f: f.write(bytes([random.randint(0, 255) for x in range(input_size)])) assert os.path.getsize(raw_input_data) == input_size - opts = randomize_opt_flags() - print('randomized opts:', ' '.join(opts)) # remove the generated wasm file, so that we can tell if the fuzzer # fails to create one if os.path.exists('a.wasm'): os.remove('a.wasm') # run an iteration of the fuzzer try: - total_wasm_size += test_one(raw_input_data, opts, given_wasm) + total_wasm_size += test_one(raw_input_data, given_wasm) except KeyboardInterrupt: print('(stopping by user request)') break diff --git a/src/ir/names.h b/src/ir/names.h index 3d49a4a36..a725c515a 100644 --- a/src/ir/names.h +++ b/src/ir/names.h @@ -72,6 +72,10 @@ inline Name getValidFunctionName(Module& module, Name root) { return getValidName( module, root, [&](Name test) { return !module.getFunctionOrNull(test); }); } +inline Name getValidEventName(Module& module, Name root) { + return getValidName( + module, root, [&](Name test) { return !module.getEventOrNull(test); }); +} } // namespace Names diff --git a/src/tools/fuzzing.h b/src/tools/fuzzing.h index 709ebe1e8..f4c29be55 100644 --- a/src/tools/fuzzing.h +++ b/src/tools/fuzzing.h @@ -31,6 +31,7 @@ high chance for set at start of loop #include <ir/find_all.h> #include <ir/literal-utils.h> #include <ir/manipulation.h> +#include <ir/names.h> #include <ir/utils.h> #include <support/file.h> #include <tools/optimization-options.h> @@ -188,6 +189,9 @@ public: void setAllowOOB(bool allowOOB_) { allowOOB = allowOOB_; } void build() { + if (HANG_LIMIT > 0) { + prepareHangLimitSupport(); + } if (allowMemory) { setupMemory(); } @@ -196,6 +200,7 @@ public: if (wasm.features.hasExceptionHandling()) { setupEvents(); } + modifyInitialFunctions(); addImportLoggingSupport(); // keep adding functions until we run out of input while (!finishedInput) { @@ -205,6 +210,9 @@ public: if (HANG_LIMIT > 0) { addHangLimitSupport(); } + if (allowMemory) { + finalizeMemory(); + } finalizeTable(); } @@ -241,7 +249,7 @@ private: // the memory that we use, a small portion so that we have a good chance of // looking at writes (we also look outside of this region with small // probability) this should be a power of 2 - static const int USABLE_MEMORY = 16; + const Address USABLE_MEMORY = 16; // the number of runtime iterations (function calls, loop backbranches) we // allow before we stop execution with a trap, to prevent hangs. 0 means @@ -406,21 +414,44 @@ private: wasm.addExport( builder.makeExport(hasher->name, hasher->name, ExternalKind::Function)); // Export memory so JS fuzzing can use it - wasm.addExport(builder.makeExport("memory", "0", ExternalKind::Memory)); + if (!wasm.getExportOrNull("memory")) { + wasm.addExport(builder.makeExport("memory", "0", ExternalKind::Memory)); + } } void setupTable() { wasm.table.exists = true; + wasm.table.initial = wasm.table.max = 0; wasm.table.segments.emplace_back(builder.makeConst(int32_t(0))); } std::map<Type, std::vector<Name>> globalsByType; void setupGlobals() { + // If there were initial wasm contents, there may be imported globals. That + // would be a problem in the fuzzer harness as we'd error if we do not + // provide them (and provide the proper type, etc.). + // Avoid that, so that all the standard fuzzing infrastructure can always + // run the wasm. + for (auto& global : wasm.globals) { + if (global->imported()) { + // Remove import info from imported globals, and give them a simple + // initializer. + global->module = global->base = Name(); + global->init = makeConst(global->type); + } else { + // If the initialization referred to an imported global, it no longer + // can point to the same global after we make it a non-imported global + // (as wasm doesn't allow that - you can only use an imported one). + if (global->init->is<GlobalGet>()) { + global->init = makeConst(global->type); + } + } + } for (size_t index = upTo(MAX_GLOBALS); index > 0; --index) { auto type = getConcreteType(); auto* global = - builder.makeGlobal(std::string("global$") + std::to_string(index), + builder.makeGlobal(Names::getValidGlobalName(wasm, "global$"), type, makeConst(type), Builder::Mutable); @@ -433,20 +464,87 @@ private: Index num = upTo(3); for (size_t i = 0; i < num; i++) { auto* event = - builder.makeEvent(std::string("event$") + std::to_string(i), + builder.makeEvent(Names::getValidEventName(wasm, "event$"), WASM_EVENT_ATTRIBUTE_EXCEPTION, Signature(getControlFlowType(), Type::none)); wasm.addEvent(event); } } + void finalizeMemory() { + for (auto& segment : wasm.memory.segments) { + Address maxOffset = segment.data.size(); + if (!segment.isPassive) { + if (auto* offset = segment.offset->dynCast<GlobalGet>()) { + // Using a non-imported global in a segment offset is not valid in + // wasm. This can occur due to us making what used to be an imported + // global, in initial contents, be not imported any more. To fix that, + // replace such invalid things with a constant. + // Note that it is still possible in theory to have imported globals + // here, as we only do the above for initial contents. While the + // fuzzer doesn't do so as of the time of this comment, do a check + // for full generality, so that this code essentially does "if this + // is invalid wasm, fix it up." + if (!wasm.getGlobal(offset->name)->imported()) { + // TODO: It would be better to avoid segment overlap so that + // MemoryPacking can run. + segment.offset = + builder.makeConst(Literal::makeFromInt32(0, Type::i32)); + } + } + if (auto* offset = segment.offset->dynCast<Const>()) { + maxOffset = maxOffset + offset->value.getInteger(); + } + } + wasm.memory.initial = std::max( + wasm.memory.initial, + Address((maxOffset + Memory::kPageSize - 1) / Memory::kPageSize)); + } + wasm.memory.initial = std::max(wasm.memory.initial, USABLE_MEMORY); + // Avoid an unlimited memory size, which would make fuzzing very difficult + // as different VMs will run out of system memory in different ways. + if (wasm.memory.max == Memory::kUnlimitedSize) { + wasm.memory.max = wasm.memory.initial; + } + if (wasm.memory.max <= wasm.memory.initial) { + // To allow growth to work (which a testcase may assume), try to make the + // maximum larger than the initial. + // TODO: scan the wasm for grow instructions? + wasm.memory.max = + std::min(Address(wasm.memory.initial + 1), Address(Memory::kMaxSize32)); + } + // Avoid an imported memory (which the fuzz harness would need to handle). + wasm.memory.module = wasm.memory.base = Name(); + } + void finalizeTable() { - wasm.table.initial = wasm.table.segments[0].data.size(); + for (auto& segment : wasm.table.segments) { + // If the offset is a global that was imported (which is ok) but no + // longer is (not ok) we need to change that. + if (auto* offset = segment.offset->dynCast<GlobalGet>()) { + if (!wasm.getGlobal(offset->name)->imported()) { + // TODO: the segments must not overlap... + segment.offset = + builder.makeConst(Literal::makeFromInt32(0, Type::i32)); + } + } + Address maxOffset = segment.data.size(); + if (auto* offset = segment.offset->dynCast<Const>()) { + maxOffset = maxOffset + offset->value.getInteger(); + } + wasm.table.initial = std::max(wasm.table.initial, maxOffset); + } wasm.table.max = oneIn(2) ? Address(Table::kUnlimitedSize) : wasm.table.initial; + // Avoid an imported table (which the fuzz harness would need to handle). + wasm.table.module = wasm.table.base = Name(); } - const Name HANG_LIMIT_GLOBAL = "hangLimit"; + Name HANG_LIMIT_GLOBAL; + + void prepareHangLimitSupport() { + HANG_LIMIT_GLOBAL = Names::getValidGlobalName(wasm, "hangLimit"); + } void addHangLimitSupport() { auto* glob = builder.makeGlobal(HANG_LIMIT_GLOBAL, @@ -455,15 +553,22 @@ private: Builder::Mutable); wasm.addGlobal(glob); + Name exportName = "hangLimitInitializer"; + auto funcName = Names::getValidFunctionName(wasm, exportName); auto* func = new Function; - func->name = "hangLimitInitializer"; + func->name = funcName; func->sig = Signature(Type::none, Type::none); - func->body = - builder.makeGlobalSet(glob->name, builder.makeConst(int32_t(HANG_LIMIT))); + func->body = builder.makeGlobalSet(HANG_LIMIT_GLOBAL, + builder.makeConst(int32_t(HANG_LIMIT))); wasm.addFunction(func); + if (wasm.getExportOrNull(exportName)) { + // We must export our actual hang limit function - remove anything + // previously existing. + wasm.removeExport(exportName); + } auto* export_ = new Export; - export_->name = func->name; + export_->name = exportName; export_->value = func->name; export_->kind = ExternalKind::Function; wasm.addExport(export_); @@ -507,11 +612,28 @@ private: std::map<Type, std::vector<Index>> typeLocals; // type => list of locals with that type + void prepareToCreateFunctionContents(Function* func_) { + func = func_; + labelIndex = 0; + assert(breakableStack.empty()); + assert(hangStack.empty()); + } + + void finishCreatingFunctionContents() { + if (HANG_LIMIT > 0) { + addHangLimitChecks(func); + } + typeLocals.clear(); + assert(breakableStack.empty()); + assert(hangStack.empty()); + } + + Index numAddedFunctions = 0; + Function* addFunction() { LOGGING_PERCENT = upToSquared(100); - Index num = wasm.functions.size(); func = new Function; - func->name = std::string("func_") + std::to_string(num); + func->name = Names::getValidFunctionName(wasm, "func"); assert(typeLocals.empty()); Index numParams = upToSquared(MAX_PARAMS); std::vector<Type> params; @@ -528,9 +650,7 @@ private: typeLocals[type].push_back(params.size() + func->vars.size()); func->vars.push_back(type); } - labelIndex = 0; - assert(breakableStack.empty()); - assert(hangStack.empty()); + prepareToCreateFunctionContents(func); // with small chance, make the body unreachable auto bodyType = func->sig.results; if (oneIn(10)) { @@ -558,15 +678,11 @@ private: fixLabels(func); } // Add hang limit checks after all other operations on the function body. - if (HANG_LIMIT > 0) { - addHangLimitChecks(func); - } - assert(breakableStack.empty()); - assert(hangStack.empty()); wasm.addFunction(func); // export some, but not all (to allow inlining etc.). make sure to // export at least one, though, to keep each testcase interesting - if (num == 0 || oneIn(2)) { + if ((numAddedFunctions == 0 || oneIn(2)) && + !wasm.getExportOrNull(func->name)) { auto* export_ = new Export; export_->name = func->name; export_->value = func->name; @@ -578,7 +694,8 @@ private: wasm.table.segments[0].data.push_back(func->name); } // cleanup - typeLocals.clear(); + finishCreatingFunctionContents(); + numAddedFunctions++; return func; } @@ -783,10 +900,86 @@ private: ReFinalize().walkFunctionInModule(func, &wasm); } + void modifyInitialFunctions() { + if (wasm.functions.empty()) { + return; + } + // Pick a chance to fuzz the contents of a function. + const int RESOLUTION = 10; + auto chance = upTo(RESOLUTION + 1); + for (auto& ref : wasm.functions) { + auto* func = ref.get(); + prepareToCreateFunctionContents(func); + if (func->imported()) { + // We can't allow extra imports, as the fuzzing infrastructure wouldn't + // know what to provide. + func->module = func->base = Name(); + func->body = make(func->sig.results); + } + // Optionally, fuzz the function contents. + if (upTo(RESOLUTION) >= chance) { + dropToLog(func); + // TODO add some locals? and the rest of addFunction's operations? + // TODO: interposition, replace initial a(b) with a(RANDOM_THING(b)) + // TODO: if we add OOB checks after creation, then we can do it on + // initial contents too, and it may be nice to *not* run these + // passes, like we don't run them on new functions. But, we may + // still want to run them some of the time, at least, so that we + // check variations on initial testcases even at the risk of OOB. + recombine(func); + mutate(func); + fixLabels(func); + } + // Note that even if we don't fuzz the contents we still need to call + // finish so that we add hang limit protection and other general things. + finishCreatingFunctionContents(); + } + // Remove a start function - the fuzzing harness expects code to run only + // from exports. + wasm.start = Name(); + } + + // Initial wasm contents may have come from a test that uses the drop pattern: + // + // (drop ..something interesting..) + // + // The dropped interesting thing is optimized to some other interesting thing + // by a pass, and we verify it is the expected one. But this does not use the + // value in a way the fuzzer can notice. Replace some drops with a logging + // operation instead. + void dropToLog(Function* func) { + // Don't always do this. + if (oneIn(2)) { + return; + } + struct Modder : public PostWalker<Modder> { + Module& wasm; + TranslateToFuzzReader& parent; + + Modder(Module& wasm, TranslateToFuzzReader& parent) + : wasm(wasm), parent(parent) {} + + void visitDrop(Drop* curr) { + if (parent.isLoggableType(curr->value->type) && parent.oneIn(2)) { + replaceCurrent(parent.builder.makeCall(std::string("log-") + + curr->value->type.toString(), + {curr->value}, + Type::none)); + } + } + }; + Modder modder(wasm, *this); + modder.walk(func->body); + } + // the fuzzer external interface sends in zeros (simpler to compare // across invocations from JS or wasm-opt etc.). Add invocations in // the wasm, so they run everywhere void addInvocations(Function* func) { + Name name = func->name.str + std::string("_invoker"); + if (wasm.getFunctionOrNull(name) || wasm.getExportOrNull(name)) { + return; + } std::vector<Expression*> invocations; while (oneIn(2) && !finishedInput) { std::vector<Expression*> args; @@ -808,13 +1001,13 @@ private: return; } auto* invoker = new Function; - invoker->name = func->name.str + std::string("_invoker"); + invoker->name = name; invoker->sig = Signature(Type::none, Type::none); invoker->body = builder.makeBlock(invocations); wasm.addFunction(invoker); auto* export_ = new Export; - export_->name = invoker->name; - export_->value = invoker->name; + export_->name = name; + export_->value = name; export_->kind = ExternalKind::Function; wasm.addExport(export_); } diff --git a/src/tools/wasm-opt.cpp b/src/tools/wasm-opt.cpp index ecf4f1c7c..56a26ccfb 100644 --- a/src/tools/wasm-opt.cpp +++ b/src/tools/wasm-opt.cpp @@ -81,6 +81,7 @@ int main(int argc, const char* argv[]) { bool fuzzExecAfter = false; std::string extraFuzzCommand; bool translateToFuzz = false; + std::string initialFuzz; bool fuzzPasses = false; bool fuzzMemory = true; bool fuzzOOB = true; @@ -141,6 +142,13 @@ int main(int argc, const char* argv[]) { "fuzzing", Options::Arguments::Zero, [&](Options* o, const std::string& arguments) { translateToFuzz = true; }) + .add("--initial-fuzz", + "-if", + "Initial wasm content in translate-to-fuzz (-ttf) mode", + Options::Arguments::One, + [&initialFuzz](Options* o, const std::string& argument) { + initialFuzz = argument; + }) .add("--fuzz-passes", "-fp", "Pick a random set of passes to run, useful for fuzzing. this depends " @@ -224,7 +232,13 @@ int main(int argc, const char* argv[]) { Fatal() << message; }; - if (!translateToFuzz) { + // In normal (non-translate-to-fuzz) mode we read the input file. In + // translate-to-fuzz mode the input file is the random data, and used later + // down in TranslateToFuzzReader, but there is also an optional initial fuzz + // file that if it exists we read it, then add more fuzz on top. + if (!translateToFuzz || initialFuzz.size()) { + std::string inputFile = + translateToFuzz ? initialFuzz : options.extra["infile"]; ModuleReader reader; // Enable DWARF parsing if we were asked for debug info, and were not // asked to remove it. @@ -232,7 +246,7 @@ int main(int argc, const char* argv[]) { !willRemoveDebugInfo(options.passes)); reader.setProfile(options.profile); try { - reader.read(options.extra["infile"], wasm, inputSourceMapFilename); + reader.read(inputFile, wasm, inputSourceMapFilename); } catch (ParseException& p) { p.dump(std::cerr); std::cerr << '\n'; @@ -253,8 +267,8 @@ int main(int argc, const char* argv[]) { exitOnInvalidWasm("error validating input"); } } - } else { - // translate-to-fuzz + } + if (translateToFuzz) { options.applyFeatures(wasm); TranslateToFuzzReader reader(wasm, options.extra["infile"]); if (fuzzPasses) { diff --git a/test/passes/fuzz_metrics_noprint.bin.txt b/test/passes/fuzz_metrics_noprint.bin.txt index da8acec57..ad654067f 100644 --- a/test/passes/fuzz_metrics_noprint.bin.txt +++ b/test/passes/fuzz_metrics_noprint.bin.txt @@ -1,30 +1,30 @@ total [events] : 0 - [exports] : 45 - [funcs] : 72 + [exports] : 35 + [funcs] : 47 [globals] : 7 [imports] : 4 [memory-data] : 4 - [table-data] : 30 - [total] : 5321 - [vars] : 256 - binary : 416 - block : 755 - break : 178 - call : 234 - call_indirect : 49 - const : 970 - drop : 58 - global.get : 461 - global.set : 201 - if : 313 - load : 98 - local.get : 412 - local.set : 301 - loop : 119 - nop : 80 - return : 209 - select : 46 - store : 43 - unary : 375 - unreachable : 3 + [table-data] : 16 + [total] : 6048 + [vars] : 137 + binary : 458 + block : 871 + break : 230 + call : 240 + call_indirect : 52 + const : 1081 + drop : 40 + global.get : 480 + global.set : 204 + if : 356 + load : 108 + local.get : 527 + local.set : 351 + loop : 155 + nop : 120 + return : 233 + select : 51 + store : 58 + unary : 432 + unreachable : 1 diff --git a/test/passes/translate-to-fuzz_all-features.txt b/test/passes/translate-to-fuzz_all-features.txt index 3d4182cba..5a9d9dc7b 100644 --- a/test/passes/translate-to-fuzz_all-features.txt +++ b/test/passes/translate-to-fuzz_all-features.txt @@ -1,35 +1,31 @@ (module (type $none_=>_none (func)) - (type $none_=>_i32 (func (result i32))) - (type $i64_=>_none (func (param i64))) (type $i32_=>_none (func (param i32))) + (type $i64_=>_none (func (param i64))) (type $f32_=>_none (func (param f32))) (type $f64_=>_none (func (param f64))) (type $v128_=>_none (func (param v128))) + (type $funcref_=>_none (func (param funcref))) (type $exnref_=>_none (func (param exnref))) - (type $funcref_f64_=>_i32 (func (param funcref f64) (result i32))) - (type $none_=>_f32 (func (result f32))) - (type $eqref_i32_=>_funcref (func (param eqref i32) (result funcref))) - (type $i32_i32_f32_exnref_=>_externref (func (param i32 i32 f32 exnref) (result externref))) - (type $externref_f64_f32_eqref_i31ref_anyref_=>_externref (func (param externref f64 f32 eqref i31ref anyref) (result externref))) - (type $exnref_f32_i31ref_externref_funcref_i31ref_i64_=>_exnref (func (param exnref f32 i31ref externref funcref i31ref i64) (result exnref))) - (type $none_=>_eqref_i31ref_i64_v128_eqref (func (result eqref i31ref i64 v128 eqref))) - (type $v128_i31ref_=>_eqref_i31ref_i64_v128_eqref (func (param v128 i31ref) (result eqref i31ref i64 v128 eqref))) - (type $none_=>_i31ref (func (result i31ref))) + (type $eqref_=>_none (func (param eqref))) + (type $none_=>_i32 (func (result i32))) + (type $anyref_anyref_externref_eqref_v128_=>_i64 (func (param anyref anyref externref eqref v128) (result i64))) + (type $i31ref_v128_=>_exnref (func (param i31ref v128) (result exnref))) + (type $i31ref_externref_v128_f32_f32_=>_eqref (func (param i31ref externref v128 f32 f32) (result eqref))) (import "fuzzing-support" "log-i32" (func $log-i32 (param i32))) (import "fuzzing-support" "log-i64" (func $log-i64 (param i64))) (import "fuzzing-support" "log-f32" (func $log-f32 (param f32))) (import "fuzzing-support" "log-f64" (func $log-f64 (param f64))) (import "fuzzing-support" "log-v128" (func $log-v128 (param v128))) (import "fuzzing-support" "log-exnref" (func $log-exnref (param exnref))) - (memory $0 (shared 1 1)) + (memory $0 (shared 16 17)) (data (i32.const 0) "N\0fN\f5\f9\b1\ff\fa\eb\e5\fe\a7\ec\fb\fc\f4\a6\e4\ea\f0\ae\e3") - (table $0 5 5 funcref) - (elem (i32.const 0) $func_9 $func_9 $func_9 $func_10 $func_14) - (global $global$5 (mut eqref) (ref.null eq)) - (global $global$4 (mut i32) (i32.const 470177031)) - (global $global$3 (mut f64) (f64.const 2147483647)) - (global $global$2 (mut (eqref f32 eqref funcref funcref i64)) (tuple.make + (table $0 3 funcref) + (elem (i32.const 0) $func_1 $func_1 $func_1) + (global $global$ (mut eqref) (ref.null eq)) + (global $global$_0 (mut i32) (i32.const 470177031)) + (global $global$_1 (mut f64) (f64.const 2147483647)) + (global $global$_2 (mut (eqref f32 eqref funcref funcref i64)) (tuple.make (ref.null eq) (f32.const -2147483648) (ref.null eq) @@ -37,257 +33,274 @@ (ref.null func) (i64.const -32) )) - (global $global$1 (mut f32) (f32.const -32769)) + (global $global$_3 (mut f32) (f32.const -32769)) (global $hangLimit (mut i32) (i32.const 10)) (export "hashMemory" (func $hashMemory)) (export "memory" (memory $0)) - (export "func_7_invoker" (func $func_7_invoker)) - (export "func_9" (func $func_9)) - (export "func_11_invoker" (func $func_11_invoker)) - (export "func_14" (func $func_14)) - (export "func_18" (func $func_18)) - (export "func_19" (func $func_19)) + (export "func" (func $func)) + (export "func_invoker" (func $func_invoker)) + (export "func_1_invoker" (func $func_1_invoker)) + (export "func_2" (func $func_2)) + (export "func_2_invoker" (func $func_2_invoker)) + (export "func_3" (func $func_3)) (export "hangLimitInitializer" (func $hangLimitInitializer)) (func $hashMemory (result i32) (local $0 i32) - (local.set $0 - (i32.const 5381) - ) - (local.set $0 - (i32.xor - (i32.add - (i32.shl - (local.get $0) - (i32.const 5) - ) - (local.get $0) + (block + (if + (i32.eqz + (global.get $hangLimit) ) - (i32.load8_u - (i32.const 0) + (return + (i32.const 69) + ) + ) + (global.set $hangLimit + (i32.sub + (global.get $hangLimit) + (i32.const 1) ) ) ) - (local.set $0 - (i32.xor - (i32.add - (i32.shl + (block (result i32) + (local.set $0 + (i32.const 5381) + ) + (local.set $0 + (i32.xor + (i32.add + (i32.shl + (local.get $0) + (i32.const 5) + ) (local.get $0) - (i32.const 5) ) - (local.get $0) - ) - (i32.load8_u offset=1 - (i32.const 0) + (i32.load8_u + (i32.const 0) + ) ) ) - ) - (local.set $0 - (i32.xor - (i32.add - (i32.shl + (local.set $0 + (i32.xor + (i32.add + (i32.shl + (local.get $0) + (i32.const 5) + ) (local.get $0) - (i32.const 5) ) - (local.get $0) - ) - (i32.load8_u offset=2 - (i32.const 0) + (i32.load8_u offset=1 + (i32.const 0) + ) ) ) - ) - (local.set $0 - (i32.xor - (i32.add - (i32.shl + (local.set $0 + (i32.xor + (i32.add + (i32.shl + (local.get $0) + (i32.const 5) + ) (local.get $0) - (i32.const 5) ) - (local.get $0) - ) - (i32.load8_u offset=3 - (i32.const 0) + (i32.load8_u offset=2 + (i32.const 0) + ) ) ) - ) - (local.set $0 - (i32.xor - (i32.add - (i32.shl + (local.set $0 + (i32.xor + (i32.add + (i32.shl + (local.get $0) + (i32.const 5) + ) (local.get $0) - (i32.const 5) ) - (local.get $0) - ) - (i32.load8_u offset=4 - (i32.const 0) + (i32.load8_u offset=3 + (i32.const 0) + ) ) ) - ) - (local.set $0 - (i32.xor - (i32.add - (i32.shl + (local.set $0 + (i32.xor + (i32.add + (i32.shl + (local.get $0) + (i32.const 5) + ) (local.get $0) - (i32.const 5) ) - (local.get $0) - ) - (i32.load8_u offset=5 - (i32.const 0) + (i32.load8_u offset=4 + (i32.const 0) + ) ) ) - ) - (local.set $0 - (i32.xor - (i32.add - (i32.shl + (local.set $0 + (i32.xor + (i32.add + (i32.shl + (local.get $0) + (i32.const 5) + ) (local.get $0) - (i32.const 5) ) - (local.get $0) - ) - (i32.load8_u offset=6 - (i32.const 0) + (i32.load8_u offset=5 + (i32.const 0) + ) ) ) - ) - (local.set $0 - (i32.xor - (i32.add - (i32.shl + (local.set $0 + (i32.xor + (i32.add + (i32.shl + (local.get $0) + (i32.const 5) + ) (local.get $0) - (i32.const 5) ) - (local.get $0) - ) - (i32.load8_u offset=7 - (i32.const 0) + (i32.load8_u offset=6 + (i32.const 0) + ) ) ) - ) - (local.set $0 - (i32.xor - (i32.add - (i32.shl + (local.set $0 + (i32.xor + (i32.add + (i32.shl + (local.get $0) + (i32.const 5) + ) (local.get $0) - (i32.const 5) ) - (local.get $0) - ) - (i32.load8_u offset=8 - (i32.const 0) + (i32.load8_u offset=7 + (i32.const 0) + ) ) ) - ) - (local.set $0 - (i32.xor - (i32.add - (i32.shl + (local.set $0 + (i32.xor + (i32.add + (i32.shl + (local.get $0) + (i32.const 5) + ) (local.get $0) - (i32.const 5) ) - (local.get $0) - ) - (i32.load8_u offset=9 - (i32.const 0) + (i32.load8_u offset=8 + (i32.const 0) + ) ) ) - ) - (local.set $0 - (i32.xor - (i32.add - (i32.shl + (local.set $0 + (i32.xor + (i32.add + (i32.shl + (local.get $0) + (i32.const 5) + ) (local.get $0) - (i32.const 5) ) - (local.get $0) - ) - (i32.load8_u offset=10 - (i32.const 0) + (i32.load8_u offset=9 + (i32.const 0) + ) ) ) - ) - (local.set $0 - (i32.xor - (i32.add - (i32.shl + (local.set $0 + (i32.xor + (i32.add + (i32.shl + (local.get $0) + (i32.const 5) + ) (local.get $0) - (i32.const 5) ) - (local.get $0) - ) - (i32.load8_u offset=11 - (i32.const 0) + (i32.load8_u offset=10 + (i32.const 0) + ) ) ) - ) - (local.set $0 - (i32.xor - (i32.add - (i32.shl + (local.set $0 + (i32.xor + (i32.add + (i32.shl + (local.get $0) + (i32.const 5) + ) (local.get $0) - (i32.const 5) ) - (local.get $0) - ) - (i32.load8_u offset=12 - (i32.const 0) + (i32.load8_u offset=11 + (i32.const 0) + ) ) ) - ) - (local.set $0 - (i32.xor - (i32.add - (i32.shl + (local.set $0 + (i32.xor + (i32.add + (i32.shl + (local.get $0) + (i32.const 5) + ) (local.get $0) - (i32.const 5) ) - (local.get $0) - ) - (i32.load8_u offset=13 - (i32.const 0) + (i32.load8_u offset=12 + (i32.const 0) + ) ) ) - ) - (local.set $0 - (i32.xor - (i32.add - (i32.shl + (local.set $0 + (i32.xor + (i32.add + (i32.shl + (local.get $0) + (i32.const 5) + ) (local.get $0) - (i32.const 5) ) - (local.get $0) - ) - (i32.load8_u offset=14 - (i32.const 0) + (i32.load8_u offset=13 + (i32.const 0) + ) ) ) - ) - (local.set $0 - (i32.xor - (i32.add - (i32.shl + (local.set $0 + (i32.xor + (i32.add + (i32.shl + (local.get $0) + (i32.const 5) + ) (local.get $0) - (i32.const 5) ) - (local.get $0) + (i32.load8_u offset=14 + (i32.const 0) + ) ) - (i32.load8_u offset=15 - (i32.const 0) + ) + (local.set $0 + (i32.xor + (i32.add + (i32.shl + (local.get $0) + (i32.const 5) + ) + (local.get $0) + ) + (i32.load8_u offset=15 + (i32.const 0) + ) ) ) + (local.get $0) ) - (local.get $0) ) - (func $func_7 (param $0 i32) (param $1 i32) (param $2 f32) (param $3 exnref) (result externref) - (local $4 i32) + (func $func (param $0 i31ref) (param $1 v128) (result exnref) (block (if (i32.eqz (global.get $hangLimit) ) (return - (ref.null extern) + (ref.null exn) ) ) (global.set $hangLimit @@ -297,34 +310,30 @@ ) ) ) - (ref.null extern) + (ref.null exn) ) - (func $func_7_invoker + (func $func_invoker (drop - (call $func_7 - (i32.const -127) - (i32.const -268435456) - (f32.const 1179405440) - (ref.null exn) + (call $func + (i31.new + (i32.const 1633240409) + ) + (v128.const i32x4 0xcf800000 0x4c816020 0x3e1a1a1a 0x00000000) ) ) - (call $log-i32 - (call $hashMemory) - ) ) - (func $func_9 (result f32) - (local $0 anyref) - (local $1 f64) - (local $2 (anyref anyref)) - (local $3 eqref) - (local $4 externref) + (func $func_0 (param $0 anyref) (param $1 anyref) (param $2 externref) (param $3 eqref) (param $4 v128) (result i64) + (local $5 eqref) + (local $6 (funcref externref i32 f64 f32 i64)) + (local $7 (f32 funcref eqref i32 i31ref i32)) + (local $8 v128) (block (if (i32.eqz (global.get $hangLimit) ) (return - (f32.const 1.4949444621624858e-31) + (i64.const 2147483647) ) ) (global.set $hangLimit @@ -334,35 +343,30 @@ ) ) ) - (block $label$0 + (block $label$0 (result i64) (nop) - (block $label$1 - (nop) - (return - (f32.const 7.396028525772014e-24) + (br_if $label$0 + (tuple.extract 1 + (tuple.make + (f64.const -255) + (i64.const -127) + (ref.null func) + ) ) + (i32.const -24) ) ) ) - (func $func_10 (param $0 externref) (param $1 f64) (param $2 f32) (param $3 eqref) (param $4 i31ref) (param $5 anyref) (result externref) - (local $6 i64) - (local $7 (anyref exnref f32 f64 f64)) - (local $8 eqref) - (local $9 exnref) - (local $10 i64) - (local $11 f64) - (local $12 f32) - (local $13 v128) - (local $14 exnref) - (local $15 (funcref funcref v128 i31ref v128)) - (local $16 anyref) + (func $func_1 (param $0 i31ref) (param $1 externref) (param $2 v128) (param $3 f32) (param $4 f32) (result eqref) + (local $5 v128) + (local $6 (exnref externref i64 i32 f64 funcref)) (block (if (i32.eqz (global.get $hangLimit) ) (return - (ref.null extern) + (ref.null eq) ) ) (global.set $hangLimit @@ -372,121 +376,75 @@ ) ) ) - (ref.null extern) + (ref.null eq) ) - (func $func_11 (param $0 exnref) (param $1 f32) (param $2 i31ref) (param $3 externref) (param $4 funcref) (param $5 i31ref) (param $6 i64) (result exnref) - (local $7 (i32 i64 anyref externref externref eqref)) - (block - (if - (i32.eqz - (global.get $hangLimit) - ) - (return - (ref.null exn) - ) - ) - (global.set $hangLimit - (i32.sub - (global.get $hangLimit) - (i32.const 1) + (func $func_1_invoker + (drop + (call $func_1 + (i31.new + (i32.const 16777216) ) + (ref.null extern) + (v128.const i32x4 0x5d455846 0xcf800000 0x42aa0000 0x46b2a800) + (f32.const -17592186044416) + (f32.const -4294967296) ) ) - (ref.null exn) - ) - (func $func_11_invoker + (call $log-i32 + (call $hashMemory) + ) (drop - (call $func_11 - (ref.null exn) - (f32.const -1022.1400146484375) + (call $func_1 (i31.new - (i32.const -32766) + (i32.const 73) ) (ref.null extern) - (ref.null func) - (i31.new - (i32.const -65535) - ) - (i64.const 3) + (v128.const i32x4 0xffff8001 0xffffffff 0x184c764c 0x10105676) + (f32.const -9223372036854775808) + (f32.const 129) ) ) (call $log-i32 (call $hashMemory) ) - ) - (func $func_13 (result i31ref) - (local $0 i31ref) - (local $1 (funcref f32 anyref f32 externref)) - (local $2 f64) - (local $3 f64) - (local $4 (i32 v128)) - (local $5 (anyref i64 v128 eqref funcref exnref)) - (local $6 i32) - (block - (if - (i32.eqz - (global.get $hangLimit) - ) - (return - (i31.new - (i32.const -28) - ) - ) - ) - (global.set $hangLimit - (i32.sub - (global.get $hangLimit) - (i32.const 1) + (drop + (call $func_1 + (i31.new + (i32.const -4096) ) + (ref.null extern) + (v128.const i32x4 0x00000000 0x00000020 0x00000001 0xffc00000) + (f32.const -nan:0x7fffbd) + (f32.const -255.8040008544922) ) ) - (i31.new - (i32.const 64) + (call $log-i32 + (call $hashMemory) ) - ) - (func $func_14 (result i32) - (local $0 i32) - (local $1 funcref) - (local $2 (anyref externref)) - (local $3 funcref) - (local $4 i64) - (local $5 externref) - (local $6 (exnref f64)) - (local $7 (anyref f64 f64)) - (local $8 (i64 i32 eqref exnref)) - (local $9 (v128 i64 funcref i32 anyref anyref)) - (local $10 (i32 eqref f64 funcref)) - (local $11 eqref) - (block - (if - (i32.eqz - (global.get $hangLimit) - ) - (return - (local.get $0) - ) - ) - (global.set $hangLimit - (i32.sub - (global.get $hangLimit) - (i32.const 1) + (drop + (call $func_1 + (i31.new + (i32.const 1936946035) ) + (ref.null extern) + (v128.const i32x4 0xff010001 0x4e07ffff 0x00060002 0xff00001f) + (f32.const 6918) + (f32.const 4.921484278772694e-25) ) ) - (block $label$0 (result i32) - (nop) - (local.get $0) + (call $log-i32 + (call $hashMemory) ) ) - (func $func_15 (result i32) + (func $func_2 (param $0 funcref) + (local $1 exnref) + (local $2 (f64 externref f32 f32 v128 eqref)) (block (if (i32.eqz (global.get $hangLimit) ) - (return - (i32.const 32768) - ) + (return) ) (global.set $hangLimit (i32.sub @@ -495,89 +453,26 @@ ) ) ) - (global.get $global$4) - ) - (func $func_16 (param $0 eqref) (param $1 i32) (result funcref) - (local $2 v128) - (local $3 funcref) - (local $4 eqref) - (block - (if - (i32.eqz - (global.get $hangLimit) - ) - (return - (local.get $3) - ) - ) - (global.set $hangLimit - (i32.sub - (global.get $hangLimit) - (i32.const 1) + (block $label$0 + (f32.store offset=3 align=2 + (i32.and + (i32.const 15) + (i32.const 15) ) + (f32.const 66) ) + (return) ) - (loop $label$1 (result funcref) - (block - (if - (i32.eqz - (global.get $hangLimit) - ) - (return - (local.get $3) - ) - ) - (global.set $hangLimit - (i32.sub - (global.get $hangLimit) - (i32.const 1) - ) - ) - ) - (block (result funcref) - (block $label$2 - (br_if $label$2 - (i32.eqz - (block $label$3 - (global.set $global$4 - (local.tee $1 - (local.get $1) - ) - ) - (block $label$4 - (nop) - (br_if $label$1 - (i32.eqz - (i31.get_u - (i31.new - (i32.const -90) - ) - ) - ) - ) - ) - (br $label$1) - ) - ) - ) - (memory.init 0 - (i32.and - (local.get $1) - (i32.const 15) - ) - (i32.const 16) - (i32.const 3) - ) - ) - (br_if $label$1 - (i32.const 131071) - ) - (local.get $3) - ) + ) + (func $func_2_invoker + (call $func_2 + (ref.null func) ) ) - (func $func_17 (param $0 i64) - (local $1 externref) + (func $func_3 (param $0 eqref) + (local $1 eqref) + (local $2 funcref) + (local $3 funcref) (block (if (i32.eqz @@ -593,249 +488,214 @@ ) ) (block $label$0 - (call $log-i32 - (call $hashMemory) - ) + (atomic.fence) (if - (i32.eqz - (f64.gt - (f64.const 103) - (f64.max - (f64.const 8388607.124) - (if - (i32.eqz - (global.get $global$4) - ) - (block $label$1 - (call $log-v128 - (f64x2.replace_lane 0 - (f32x4.neg - (i8x16.shr_u - (v128.load offset=3 - (i32.const 65535) + (i64.gt_s + (i64.const 379) + (i64.add + (i32.atomic.load offset=22 + (i32.and + (i32.atomic.load offset=22 + (i32.and + (select + (block $label$1 (result i32) + (block $label$2 + (memory.init 0 + (i32.and + (ref.eq + (ref.null eq) + (local.get $0) + ) + (i32.const 15) + ) + (i32.const 8) + (i32.const 9) ) - (loop $label$2 (result i32) - (block - (if - (i32.eqz - (global.get $hangLimit) - ) - (return) + (if + (i32.eqz + (i32.const -75) + ) + (block $label$3 + (nop) + (nop) + ) + (nop) + ) + ) + (i32.const 1903327608) + ) + (select + (loop $label$6 (result i32) + (block + (if + (i32.eqz + (global.get $hangLimit) ) - (global.set $hangLimit - (i32.sub - (global.get $hangLimit) - (i32.const 1) - ) + (return) + ) + (global.set $hangLimit + (i32.sub + (global.get $hangLimit) + (i32.const 1) ) ) - (block (result i32) - (block $label$3 - (call $log-exnref - (ref.null exn) + ) + (block $label$7 (result i32) + (loop $label$8 + (block + (if + (i32.eqz + (global.get $hangLimit) + ) + (return) ) - (call $log-i32 - (i32.atomic.rmw8.sub_u offset=22 - (i32.and - (i32.const 6) - (i32.const 15) - ) - (i32.const 1852667194) + (global.set $hangLimit + (i32.sub + (global.get $hangLimit) + (i32.const 1) ) ) ) - (br_if $label$2 - (tuple.extract 0 - (tuple.make + (block + (nop) + (br_if $label$8 + (i32.eqz (i32.const -2147483648) - (i31.new - (i32.const -32767) - ) ) ) + (nop) ) - (i32.const -2147483648) ) + (i32.const 76) ) ) - ) - (f64.const -nan:0xffffffffffff3) - ) - ) - (br $label$0) - ) - (block $label$4 - (br_if $label$0 - (i32.eqz - (tuple.extract 2 - (block $label$5 - (call $log-i32 - (call $hashMemory) + (global.get $global$_0) + (if + (i32.const -96) + (block $label$4 + (f32.store offset=3 + (i32.and + (i32.const 4124) + (i32.const 15) + ) + (f32.const -8192.1474609375) + ) + (return) + ) + (block $label$5 + (memory.copy + (i32.and + (i32.const 32768) + (i32.const 15) + ) + (i32.and + (i32.const 2087520520) + (i32.const 15) + ) + (i32.const -131072) + ) + (return) ) - (br $label$0) ) ) + (i32.const 85) ) + (i32.const 15) ) - (br $label$0) ) + (i32.const 15) ) ) + (i64.const 125) ) ) - (block $label$6 - (call $log-i32 - (call $hashMemory) - ) - (call $log-i32 - (call $hashMemory) - ) - ) - (if - (i32.eqz - (i32.or - (i32.const 65535) - (i32.const 8) - ) - ) - (call $log-i32 - (i32.const 608321884) - ) - (atomic.fence) - ) - ) - ) - ) - (func $func_18 (param $0 v128) (param $1 i31ref) (result eqref i31ref i64 v128 eqref) - (local $2 i64) - (local $3 v128) - (local $4 f32) - (block - (if - (i32.eqz - (global.get $hangLimit) - ) - (return - (tuple.make - (ref.null eq) - (i31.new - (i32.const -2147483648) - ) - (i64.const 369041285507055655) - (v128.const i32x4 0xffffffd1 0xffffffff 0x25312936 0x5455263f) - (ref.null eq) - ) - ) - ) - (global.set $hangLimit - (i32.sub - (global.get $hangLimit) - (i32.const 1) - ) - ) - ) - (tuple.make - (ref.null eq) - (local.get $1) - (i64.const -32766) - (v128.bitselect - (local.get $3) - (block $label$2 - (atomic.fence) - (return - (tuple.make - (ref.null eq) - (i31.new - (i32.const 19521) + (block $label$9 + (loop $label$10 + (block + (if + (i32.eqz + (global.get $hangLimit) + ) + (return) + ) + (global.set $hangLimit + (i32.sub + (global.get $hangLimit) + (i32.const 1) + ) ) - (i64.const -32766) - (v128.const i32x4 0x4f800000 0x3e116873 0x46ca0800 0x54000000) - (ref.null eq) - ) - ) - ) - (tuple.extract 2 - (tuple.make - (i31.new - (i32.const 1684216173) ) - (ref.null eq) - (v128.const i32x4 0xffa20004 0x00000000 0x2b25ffa6 0x005b0080) - (ref.null eq) - ) - ) - ) - (global.get $global$5) - ) - ) - (func $func_19 (param $0 funcref) (param $1 f64) (result i32) - (local $2 (funcref f32 exnref exnref externref)) - (local $3 externref) - (local $4 v128) - (local $5 f64) - (local $6 i32) - (local $7 (f64 i31ref eqref eqref)) - (local $8 anyref) - (local $9 (eqref i31ref i32 i31ref anyref)) - (local $10 eqref) - (local $11 exnref) - (local $12 externref) - (local $13 externref) - (local $14 externref) - (local $15 i31ref) - (block - (if - (i32.eqz - (global.get $hangLimit) - ) - (return - (i32.const -131072) - ) - ) - (global.set $hangLimit - (i32.sub - (global.get $hangLimit) - (i32.const 1) - ) - ) - ) - (select - (i32.atomic.load8_u offset=22 - (i32.and - (local.get $6) - (i32.const 15) - ) - ) - (local.tee $6 - (select - (i16x8.extract_lane_s 4 - (v128.const i32x4 0xfffffff8 0x00008001 0xffffffa0 0x180b1217) - ) - (if (result i32) - (if (result i32) - (i32.eqz - (i32.const 524287) + (block + (local.set $1 + (tuple.extract 2 + (tuple.make + (f32.const 353703200) + (f32.const 31868) + (ref.null eq) + (v128.const i32x4 0x56565656 0xff7fffff 0x41000000 0x41300000) + (i32.const 135290122) + (i31.new + (i32.const 1396855106) + ) + ) + ) + ) + (br_if $label$10 + (i32.eqz + (i32.const -2147483646) + ) ) - (block $label$1 - (memory.init 0 + (i32.atomic.store offset=22 + (i32.and + (i32.load16_u offset=22 + (i32.and + (i32.const 521404930) + (i32.const 15) + ) + ) + (i32.const 15) + ) + (i32.atomic.load offset=3 (i32.and - (f32.ge - (block $label$2 (result f32) - (call $log-i32 - (call $hashMemory) - ) - (if (result f32) - (if (result i32) - (i32.eqz - (loop $label$3 (result i32) + (atomic.notify offset=4 + (i32.and + (if (result i32) + (i32.eqz + (i32.const 1) + ) + (block $label$11 + (br $label$10) + ) + (block $label$12 (result i32) + (i32.store16 offset=2 + (i32.and + (i32.const 65535) + (i32.const 15) + ) + (select + (i32.const -2147483648) + (i32.const 16) + (i32.const 8) + ) + ) + (i64.le_u + (if (result i64) + (i32.const 1464489052) + (i64.atomic.load8_u offset=2 + (i32.and + (global.get $global$_0) + (i32.const 15) + ) + ) + (i64.const 942112219034230640) + ) + (loop $label$13 (result i64) (block (if (i32.eqz (global.get $hangLimit) ) - (return - (i32.const -83) - ) + (return) ) (global.set $hangLimit (i32.sub @@ -844,170 +704,414 @@ ) ) ) - (block (result i32) - (br_if $label$3 - (local.get $6) + (block (result i64) + (if + (i32.eqz + (i32.const 134217728) + ) + (block $label$14 + (nop) + (f64.store offset=22 align=4 + (i32.and + (loop $label$15 (result i32) + (block + (if + (i32.eqz + (global.get $hangLimit) + ) + (return) + ) + (global.set $hangLimit + (i32.sub + (global.get $hangLimit) + (i32.const 1) + ) + ) + ) + (block (result i32) + (nop) + (br_if $label$15 + (i32.const 101189382) + ) + (i32.const -127) + ) + ) + (i32.const 15) + ) + (select + (f64.const -nan:0xfffffffffffa1) + (f64.const -0.394) + (i32.const -127) + ) + ) + ) + (block $label$16 + (f32.store offset=4 align=1 + (i32.and + (block $label$17 + (nop) + (br $label$16) + ) + (i32.const 15) + ) + (f32.const -2048) + ) + (loop $label$18 + (block + (if + (i32.eqz + (global.get $hangLimit) + ) + (return) + ) + (global.set $hangLimit + (i32.sub + (global.get $hangLimit) + (i32.const 1) + ) + ) + ) + (block + (block $label$19 + (local.set $2 + (ref.null func) + ) + (nop) + ) + (br_if $label$18 + (i32.eqz + (i32.const 1246300445) + ) + ) + (drop + (tuple.make + (ref.null func) + (ref.null extern) + (ref.null any) + (v128.const i32x4 0x6c15621e 0x74813332 0x00f8da01 0x001644ae) + (ref.null exn) + ) + ) + ) + ) + ) ) - (br_if $label$3 - (local.get $6) + (br_if $label$13 + (i32.eqz + (global.get $global$_0) + ) ) - (i32.const -33) + (i64.const -4611686018427387904) ) ) ) - (i32.const 2147483647) - (if (result i32) - (i32.eqz - (i32.const 32768) - ) - (i32.const 16404) - (i32.const -1024) - ) ) - (f32.const -nan:0x7fffa8) - (block $label$4 (result f32) - (call $log-i32 - (call $hashMemory) - ) - (f32.const 18446744073709551615) + ) + (i32.const 15) + ) + (i32.const 1111245614) + ) + (i32.const 15) + ) + ) + ) + ) + ) + (return) + ) + (block $label$20 + (nop) + (i64.shr_u + (loop $label$40 + (block + (if + (i32.eqz + (global.get $hangLimit) + ) + (return) + ) + (global.set $hangLimit + (i32.sub + (global.get $hangLimit) + (i32.const 1) + ) + ) + ) + (block + (block $label$41 + (call $log-i32 + (i32.const -66) + ) + ) + (br_if $label$40 + (i32.eqz + (loop $label$42 (result i32) + (block + (if + (i32.eqz + (global.get $hangLimit) + ) + (return) + ) + (global.set $hangLimit + (i32.sub + (global.get $hangLimit) + (i32.const 1) ) ) ) - (if (result f32) - (i32.eqz - (ref.is_null - (if (result externref) + (block (result i32) + (block $label$43 + (nop) + (drop + (if (result f64) (i32.eqz - (i32.const -65535) + (block $label$44 + (call $log-i32 + (call $hashMemory) + ) + (br $label$40) + ) + ) + (tuple.extract 0 + (tuple.make + (f64.const 18446744073709551615) + (i64.const 4294967222) + (i32.const 847216455) + ) + ) + (block $label$45 + (br $label$40) ) - (ref.null extern) - (local.get $12) ) ) ) - (block $label$5 (result f32) - (call $log-f32 - (select - (f32.const 4096) - (f32.const -nan:0x7fffa1) - (i32.const 2097640319) + (br_if $label$42 + (i32.eqz + (if (result i32) + (loop $label$46 + (block + (if + (i32.eqz + (global.get $hangLimit) + ) + (return) + ) + (global.set $hangLimit + (i32.sub + (global.get $hangLimit) + (i32.const 1) + ) + ) + ) + (block $label$47 + (nop) + (br $label$42) + ) + ) + (i32.const 0) + (i32.const 2376257) ) ) - (f32.const 8192) ) - (f32.const -1125899906842624) + (i8x16.all_true + (v128.const i32x4 0xff7fff7f 0x00ffffff 0xfdff0c03 0xffe5fff7) + ) ) ) - (i32.const 15) ) - (i32.const 1) - (i32.const 2) ) - (return - (local.get $6) + (block $label$48 + (br_if $label$40 + (i32.const -32768) + ) + (return) ) ) - (block $label$6 (result i32) - (loop $label$7 - (block - (if - (i32.eqz - (global.get $hangLimit) - ) - (return - (i32.const -4096) - ) - ) - (global.set $hangLimit - (i32.sub - (global.get $hangLimit) - (i32.const 1) - ) - ) + ) + (loop $label$49 + (block + (if + (i32.eqz + (global.get $hangLimit) ) - (block $label$8 - (nop) - (memory.fill - (i32.and - (i8x16.extract_lane_s 11 - (i8x16.max_s - (local.get $4) - (i16x8.add_saturate_u - (v128.const i32x4 0xffea0f4a 0xfdffffec 0xdfff0512 0x1910ffff) - (local.get $4) - ) - ) - ) - (i32.const 15) + (return) + ) + (global.set $hangLimit + (i32.sub + (global.get $hangLimit) + (i32.const 1) + ) + ) + ) + (block + (block $label$50 + (nop) + (block $label$51 + (drop + (ref.func $func_2) + ) + (br_if $label$51 + (f64.eq + (f64.const 4398046511104) + (global.get $global$_1) ) - (i32.const 1326258715) - (local.get $6) ) ) ) - (local.get $6) - ) - ) - (block $label$9 (result i32) - (i32.const 10353) - ) - (local.get $6) - ) - (select - (i32.const 33554433) - (i32.trunc_f64_s - (f64.const 3402823466385288598117041e14) - ) - (local.tee $6 - (select - (local.get $6) - (ref.eq - (loop $label$0 (result i31ref) - (block - (if - (i32.eqz - (global.get $hangLimit) - ) - (return - (i32.const -32768) + (br_if $label$49 + (i32.eqz + (loop $label$52 (result i32) + (block + (if + (i32.eqz + (global.get $hangLimit) + ) + (return) ) - ) - (global.set $hangLimit - (i32.sub - (global.get $hangLimit) - (i32.const 1) + (global.set $hangLimit + (i32.sub + (global.get $hangLimit) + (i32.const 1) + ) ) ) - ) - (block (result i31ref) - (local.set $5 - (f64.const 371920655) - ) - (br_if $label$0 - (i32.eqz - (local.tee $6 - (local.tee $6 - (local.tee $6 - (local.tee $6 - (local.get $6) + (block (result i32) + (block $label$53 + (if + (i32.eqz + (i31.get_s + (i31.new + (i32.const -93) + ) + ) + ) + (br_if $label$49 + (i32.eqz + (i32.const 353711929) + ) + ) + (block $label$54 + (call $log-i32 + (call $hashMemory) + ) + (if + (i32.eqz + (i32.const 511508602) + ) + (block $label$55 + (br_if $label$53 + (i32.eqz + (i31.get_u + (i31.new + (i32.const -512) + ) + ) + ) + ) + (loop $label$56 + (block + (if + (i32.eqz + (global.get $hangLimit) + ) + (return) + ) + (global.set $hangLimit + (i32.sub + (global.get $hangLimit) + (i32.const 1) + ) + ) + ) + (block + (call $log-i32 + (loop $label$57 (result i32) + (block + (if + (i32.eqz + (global.get $hangLimit) + ) + (return) + ) + (global.set $hangLimit + (i32.sub + (global.get $hangLimit) + (i32.const 1) + ) + ) + ) + (block $label$58 (result i32) + (ref.eq + (i31.new + (i32.const -28) + ) + (local.tee $1 + (local.get $1) + ) + ) + ) + ) + ) + (br_if $label$56 + (i32.eqz + (loop $label$59 (result i32) + (block + (if + (i32.eqz + (global.get $hangLimit) + ) + (return) + ) + (global.set $hangLimit + (i32.sub + (global.get $hangLimit) + (i32.const 1) + ) + ) + ) + (block (result i32) + (br_if $label$54 + (i32.eqz + (i32.const 3) + ) + ) + (br_if $label$59 + (i32.const -32769) + ) + (i32.const 337444946) + ) + ) + ) + ) + (nop) + ) + ) + ) + (block $label$60 + (nop) ) ) ) ) + (nop) + ) + (br_if $label$52 + (i32.eqz + (i32.const 127) + ) ) + (i32.const 524288) ) - (local.get $15) ) ) - (local.get $10) ) - (local.get $6) + (return) ) ) ) ) ) - (global.get $global$4) ) ) (func $hangLimitInitializer diff --git a/test/unit/input/hello_world.wat b/test/unit/input/hello_world.wat new file mode 100644 index 000000000..680ee809a --- /dev/null +++ b/test/unit/input/hello_world.wat @@ -0,0 +1,11 @@ +(module + (type $i32_i32_=>_i32 (func (param i32 i32) (result i32))) + (memory $0 256 256) + (export "add" (func $add)) + (func $add (param $x i32) (param $y i32) (result i32) + (i32.add + (local.get $x) + (local.get $y) + ) + ) +) diff --git a/test/unit/input/random_data.txt b/test/unit/input/random_data.txt new file mode 100644 index 000000000..d5000bfbd --- /dev/null +++ b/test/unit/input/random_data.txt @@ -0,0 +1 @@ +6sgkjdfghk34589n-947-vn98f2yr-nb8f7t08b7gv*~&!%&^@}{PASD kjgsdf768 diff --git a/test/unit/test_initial_fuzz.py b/test/unit/test_initial_fuzz.py new file mode 100644 index 000000000..2ddc29884 --- /dev/null +++ b/test/unit/test_initial_fuzz.py @@ -0,0 +1,35 @@ +import subprocess +from scripts.test import shared +from . import utils + + +class InitialFuzzTest(utils.BinaryenTestCase): + def test_empty_initial(self): + # generate fuzz from random data + data = self.input_path('random_data.txt') + a = shared.run_process(shared.WASM_OPT + ['-ttf', '--print', data], + stdout=subprocess.PIPE).stdout + + # generate fuzz from random data with initial empty wasm + empty_wasm = self.input_path('empty.wasm') + b = shared.run_process( + shared.WASM_OPT + ['-ttf', '--print', data, + '--initial-fuzz=' + empty_wasm], + stdout=subprocess.PIPE).stdout + + # an empty initial wasm causes no changes + self.assertEqual(a, b) + + def test_small_initial(self): + data = self.input_path('random_data.txt') + hello_wat = self.input_path('hello_world.wat') + out = shared.run_process(shared.WASM_OPT + ['-ttf', '--print', data, + '--initial-fuzz=' + hello_wat], + stdout=subprocess.PIPE).stdout + + # the function should be there (perhaps with modified contents - don't + # check that) + self.assertIn('(export "add" (func $add))', out) + + # there should be other fuzz contents added as well + self.assertGreater(out.count('(export '), 1) |