def __init__(self, options, runthis, logPrefix, inCompareJIT): pathToBinary = runthis[0] # This relies on the shell being a local one from compileShell.py: # Ignore trailing ".exe" in Win, also abspath makes it work w/relative paths like './js' pc = ProgramConfiguration.fromBinary( os.path.abspath(pathToBinary).split('.')[0]) pc.addProgramArguments(runthis[1:-1]) if options.valgrind: runthis = (inspectShell.constructVgCmdList( errorCode=VALGRIND_ERROR_EXIT_CODE) + valgrindSuppressions(options.knownPath) + runthis) preexec_fn = ulimitSet if os.name == 'posix' else None runinfo = timed_run.timed_run(runthis, options.timeout, logPrefix, preexec_fn=preexec_fn) lev = JS_FINE issues = [] auxCrashData = [] # FuzzManager expects a list of strings rather than an iterable, so bite the # bullet and 'readlines' everything into memory. with open(logPrefix + "-out.txt") as f: out = f.readlines() with open(logPrefix + "-err.txt") as f: err = f.readlines() if options.valgrind and runinfo.return_code == VALGRIND_ERROR_EXIT_CODE: issues.append("valgrind reported an error") lev = max(lev, JS_VG_AMISS) valgrindErrorPrefix = "==" + str(runinfo.pid) + "==" for line in err: if valgrindErrorPrefix and line.startswith( valgrindErrorPrefix): issues.append(line.rstrip()) elif runinfo.sta == timed_run.CRASHED: if sps.grabCrashLog(runthis[0], runinfo.pid, logPrefix, True): with open(logPrefix + "-crash.txt") as f: auxCrashData = [line.strip() for line in f.readlines()] elif detect_malloc_errors.amiss(logPrefix): issues.append("malloc error") lev = max(lev, JS_NEW_ASSERT_OR_CRASH) elif runinfo.return_code == 0 and not inCompareJIT: # We might have(??) run jsfunfuzz directly, so check for special kinds of bugs for line in out: if line.startswith("Found a bug: ") and not ("NestTest" in line and oomed(err)): lev = JS_DECIDED_TO_EXIT issues.append(line.rstrip()) if options.shellIsDeterministic and not understoodJsfunfuzzExit( out, err) and not oomed(err): issues.append("jsfunfuzz didn't finish") lev = JS_DID_NOT_FINISH # Copy non-crash issues to where FuzzManager's "AssertionHelper.py" can see it. if lev != JS_FINE: for issue in issues: err.append("[Non-crash bug] " + issue) # Finally, make a CrashInfo object and parse stack traces for asan/crash/assertion bugs crashInfo = CrashInfo.CrashInfo.fromRawCrashData( out, err, pc, auxCrashData=auxCrashData) createCollector.printCrashInfo(crashInfo) # We only care about crashes and assertion failures on shells with no symbols # Note that looking out for the Assertion failure message is highly SpiderMonkey-specific if not isinstance(crashInfo, CrashInfo.NoCrashInfo) or \ 'Assertion failure: ' in str(crashInfo.rawStderr) or \ 'Segmentation fault' in str(crashInfo.rawStderr) or \ 'Bus error' in str(crashInfo.rawStderr): lev = max(lev, JS_NEW_ASSERT_OR_CRASH) match = options.collector.search(crashInfo) if match[0] is not None: createCollector.printMatchingSignature(match) lev = JS_FINE print("%s | %s" % (logPrefix, summaryString(issues, lev, runinfo.elapsedtime))) if lev != JS_FINE: fileManipulation.writeLinesToFile([ 'Number: ' + logPrefix + '\n', 'Command: ' + sps.shellify(runthis) + '\n' ] + ['Status: ' + i + "\n" for i in issues], logPrefix + '-summary.txt') self.lev = lev self.out = out self.err = err self.issues = issues self.crashInfo = crashInfo self.match = match self.runinfo = runinfo self.return_code = runinfo.return_code
def __init__(self, cfg, url, logPrefix, extraPrefs="", quiet=False, leaveProfile=False): """Run Firefox once, detect bugs, and determine a 'level' based on the most severe unknown bug.""" profileDir = mkdtemp(prefix="domfuzz-rdf-profile") createDOMFuzzProfile(profileDir) writePrefs(profileDir, extraPrefs) runBrowserArgs = [cfg.dirs.reftestScriptDir, cfg.dirs.utilityDir, profileDir] assert logPrefix # :( leakLogFile = logPrefix + "-leaks.txt" runbrowserpy = [sys.executable, "-u", os.path.join(THIS_SCRIPT_DIRECTORY, "runbrowser.py")] runbrowser = subprocess.Popen( runbrowserpy + ["--leak-log-file=" + leakLogFile] + cfg.runBrowserOptions + runBrowserArgs + [url], stdin=None, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, # Hmm, CrashInfo.fromRawCrashData expects them separate, but I like them together... env=cfg.env, close_fds=close_fds ) alh = AmissLogHandler(cfg.knownPath, cfg.options.valgrind) # Bug 718208 if extraPrefs.find("inflation") != -1: alh.expectedToRenderInconsistently = True statusLinePrefix = "RUNBROWSER INFO | runbrowser.py | runApp: exited with status " status = -9000 # NB: not using 'for line in runbrowser.stdout' because that uses a hidden buffer # see http://docs.python.org/library/stdtypes.html#file.next while True: line = runbrowser.stdout.readline() if line != '': line = alh.processLine(line) if not quiet: print line, if line.startswith(statusLinePrefix): status = int(line[len(statusLinePrefix):]) else: break lev = DOM_FINE if alh.mallocFailure: lev = max(lev, DOM_NEW_ASSERT_OR_CRASH) if alh.fuzzerComplained or alh.sawChromeFailure: lev = max(lev, DOM_FUZZER_COMPLAINED) if alh.sawValgrindComplaint: lev = max(lev, DOM_VG_AMISS) if alh.sawNewNonfatalAssertion: lev = max(lev, DOM_NEW_ASSERT_OR_CRASH) # Leak stuff if 'user_pref("layers.use-deprecated-textures", true);' in extraPrefs: # Bug 933569 # Doing the change *here* only works because this is a small leak that shouldn't affect the reads in alh alh.expectedToLeak = True if 'user_pref("gfx.downloadable_fonts.disable_cache", true);' in extraPrefs: # Bug 1258031 # This is a large leak. I don't remember what the comment above is about. alh.expectedToLeak = True if os.path.exists(leakLogFile) and status == 0 and detect_leaks.amiss(cfg.knownPath, leakLogFile, verbose=not quiet) and not alh.expectedToLeak: alh.printAndLog(DOMI_MARKER + "Leak (trace-refcnt)") alh.printAndLog("Leak details: " + os.path.basename(leakLogFile)) lev = max(lev, DOM_UNEXPECTED_LEAK) else: if alh.sawOMGLEAK and not alh.expectedToLeak: lev = max(lev, DOM_UNEXPECTED_LEAK) if leakLogFile: # Remove the main leak log file, plus any plugin-process leak log files for f in glob.glob(leakLogFile + "*"): os.remove(f) # Do various stuff based on how the process exited if alh.timedOut: if alh.expectedToHang or cfg.options.valgrind: alh.printAndLog("%%% An expected hang") else: alh.printAndLog(DOMI_MARKER + "Unexpected hang") lev = max(lev, DOM_UNEXPECTED_HANG) elif status < 0 and os.name == 'posix': signum = -status signame = getSignalName(signum, "unknown signal") print "DOMFUZZ INFO | domInteresting.py | Terminated by signal " + str(signum) + " (" + signame + ")" elif status == 1: alh.printAndLog("%%% Exited with status 1 (crash?)") elif status == -2147483645 and sps.isWin: alh.printAndLog("%%% Exited with status -2147483645 (plugin issue, bug 867263?)") elif status != 0: alh.printAndLog(DOMI_MARKER + "Abnormal exit (status %d)" % status) lev = max(lev, DOM_ABNORMAL_EXIT) # Always look for crash information in stderr. linesWithoutLineBreaks = [s.rstrip() for s in alh.fullLog] linesWithoutLineBreaks = fileManipulation.truncateMid(linesWithoutLineBreaks, 5000, ["..."]) crashInfo = CrashInfo.CrashInfo.fromRawCrashData([], linesWithoutLineBreaks, cfg.pc) # If the program crashed but we didn't find crash info in stderr (breakpad/asan), # poll for a core file (to feed to gdb) or log from the Mac crash reporter. if isinstance(crashInfo, CrashInfo.NoCrashInfo) and status < 0 and os.name == 'posix': signum = -status if signum != signal.SIGKILL and signum != signal.SIGTERM: wantStack = True assert alh.theapp crashLog = sps.grabCrashLog(alh.theapp, alh.pid, logPrefix, wantStack) if crashLog: with open(crashLog) as f: auxCrashData = f.readlines() crashInfo = CrashInfo.CrashInfo.fromRawCrashData([], linesWithoutLineBreaks, cfg.pc, auxCrashData=auxCrashData) else: alh.printAndLog(DOMI_MARKER + "The browser crashed, but did not leave behind any crash information!") lev = max(lev, DOM_NEW_ASSERT_OR_CRASH) createCollector.printCrashInfo(crashInfo) if not isinstance(crashInfo, CrashInfo.NoCrashInfo): lev = max(lev, DOM_NEW_ASSERT_OR_CRASH) match = cfg.collector.search(crashInfo) if match[0] is not None: createCollector.printMatchingSignature(match) lev = DOM_FINE if lev > DOM_FINE: with open(logPrefix + "-output.txt", "w") as outlog: outlog.writelines(alh.fullLog) subprocess.call(["gzip", logPrefix + "-output.txt"]) with open(logPrefix + "-summary.txt", "w") as summaryLogFile: summaryLogFile.writelines(alh.summaryLog) if lev == DOM_FINE: removeIfExists(logPrefix + "-core.gz") removeIfExists(logPrefix + "-crash.txt") if not leaveProfile: shutil.rmtree(profileDir) print "DOMFUZZ INFO | domInteresting.py | " + str(lev) self.level = lev self.lines = alh.fullLog self.crashInfo = crashInfo
def __init__(self, options, runthis, logPrefix, inCompareJIT): pathToBinary = runthis[0] # This relies on the shell being a local one from compileShell.py: pc = ProgramConfiguration.fromBinary(pathToBinary.split('.')[0]) # Ignore trailing ".exe" in Win pc.addProgramArguments(runthis[1:-1]) if options.valgrind: runthis = ( inspectShell.constructVgCmdList(errorCode=VALGRIND_ERROR_EXIT_CODE) + valgrindSuppressions(options.knownPath) + runthis) preexec_fn = ulimitSet if os.name == 'posix' else None runinfo = timedRun.timed_run(runthis, options.timeout, logPrefix, preexec_fn=preexec_fn) lev = JS_FINE issues = [] auxCrashData = [] # FuzzManager expects a list of strings rather than an iterable, so bite the # bullet and 'readlines' everything into memory. with open(logPrefix + "-out.txt") as f: out = f.readlines() with open(logPrefix + "-err.txt") as f: err = f.readlines() if options.valgrind and runinfo.rc == VALGRIND_ERROR_EXIT_CODE: issues.append("valgrind reported an error") lev = max(lev, JS_VG_AMISS) valgrindErrorPrefix = "==" + str(runinfo.pid) + "==" for line in err: if valgrindErrorPrefix and line.startswith(valgrindErrorPrefix): issues.append(line.rstrip()) elif runinfo.sta == timedRun.CRASHED: if sps.grabCrashLog(runthis[0], runinfo.pid, logPrefix, True): with open(logPrefix + "-crash.txt") as f: auxCrashData = [line.strip() for line in f.readlines()] elif detect_malloc_errors.amiss(logPrefix): issues.append("malloc error") lev = max(lev, JS_NEW_ASSERT_OR_CRASH) elif runinfo.rc == 0 and not inCompareJIT: # We might have(??) run jsfunfuzz directly, so check for special kinds of bugs for line in out: if line.startswith("Found a bug: ") and not ("NestTest" in line and oomed(err)): lev = JS_DECIDED_TO_EXIT issues.append(line.rstrip()) if options.shellIsDeterministic and not understoodJsfunfuzzExit(out, err) and not oomed(err): issues.append("jsfunfuzz didn't finish") lev = JS_DID_NOT_FINISH # Copy non-crash issues to where FuzzManager's "AssertionHelper.py" can see it. if lev != JS_FINE: for issue in issues: err.append("[Non-crash bug] " + issue) # Finally, make a CrashInfo object and parse stack traces for asan/crash/assertion bugs crashInfo = CrashInfo.CrashInfo.fromRawCrashData(out, err, pc, auxCrashData=auxCrashData) createCollector.printCrashInfo(crashInfo) if not isinstance(crashInfo, CrashInfo.NoCrashInfo): lev = max(lev, JS_NEW_ASSERT_OR_CRASH) match = options.collector.search(crashInfo) if match[0] is not None: createCollector.printMatchingSignature(match) lev = JS_FINE print logPrefix + " | " + summaryString(issues, lev, runinfo.elapsedtime) if lev != JS_FINE: fileManipulation.writeLinesToFile( ['Number: ' + logPrefix + '\n', 'Command: ' + sps.shellify(runthis) + '\n'] + ['Status: ' + i + "\n" for i in issues], logPrefix + '-summary.txt') self.lev = lev self.out = out self.err = err self.issues = issues self.crashInfo = crashInfo self.match = match self.runinfo = runinfo self.rc = runinfo.rc