// Shell integration. if (typeof console === 'undefined') { console = { log: print }; } var tempRet0; var binary; if (typeof process === 'object' && typeof require === 'function' /* node.js detection */) { var args = process.argv.slice(2); binary = require('fs').readFileSync(args[0]); if (!binary.buffer) binary = new Uint8Array(binary); } else { var args; if (typeof scriptArgs != 'undefined') { args = scriptArgs; } else if (typeof arguments != 'undefined') { args = arguments; } if (typeof readbuffer === 'function') { binary = new Uint8Array(readbuffer(args[0])); } else { binary = read(args[0], 'binary'); } } // Utilities. function assert(x, y) { if (!x) throw (y || 'assertion failed');// + new Error().stack; } // Deterministic randomness. var detrand = (function() { var hash = 5381; // TODO DET_RAND_SEED; var x = 0; return function() { hash = (((hash << 5) + hash) ^ (x & 0xff)) >>> 0; x = (x + 1) % 256; return (hash % 256) / 256; }; })(); // Asyncify integration. var Asyncify = { sleeping: false, sleepingFunction: null, sleeps: 0, maxDepth: 0, DATA_ADDR: 4, // The fuzzer emits memories of size 16 (pages). Allow us to use almost all of // that (we start from offset 4, so we can't use them all). DATA_MAX: 15 * 65536, savedMemory: null, instrumentImports: function(imports) { var ret = {}; for (var module in imports) { ret[module] = {}; for (var i in imports[module]) { if (typeof imports[module][i] === 'function') { (function(module, i) { ret[module][i] = function() { refreshView(); if (!Asyncify.sleeping) { // Sleep if asyncify support is present (which also requires // that the memory be exported), and at a certain probability. if (exports.asyncify_start_unwind && view && detrand() < 0.5) { // We are called in order to start a sleep/unwind. console.log('asyncify: sleep in ' + i + '...'); Asyncify.sleepingFunction = i; Asyncify.sleeps++; var depth = new Error().stack.split('\n').length - 6; Asyncify.maxDepth = Math.max(Asyncify.maxDepth, depth); // Save the memory we use for data, so after we restore it later, the // sleep/resume appears to have had no change to memory. Asyncify.savedMemory = new Int32Array(view.subarray(Asyncify.DATA_ADDR >> 2, Asyncify.DATA_MAX >> 2)); // Unwinding. // Fill in the data structure. The first value has the stack location, // which for simplicity we can start right after the data structure itself. view[Asyncify.DATA_ADDR >> 2] = Asyncify.DATA_ADDR + 8; // The end of the stack will not be reached here anyhow. view[Asyncify.DATA_ADDR + 4 >> 2] = Asyncify.DATA_MAX; exports.asyncify_start_unwind(Asyncify.DATA_ADDR); Asyncify.sleeping = true; } else { // Don't sleep, normal execution. return imports[module][i].apply(null, arguments); } } else { // We are called as part of a resume/rewind. Stop sleeping. console.log('asyncify: resume in ' + i + '...'); assert(Asyncify.sleepingFunction === i); exports.asyncify_stop_rewind(); // The stack should have been all used up, and so returned to the original state. assert(view[Asyncify.DATA_ADDR >> 2] == Asyncify.DATA_ADDR + 8); assert(view[Asyncify.DATA_ADDR + 4 >> 2] == Asyncify.DATA_MAX); Asyncify.sleeping = false; // Restore the memory to the state from before we slept. view.set(Asyncify.savedMemory, Asyncify.DATA_ADDR >> 2); return imports[module][i].apply(null, arguments); } }; })(module, i); } else { ret[module][i] = imports[module][i]; } } } // Add ignored.print, which is ignored by asyncify, and allows debugging of asyncified code. ret['ignored'] = { 'print': function(x, y) { console.log(x, y) } }; return ret; }, instrumentExports: function(exports) { var ret = {}; for (var e in exports) { if (typeof exports[e] === 'function' && !e.startsWith('asyncify_')) { (function(e) { ret[e] = function() { while (1) { var ret = exports[e].apply(null, arguments); // If we are sleeping, then the stack was unwound; rewind it. if (Asyncify.sleeping) { console.log('asyncify: stop unwind; rewind'); assert(!ret, 'results during sleep are meaningless, just 0'); //console.log('asyncify: after unwind', view[Asyncify.DATA_ADDR >> 2], view[Asyncify.DATA_ADDR + 4 >> 2]); try { exports.asyncify_stop_unwind(); exports.asyncify_start_rewind(Asyncify.DATA_ADDR); } catch (e) { console.log('error in unwind/rewind switch', e); } continue; } return ret; } }; })(e); } else { ret[e] = exports[e]; } } return ret; }, check: function() { assert(!Asyncify.sleeping); }, finish: function() { if (Asyncify.sleeps > 0) { print('asyncify:', 'sleeps:', Asyncify.sleeps, 'max depth:', Asyncify.maxDepth); } }, }; // Fuzz integration. function logValue(x, y) { if (typeof y !== 'undefined') { console.log('[LoggingExternalInterface logging ' + x + ' ' + y + ']'); } else { console.log('[LoggingExternalInterface logging ' + x + ']'); } } // Set up the imports. var imports = { 'fuzzing-support': { 'log-i32': logValue, 'log-i64': logValue, 'log-f32': logValue, 'log-f64': logValue, }, 'env': { 'setTempRet0': function(x) { tempRet0 = x }, 'getTempRet0': function() { return tempRet0 }, }, }; imports = Asyncify.instrumentImports(imports); // Create the wasm. var module = new WebAssembly.Module(binary); var instance; try { instance = new WebAssembly.Instance(module, imports); } catch (e) { console.log('exception: failed to instantiate module'); quit(); } // Handle the exports. var exports = instance.exports; exports = Asyncify.instrumentExports(exports); var view; // Recreate the view. This is important both initially and after a growth. function refreshView() { if (exports.memory) { view = new Int32Array(exports.memory.buffer); } } // Run the wasm. var sortedExports = []; for (var e in exports) { sortedExports.push(e); } sortedExports.sort(); sortedExports = sortedExports.filter(function(e) { // Filter special intrinsic functions. return !e.startsWith('asyncify_'); }); sortedExports.forEach(function(e) { Asyncify.check(); if (typeof exports[e] !== 'function') return; try { console.log('[fuzz-exec] calling ' + e); var result = exports[e](); if (typeof result !== 'undefined') { console.log('[fuzz-exec] note result: $' + e + ' => ' + result); } } catch (e) { console.log('exception!');// + [e, e.stack]); } }); // Finish up Asyncify.finish();