Example #1
0
 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
Example #2
0
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
Example #3
0
        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
Example #4
0
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)
Example #5
0
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