def test_SignatureMatchAssertionSlashes(): # test that a forward slash assertion signature matches a backwards slash crash, but only on windows cfg_linux = ProgramConfiguration('test', 'x86-64', 'linux') cfg_windows = ProgramConfiguration('test', 'x86-64', 'windows') fs_lines = testAssertionPathFwSlashes.splitlines() bs_lines = testAssertionPathBwSlashes.splitlines() # native paths on linux use forward slash fs_linux = CrashInfo.fromRawCrashData([], [], cfg_linux, auxCrashData=fs_lines) # backward slash path on linux -- this is invalid and should never happen bs_linux = CrashInfo.fromRawCrashData([], [], cfg_linux, auxCrashData=bs_lines) # forward slashes on windows are valid, and this does happen fs_windows = CrashInfo.fromRawCrashData([], [], cfg_windows, auxCrashData=fs_lines) # native paths on windows use backslash bs_windows = CrashInfo.fromRawCrashData([], [], cfg_windows, auxCrashData=bs_lines) # test that signature generated from linux assertion matches both linux_sig = fs_linux.createCrashSignature() assert linux_sig.matches(fs_linux) assert not linux_sig.matches(bs_linux) # this is invalid and should not match assert linux_sig.matches(fs_windows) assert linux_sig.matches(bs_windows) # test that signature generated from windows assertion matches both windows_sig = bs_windows.createCrashSignature() assert windows_sig.matches(fs_linux) assert not windows_sig.matches(bs_linux) # this is invalid and should not match assert windows_sig.matches(fs_windows) assert windows_sig.matches(bs_windows)
def runTest(self): config = ProgramConfiguration("test", "x86-64", "linux") crashInfo = CrashInfo.fromRawCrashData( [], [], config, auxCrashData=tsanSimpleRaceReport.splitlines()) testSignature = crashInfo.createCrashSignature() self.assertTrue(testSignature.matches(crashInfo)) outputSymptoms = [] for symptom in testSignature.symptoms: if isinstance(symptom, OutputSymptom): self.assertEqual(symptom.src, "crashdata") outputSymptoms.append(symptom) self.assertEqual(len(outputSymptoms), 3) for stringMatchVal in [ "WARNING: ThreadSanitizer: data race", "Write of size 4 at 0x[0-9a-fA-F]+ by thread T1:", "Previous read of size 4 at 0x[0-9a-fA-F]+ by main thread:" ]: found = False for symptom in outputSymptoms: if symptom.output.value == stringMatchVal: found = True self.assertTrue(found, msg="Couldn't find OutputSymptom with value '%s'" % stringMatchVal)
def runTest(self): config = ProgramConfiguration("test", "x86-64", "linux") crashSignature1 = '{ "symptoms" : [ { "type" : "stackSize", "size" : 8 } ] }' crashSignature1Neg = '{ "symptoms" : [ { "type" : "stackSize", "size" : 9 } ] }' crashSignature2 = '{ "symptoms" : [ { "type" : "stackSize", "size" : "< 10" } ] }' crashSignature2Neg = '{ "symptoms" : [ { "type" : "stackSize", "size" : "> 10" } ] }' stackSizeSig1 = CrashSignature(crashSignature1) stackSizeSig1Neg = CrashSignature(crashSignature1Neg) stackSizeSig2 = CrashSignature(crashSignature2) stackSizeSig2Neg = CrashSignature(crashSignature2Neg) crashInfo1 = CrashInfo.fromRawCrashData( [], [], config, auxCrashData=gdbSampleTrace1.splitlines()) self.assertIsInstance(crashInfo1, GDBCrashInfo) self.assert_(stackSizeSig1.matches(crashInfo1)) self.assertFalse(stackSizeSig1Neg.matches(crashInfo1)) self.assert_(stackSizeSig2.matches(crashInfo1)) self.assertFalse(stackSizeSig2Neg.matches(crashInfo1))
def runTest(self): config = ProgramConfiguration("test", "x86-64", "linux") crashSignature1 = '{ "symptoms" : [ { "type" : "stackFrame", "functionName" : "internalAppend" } ] }' crashSignature1Neg = '{ "symptoms" : [ { "type" : "stackFrame", "functionName" : "foobar" } ] }' crashSignature2 = '{ "symptoms" : [ { "type" : "stackFrame", "functionName" : "js::ion::MBasicBlock::setBackedge", "frameNumber" : "<= 4" } ] }' crashSignature2Neg = '{ "symptoms" : [ { "type" : "stackFrame", "functionName" : "js::ion::MBasicBlock::setBackedge", "frameNumber" : "> 4" } ] }' stackFrameSig1 = CrashSignature(crashSignature1) stackFrameSig1Neg = CrashSignature(crashSignature1Neg) stackFrameSig2 = CrashSignature(crashSignature2) stackFrameSig2Neg = CrashSignature(crashSignature2Neg) crashInfo1 = CrashInfo.fromRawCrashData( [], [], config, auxCrashData=gdbSampleTrace1.splitlines()) self.assertIsInstance(crashInfo1, GDBCrashInfo) self.assert_(stackFrameSig1.matches(crashInfo1)) self.assertFalse(stackFrameSig1Neg.matches(crashInfo1)) self.assert_(stackFrameSig2.matches(crashInfo1)) self.assertFalse(stackFrameSig2Neg.matches(crashInfo1))
def runTest(self): config = ProgramConfiguration("test", "x86", "linux") crashInfo1 = GDBCrashInfo([], gdbRegressionTrace1.splitlines(), config) self.assertEqual(crashInfo1.backtrace[0], "js::ScriptedIndirectProxyHandler::defineProperty") self.assertEqual(crashInfo1.backtrace[1], "js::SetPropertyIgnoringNamedGetter")
def test_SignatureTestCaseMatchTest(): config = ProgramConfiguration("test", "x86", "linux") crashInfo = CrashInfo.fromRawCrashData( [], [], config, auxCrashData=testTrace1.splitlines()) testSig3 = CrashSignature(testSignature3) testSig4 = CrashSignature(testSignature4) testSig5 = CrashSignature(testSignature5) testSig6 = CrashSignature(testSignature6) assert not testSig3.matchRequiresTest() assert testSig4.matchRequiresTest() assert testSig5.matchRequiresTest() # Must not match without testcase provided assert not testSig4.matches(crashInfo) assert not testSig5.matches(crashInfo) assert not testSig6.matches(crashInfo) # Attach testcase crashInfo.testcase = testCase1 # Must match with testcase provided assert testSig4.matches(crashInfo) assert testSig5.matches(crashInfo) # This one does not match at all assert not testSig6.matches(crashInfo)
def test_SignatureCreateTest(): config = ProgramConfiguration("test", "x86", "linux") crashInfo = CrashInfo.fromRawCrashData( [], [], config, auxCrashData=testTrace1.splitlines()) crashSig1 = crashInfo.createCrashSignature(forceCrashAddress=True, maxFrames=4, minimumSupportedVersion=10) crashSig2 = crashInfo.createCrashSignature(forceCrashAddress=False, maxFrames=3, minimumSupportedVersion=10) crashSig3 = crashInfo.createCrashSignature(forceCrashInstruction=True, maxFrames=2, minimumSupportedVersion=10) # Check that all generated signatures match their originating crashInfo assert crashSig1.matches(crashInfo) assert crashSig2.matches(crashInfo) assert crashSig3.matches(crashInfo) # Check that the generated signatures look as expected assert json.loads(str(crashSig1)) == json.loads(testSignature1) assert json.loads(str(crashSig2)) == json.loads(testSignature2) # The third crashInfo misses 2 frames from the top 4 frames, so it will # also include the crash address, even though we did not request it. assert json.loads(str(crashSig3)) == json.loads(testSignature3)
def test_SignatureGenerationTSanRaceTest(): config = ProgramConfiguration("test", "x86-64", "linux") with open(os.path.join(CWD, 'resources', 'tsan-simple-race-report.txt'), 'r') as f: crashInfo = CrashInfo.fromRawCrashData( [], [], config, auxCrashData=f.read().splitlines()) testSignature = crashInfo.createCrashSignature() assert testSignature.matches(crashInfo) outputSymptoms = [] for symptom in testSignature.symptoms: if isinstance(symptom, OutputSymptom): assert symptom.src == "crashdata" outputSymptoms.append(symptom) assert len(outputSymptoms) == 3 for stringMatchVal in [ "WARNING: ThreadSanitizer: data race", "Write of size 4 at 0x[0-9a-fA-F]+ by thread T1:", "Previous read of size 4 at 0x[0-9a-fA-F]+ by main thread:" ]: found = False for symptom in outputSymptoms: if symptom.output.value == stringMatchVal: found = True assert found, "Couldn't find OutputSymptom with value '%s'" % stringMatchVal
def test_SignatureMatchWithUnicode(): config = ProgramConfiguration('test', 'x86-64', 'linux') crashInfo = CrashInfo.fromRawCrashData( ["(«f => (generator.throw(f))», «undefined»)"], [], config) testSignature = CrashSignature( '{"symptoms": [{"src": "stdout", "type": "output", "value": "x"}]}') assert not testSignature.matches(crashInfo)
def test_SignatureGenerationTSanRaceTestAtomic(): config = ProgramConfiguration("test", "x86-64", "linux") for fn in ['tsan-report-atomic.txt', 'tsan-report-atomic-swapped.txt']: with open(os.path.join(CWD, 'resources', fn), 'r') as f: crashInfo = CrashInfo.fromRawCrashData([], [], config, auxCrashData=f.read().splitlines()) assert(crashInfo.backtrace[0] == "pthread_mutex_destroy") assert(crashInfo.createShortSignature() == "ThreadSanitizer: data race [@ pthread_mutex_destroy] vs. [@ pthread_mutex_unlock]") testSignature = crashInfo.createCrashSignature() assert testSignature.matches(crashInfo) outputSymptoms = [] for symptom in testSignature.symptoms: if isinstance(symptom, OutputSymptom): assert symptom.src == "crashdata" outputSymptoms.append(symptom) assert len(outputSymptoms) == 3 for stringMatchVal in [ "WARNING: ThreadSanitizer: data race", "(Previous )?[Aa]tomic [Rr]ead of size 1 at 0x[0-9a-fA-F]+ by thread T[0-9]+( .+mutexes: .+)?:", "(Previous )?[Ww]rite of size 1 at 0x[0-9a-fA-F]+ by main thread( .+mutexes: .+)?:" ]: found = False for symptom in outputSymptoms: if symptom.output.value == stringMatchVal: found = True assert found, "Couldn't find OutputSymptom with value '%s'" % stringMatchVal
def getCrashInfo(self, attachTestcase=False, requiredOutputSources=("stdout", "stderr", "crashdata")): # TODO: This should be cached at some level # TODO: Need to include environment and program arguments here configuration = ProgramConfiguration(self.product.name, self.platform.name, self.os.name, self.product.version) cachedCrashInfo = None if self.cachedCrashInfo: cachedCrashInfo = json.loads(self.cachedCrashInfo) # We can skip loading raw output fields from the database iff # 1) we know we don't need them for matching *and* # 2) we already have the crash data cached (rawStdout, rawStderr, rawCrashData) = (None, None, None) if cachedCrashInfo is None or "stdout" in requiredOutputSources: rawStdout = self.rawStdout if cachedCrashInfo is None or "stderr" in requiredOutputSources: rawStderr = self.rawStderr if cachedCrashInfo is None or "crashdata" in requiredOutputSources: rawCrashData = self.rawCrashData crashInfo = CrashInfo.fromRawCrashData(rawStdout, rawStderr, configuration, rawCrashData, cacheObject=cachedCrashInfo) if attachTestcase and self.testcase is not None and not self.testcase.isBinary: self.testcase.loadTest() crashInfo.testcase = self.testcase.content return crashInfo
def runTest(self): config = ProgramConfiguration("test", "x86", "linux") with open('minidump-example.txt', 'r') as f: crashData = f.read().splitlines() crashInfo = CrashInfo.fromRawCrashData([], [], config, crashData) self.assertEqual(crashInfo.crashAddress, long(0x3e800006acb))
def runTest(self): config = ProgramConfiguration("test", "x86", "linux") crashInfo1 = CrashInfo.fromRawCrashData([], [], config, auxCrashData=asanTraceCrash.splitlines()) crashInfo2 = CrashInfo.fromRawCrashData([], asanTraceUAF.splitlines(), config) self.assertIsInstance(crashInfo1, ASanCrashInfo) self.assertIsInstance(crashInfo2, ASanCrashInfo)
def test_SignatureAsanStackOverflowTest(): config = ProgramConfiguration("test", "x86-64", "linux") crashInfoPos = CrashInfo.fromRawCrashData([], [], config, auxCrashData=testAsanStackOverflow.splitlines()) testSig = crashInfoPos.createCrashSignature() # Check matches appropriately assert testSig.matches(crashInfoPos)
def runTest(self): config = ProgramConfiguration("test", "x86-64", "macosx") with open('apple-crash-report-example.txt', 'r') as f: crashData = f.read().splitlines() crashInfo = CrashInfo.fromRawCrashData([], [], config, crashData) self.assertEqual(crashInfo.crashAddress, long(0x00007fff5f3fff98))
def test_SignatureAsanFailedAllocTest(): config = ProgramConfiguration("test", "x86-64", "linux") crashInfoPos = CrashInfo.fromRawCrashData([], [], config, auxCrashData=testAsanFailedAlloc.splitlines()) testSig = crashInfoPos.createCrashSignature() assert "/AddressSanitizer failed to allocate" in str(testSig) assert testSig.matches(crashInfoPos) assert isinstance(testSig.symptoms[1], StackFramesSymptom)
def runTest(self): config = ProgramConfiguration('test', 'x86-64', 'linux') crashInfo = CrashInfo.fromRawCrashData( ["(«f => (generator.throw(f))», «undefined»)"], [], config) testSignature = CrashSignature( '{"symptoms": [{"src": "stdout", "type": "output", "value": "x"}]}' ) self.assertFalse(testSignature.matches(crashInfo))
def test_SignatureStackSizeTest(): config = ProgramConfiguration("test", "x86-64", "linux") crashInfoPos = CrashInfo.fromRawCrashData([], [], config, auxCrashData=testAsanLongTrace.splitlines()) # The test signature uses > 15 which was previously interpreted as 0x15 # while the test crash data has 16 frames. testSig = CrashSignature(testSignatureStackSize) assert testSig.matches(crashInfoPos)
def runTest(self): config = ProgramConfiguration("test", "x86", "linux") crashInfo = CrashInfo.fromRawCrashData([], [], config, ubsanSampleTrace1.splitlines()) self.assertEqual(crashInfo.backtrace[0], "WelsDec::BsGetUe") self.assertEqual(crashInfo.backtrace[9], "_start") self.assertEqual(crashInfo.backtrace[11], "Lex< >")
def create(self, attrs): ''' Create a CrashEntry instance based on the given dictionary of values received. We need to unflatten foreign relationships like product, platform, os and client and create the foreign objects on the fly if they don't exist in our database yet. ''' missing_keys = {'rawStdout', 'rawStderr', 'rawCrashData'} - set(attrs.keys()) if missing_keys: raise InvalidArgumentException({key: ["This field is required."] for key in missing_keys}) attrs['product'] = Product.objects.get_or_create(**attrs['product'])[0] attrs['platform'] = Platform.objects.get_or_create(**attrs['platform'])[0] attrs['os'] = OS.objects.get_or_create(**attrs['os'])[0] attrs['client'] = Client.objects.get_or_create(**attrs['client'])[0] attrs['tool'] = Tool.objects.get_or_create(**attrs['tool'])[0] # Parse the incoming data using the crash signature package from FTB configuration = ProgramConfiguration(attrs['product'].name, attrs['platform'].name, attrs['os'].name, attrs['product'].version) crashInfo = CrashInfo.fromRawCrashData(attrs['rawStdout'], attrs['rawStderr'], configuration, attrs['rawCrashData']) # Populate certain fields here from the CrashInfo object we just got if crashInfo.crashAddress is not None: attrs['crashAddress'] = hex(crashInfo.crashAddress) attrs['shortSignature'] = crashInfo.createShortSignature() # If a testcase is supplied, create a testcase object and store it if 'test' in attrs['testcase']: testcase = attrs['testcase'] testcase_ext = attrs.pop('testcase_ext', None) testcase_quality = testcase.get('quality', 0) testcase_isbinary = testcase.get('isBinary', False) testcase = testcase['test'] if testcase_ext is None: raise RuntimeError("Must provide testcase extension when providing testcase") if testcase_isbinary: testcase = base64.b64decode(testcase) h = hashlib.new('sha1') if testcase_isbinary: h.update(str(testcase)) else: h.update(repr(testcase)) dbobj = TestCase(quality=testcase_quality, isBinary=testcase_isbinary, size=len(testcase)) dbobj.test.save("%s.%s" % (h.hexdigest(), testcase_ext), ContentFile(testcase)) dbobj.save() attrs['testcase'] = dbobj else: attrs['testcase'] = None # Create our CrashEntry instance return super(CrashEntrySerializer, self).create(attrs)
def runTest(self): config = ProgramConfiguration("test", "x86-64", "linux") crashInfoPos = CrashInfo.fromRawCrashData([], [], config, auxCrashData=testTraceNegativeSizeParam.splitlines()) testSig = crashInfoPos.createCrashSignature() self.assertIn("/ERROR: AddressSanitizer", str(testSig)) self.assertIn("negative\\\\-size\\\\-param", str(testSig)) self.assertTrue(isinstance(testSig.symptoms[1], StackFramesSymptom))
def runTest(self): config = ProgramConfiguration("test", "x86-64", "windows") crashInfoPos = CrashInfo.fromRawCrashData([], [], config, auxCrashData=testAsanAccessViolation.splitlines()) testSig = crashInfoPos.createCrashSignature() self.assertIn("/ERROR: AddressSanitizer", str(testSig)) self.assertIn("access\\\\-violation", str(testSig)) self.assertTrue(isinstance(testSig.symptoms[1], StackFramesSymptom))
def runTest(self): config = ProgramConfiguration("test", "x86-64", "linux") crashInfo = ASanCrashInfo([], asanTraceUAF.splitlines(), config) self.assertEqual(len(crashInfo.backtrace), 23) self.assertEqual(crashInfo.backtrace[0], "void mozilla::PodCopy<char16_t>(char16_t*, char16_t const*, unsigned long)") self.assertEqual(crashInfo.backtrace[4], "JSFunction::native() const") self.assertEqual(crashInfo.crashAddress, 0x7fd766c42800L)
def runTest(self): config = ProgramConfiguration("test", "x86", "linux") crashInfoNeg = CrashInfo.fromRawCrashData([], [], config, auxCrashData=testTraceHeapWithCrashAddress.splitlines()) crashInfoPos = CrashInfo.fromRawCrashData([], [], config, auxCrashData=testTraceHeapWithoutCrashAddress.splitlines()) testSigEmptyCrashAddress = CrashSignature(testSignatureEmptyCrashAddress) self.assertTrue(testSigEmptyCrashAddress.matches(crashInfoPos)) self.assertFalse(testSigEmptyCrashAddress.matches(crashInfoNeg))
def test_SignatureAsanAccessViolationTest(): config = ProgramConfiguration("test", "x86-64", "windows") crashInfoPos = CrashInfo.fromRawCrashData([], [], config, auxCrashData=testAsanAccessViolation.splitlines()) testSig = crashInfoPos.createCrashSignature() assert "/ERROR: AddressSanitizer" not in str(testSig) assert "access-violation" not in str(testSig) assert isinstance(testSig.symptoms[0], StackFramesSymptom)
def test_SignatureStackFramesNegativeSizeParamTest(): config = ProgramConfiguration("test", "x86-64", "linux") crashInfoPos = CrashInfo.fromRawCrashData([], [], config, auxCrashData=testTraceNegativeSizeParam.splitlines()) testSig = crashInfoPos.createCrashSignature() assert "/ERROR: AddressSanitizer" in str(testSig) assert "negative-size-param" in str(testSig) assert isinstance(testSig.symptoms[1], StackFramesSymptom)
def runTest(self): config = ProgramConfiguration("test", "x86-64", "linux") crashInfo1 = GDBCrashInfo([], gdbCrashAddress1.splitlines(), config) crashInfo2 = GDBCrashInfo([], gdbCrashAddress2.splitlines(), config) crashInfo3 = GDBCrashInfo([], gdbCrashAddress3.splitlines(), config) self.assertEqual(crashInfo1.crashAddress, 0x1L) self.assertEqual(crashInfo2.crashAddress, None) self.assertEqual(crashInfo3.crashAddress, 0xffffffffffffffa0L)
def test_SignaturePCREShortTest(): config = ProgramConfiguration("test", "x86", "linux") crashInfo = CrashInfo.fromRawCrashData([], [], config, auxCrashData=testTrace1.splitlines()) testSig1 = CrashSignature(testSignaturePCREShort1) testSig2 = CrashSignature(testSignaturePCREShort2) assert testSig1.matches(crashInfo) assert not testSig2.matches(crashInfo)
def runTest(self): config = ProgramConfiguration("test", "x86", "linux") crashInfo = CrashInfo.fromRawCrashData([], [], config, auxCrashData=testTrace1.splitlines()) testSig1 = CrashSignature(testSignaturePCREShort1) testSig2 = CrashSignature(testSignaturePCREShort2) self.assertTrue(testSig1.matches(crashInfo)) self.assertFalse(testSig2.matches(crashInfo))
def test_SignatureStackFramesRegressionTest(): config = ProgramConfiguration("test", "x86", "linux") crashInfoNeg = CrashInfo.fromRawCrashData([], [], config, auxCrashData=testTraceHeapWithCrashAddress.splitlines()) crashInfoPos = CrashInfo.fromRawCrashData([], [], config, auxCrashData=testTraceHeapWithoutCrashAddress.splitlines()) testSigEmptyCrashAddress = CrashSignature(testSignatureEmptyCrashAddress) assert testSigEmptyCrashAddress.matches(crashInfoPos) assert not testSigEmptyCrashAddress.matches(crashInfoNeg)