def execute_js(engine): print('(run in %s)' % engine) try: js = shared.timeout_run(Popen(shared.NODE_JS + [filename + '.js'], stdout=PIPE, stderr=PIPE), 15 * 60) except Exception: print('failed to run in primary') return False js = js.split('\n')[0] + '\n' # remove any extra printed stuff (node workarounds) return correct1 == js or correct2 == js
def eval_ctors_wasm(js, wasm_file, num): ctors_start, ctors_end, all_ctors, ctors = find_ctors_data(js, num) cmd = [ os.path.join(binaryen_bin, 'wasm-ctor-eval'), wasm_file, '-o', wasm_file, '--ctors=' + ','.join(ctors) ] cmd += extra_args if debug_info: cmd += ['-g'] logger.debug('wasm ctor cmd: ' + str(cmd)) proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) try: err = shared.timeout_run(proc, timeout=10, full_output=True, check=False) except Exception as e: if 'Timed out' not in str(e): raise logger.debug('ctors timed out\n') return 0, js if proc.returncode != 0: shared.exit_with_error( 'unexpected error while trying to eval ctors:\n' + err) num_successful = err.count('success on') logger.debug(err) if len(ctors) == num_successful: new_ctors = '' else: elements = [] for ctor in all_ctors[num_successful:]: elements.append('{ func: function() { %s() } }' % ctor) new_ctors = '__ATINIT__.push(' + ', '.join(elements) + ');' js = js[:ctors_start] + new_ctors + js[ctors_end:] return num_successful, js
notes['invalid'] += 1 continue shared.run_process([ COMP, '-m32', opts, '-emit-llvm', '-c', fullname, '-o', filename + '.bc' ] + CSMITH_CFLAGS + shared.get_cflags() + ['-w']) shared.run_process( [shared.path_from_root('tools', 'nativize_llvm.py'), filename + '.bc'], stderr=PIPE) shutil.move(filename + '.bc.run', filename + '2') shared.run_process([COMP, fullname, '-o', filename + '3'] + CSMITH_CFLAGS + ['-w']) print('3) Run natively') try: correct1 = shared.timeout_run( Popen([filename + '1'], stdout=PIPE, stderr=PIPE), 3) if 'Segmentation fault' in correct1 or len(correct1) < 10: raise Exception('segfault') correct2 = shared.timeout_run( Popen([filename + '2'], stdout=PIPE, stderr=PIPE), 3) if 'Segmentation fault' in correct2 or len(correct2) < 10: raise Exception('segfault') correct3 = shared.timeout_run( Popen([filename + '3'], stdout=PIPE, stderr=PIPE), 3) if 'Segmentation fault' in correct3 or len(correct3) < 10: raise Exception('segfault') if correct1 != correct3: raise Exception('clang opts change result') except Exception as e: print('Failed or infinite looping in native, skipping', e) notes['invalid'] += 1
def eval_ctors_js(js, mem_init, num): def kill_func(asm, name): asm = asm.replace('function ' + name + '(', 'function KILLED_' + name + '(', 1) return asm def add_func(asm, func): before = len(asm) asm = asm.replace('function ', ' ' + func + '\nfunction ', 1) assert len(asm) > before name = func[func.find(' ') + 1:func.find('(')] asm = asm.replace('return {', 'return { ' + name + ': ' + name + ',') return asm # Find the global ctors ctors_start, ctors_end, all_ctors, ctors = find_ctors_data(js, num) logging.debug('trying to eval ctors: ' + ', '.join(ctors)) # Find the asm module, and receive the mem init. asm = get_asm(js) assert len(asm) asm = asm.replace('use asm', 'not asm') # don't try to validate this # Substitute sbrk with a failing stub: the dynamic heap memory area shouldn't get increased during static ctor initialization. asm = asm.replace( 'function _sbrk(', 'function _sbrk(increment) { throw "no sbrk when evalling ctors!"; } function KILLED_sbrk(', 1) # find all global vars, and provide only safe ones. Also add dumping for those. pre_funcs_start = asm.find(';') + 1 pre_funcs_end = asm.find('function ', pre_funcs_start) pre_funcs_end = asm.rfind(';', pre_funcs_start, pre_funcs_end) + 1 pre_funcs = asm[pre_funcs_start:pre_funcs_end] parts = [ x for x in [x.strip() for x in pre_funcs.split(';')] if x.startswith('var ') ] global_vars = [] new_globals = '\n' for part in parts: part = part[4:] # skip 'var ' bits = [x.strip() for x in part.split(',')] for bit in bits: name, value = [x.strip() for x in bit.split('=', 1)] if value in ['0', '+0', '0.0'] or name in [ 'STACKTOP', 'STACK_MAX', 'DYNAMICTOP_PTR', 'HEAP8', 'HEAP16', 'HEAP32', 'HEAPU8', 'HEAPU16', 'HEAPU32', 'HEAPF32', 'HEAPF64', 'Int8View', 'Int16View', 'Int32View', 'Uint8View', 'Uint16View', 'Uint32View', 'Float32View', 'Float64View', 'nan', 'inf', '_emscripten_memcpy_big', '___dso_handle', '_atexit', '___cxa_atexit', ] or name.startswith('Math_'): if 'new ' not in value: global_vars.append(name) new_globals += ' var ' + name + ' = ' + value + ';\n' asm = asm[:pre_funcs_start] + new_globals + asm[pre_funcs_end:] asm = add_func( asm, 'function dumpGlobals() { return [ ' + ', '.join(global_vars) + '] }') # find static bump. this is the maximum area we'll write to during startup. static_bump_op = 'STATICTOP = STATIC_BASE + ' static_bump_start = js.find(static_bump_op) static_bump_end = js.find(';', static_bump_start) static_bump = int(js[static_bump_start + len(static_bump_op):static_bump_end]) # Generate a safe sandboxed environment. We replace all ffis with errors. Otherwise, # asm.js can't call outside, so we are ok. # if shared.DEBUG: # temp_file = os.path.join(shared.CANONICAL_TEMP_DIR, 'ctorEval.js') # shared.safe_ensure_dirs(shared.CANONICAL_TEMP_DIR) # else: # temp_file = config.get_temp_files().get('.ctorEval.js').name with shared.configuration.get_temp_files().get_file( '.ctorEval.js') as temp_file: open(temp_file, 'w').write(''' var totalMemory = %d; var totalStack = %d; var buffer = new ArrayBuffer(totalMemory); var heap = new Uint8Array(buffer); var heapi32 = new Int32Array(buffer); var memInit = %s; var globalBase = %d; var staticBump = %d; heap.set(memInit, globalBase); var staticTop = globalBase + staticBump; var staticBase = staticTop; var stackTop = staticTop; while (stackTop %% 16 !== 0) stackTop--; var stackBase = stackTop; var stackMax = stackTop + totalStack; if (stackMax >= totalMemory) throw 'not enough room for stack'; var dynamicTopPtr = stackMax; heapi32[dynamicTopPtr >> 2] = stackMax; if (!Math.imul) { Math.imul = Math.imul || function(a, b) { var ah = (a >>> 16) & 0xffff; var al = a & 0xffff; var bh = (b >>> 16) & 0xffff; var bl = b & 0xffff; // the shift by 0 fixes the sign on the high part // the final |0 converts the unsigned value into a signed value return ((al * bl) + (((ah * bl + al * bh) << 16) >>> 0)|0); }; } if (!Math.fround) { var froundBuffer = new Float32Array(1); Math.fround = function(x) { froundBuffer[0] = x; return froundBuffer[0] }; } var atexits = []; // we record and replay atexits var globalArg = { Int8Array: Int8Array, Int16Array: Int16Array, Int32Array: Int32Array, Uint8Array: Uint8Array, Uint16Array: Uint16Array, Uint32Array: Uint32Array, Float32Array: Float32Array, Float64Array: Float64Array, NaN: NaN, Infinity: Infinity, Math: Math, }; var libraryArg = { STACKTOP: stackTop, STACK_MAX: stackMax, DYNAMICTOP_PTR: dynamicTopPtr, ___dso_handle: 0, // used by atexit, value doesn't matter _emscripten_memcpy_big: function(dest, src, num) { heap.set(heap.subarray(src, src+num), dest); return dest; }, _atexit: function(x) { atexits.push([x, 0]); return 0; }, ___cxa_atexit: function(x, y) { atexits.push([x, y]); return 0; }, }; // Instantiate asm %s (globalArg, libraryArg, buffer); // Try to run the constructors var allCtors = %s; var numSuccessful = 0; for (var i = 0; i < allCtors.length; i++) { try { var globalsBefore = asm['dumpGlobals'](); asm[allCtors[i]](); var globalsAfter = asm['dumpGlobals'](); if (JSON.stringify(globalsBefore) !== JSON.stringify(globalsAfter)) { console.warn('globals modified'); break; } if (heapi32[dynamicTopPtr >> 2] !== stackMax) { console.warn('dynamic allocation was performend'); break; } // this one was ok. numSuccessful = i + 1; } catch (e) { console.warn(e.stack); break; } } // Write out new mem init. It might be bigger if we added to the zero section, look for zeros var newSize = globalBase + staticBump; while (newSize > globalBase && heap[newSize-1] == 0) newSize--; console.log(JSON.stringify([numSuccessful, Array.prototype.slice.call(heap.subarray(globalBase, newSize)), atexits])); ''' % (total_memory, total_stack, mem_init, global_base, static_bump, asm, json.dumps(ctors))) def read_and_delete(filename): result = '' try: result = open(filename, 'r').read() finally: try_delete(filename) return result # Execute the sandboxed code. If an error happened due to calling an ffi, that's fine, # us exiting with an error tells the caller that we failed. If it times out, give up. out_file = shared.configuration.get_temp_files().get('.out').name err_file = shared.configuration.get_temp_files().get('.err').name out_file_handle = open(out_file, 'w') err_file_handle = open(err_file, 'w') proc = subprocess.Popen(shared.NODE_JS + [temp_file], stdout=out_file_handle, stderr=err_file_handle, universal_newlines=True) try: shared.timeout_run(proc, timeout=10, full_output=True, check=False) except Exception as e: if 'Timed out' not in str(e): raise logger.debug('ctors timed out\n') return (0, 0, 0, 0) if shared.WINDOWS: time.sleep( 0.5 ) # On Windows, there is some kind of race condition with Popen output stream related functions, where file handles are still in use a short period after the process has finished. out_file_handle.close() err_file_handle.close() out_result = read_and_delete(out_file) err_result = read_and_delete(err_file) if proc.returncode != 0: # TODO(sbc): This should never happen under normal circumstances. # switch to exit_with_error once we fix https://github.com/emscripten-core/emscripten/issues/7463 logger.debug('unexpected error while trying to eval ctors:\n' + out_result + '\n' + err_result) return (0, 0, 0, 0) # out contains the new mem init and other info num_successful, mem_init_raw, atexits = json.loads(out_result) mem_init = bytes(bytearray(mem_init_raw)) total_ctors = len(all_ctors) if num_successful < total_ctors: logger.debug( 'not all ctors could be evalled, something was used that was not safe (and therefore was not defined, and caused an error):\n========\n' + err_result + '========') # Remove the evalled ctors, add a new one for atexits if needed, and write that out if len(ctors) == total_ctors and len(atexits) == 0: new_ctors = '' else: elements = [] if len(atexits): elements.append('{ func: function() { %s } }' % '; '.join([ '_atexit(' + str(x[0]) + ',' + str(x[1]) + ')' for x in atexits ])) for ctor in all_ctors[num:]: elements.append('{ func: function() { %s() } }' % ctor) new_ctors = '__ATINIT__.push(' + ', '.join(elements) + ');' js = js[:ctors_start] + new_ctors + js[ctors_end:] return (num_successful, js, mem_init, ctors)
def run_js(filename, engine=None, args=[], check_timeout=False, stdin=None, stdout=PIPE, stderr=None, cwd=None, full_output=False, assert_returncode=0, skip_check=False): if not engine: engine = shared.JS_ENGINES[0] """Execute javascript code generated by tests, with possible timeout.""" if not os.path.exists(filename): raise Exception('JavaScript file not found: ' + filename) # # code to serialize out the test suite files # # XXX make sure to disable memory init files, and clear out the base_dir. you may also need to manually grab e.g. paper.pdf.js from a run of test_poppler # import shutil, json # base_dir = '/tmp/emscripten_suite' # if not os.path.exists(base_dir): # os.makedirs(base_dir) # commands_file = os.path.join(base_dir, 'commands.txt') # commands = '' # if os.path.exists(commands_file): # commands = open(commands_file).read() # i = 0 # while True: # curr = os.path.join(base_dir, str(i) + '.js') # if not os.path.exists(curr): break # i += 1 # shutil.copyfile(filename, curr) # commands += os.path.basename(curr) + ',' + json.dumps(args) + '\n' # open(commands_file, 'w').write(commands) command = make_command(filename, engine, args) try: proc = Popen(command, stdin=stdin, stdout=stdout, stderr=stderr, cwd=cwd, universal_newlines=True) except Exception: # the failure may be because the engine is not present. show the proper # error in that case if not skip_check: require_engine(engine) # if we got here, then require_engine succeeded, so we can raise the original error raise timeout = 15 * 60 if check_timeout else None if shared.TRACK_PROCESS_SPAWNS: logging.info('Blocking on process ' + str(proc.pid) + ': ' + str(command) + (' for ' + str(timeout) + ' seconds' if timeout else ' until it finishes.')) try: ret = shared.timeout_run(proc, timeout, 'Execution', full_output=full_output, throw_on_failure=False) except Exception: # the failure may be because the engine does not work. show the proper # error in that case if not skip_check: require_engine(engine) # if we got here, then require_engine succeeded, so we can raise the original error raise if assert_returncode is not None and proc.returncode is not assert_returncode: raise CalledProcessError(proc.returncode, ' '.join(command), str(ret)) return ret