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): # 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", "linux") crashInfo1 = CrashInfo.fromRawCrashData([], [], config, auxCrashData=asanTraceCrash.splitlines()) crashInfo2 = CrashInfo.fromRawCrashData([], asanTraceUAF.splitlines(), config) self.assertIsInstance(crashInfo1, ASanCrashInfo) self.assertIsInstance(crashInfo2, ASanCrashInfo)
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_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)
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", "linux") crashInfo = CrashInfo.fromRawCrashData([], [], config, auxCrashData=testTrace1.splitlines()) testSig3 = CrashSignature(testSignature3) testSig4 = CrashSignature(testSignature4) testSig5 = CrashSignature(testSignature5) testSig6 = CrashSignature(testSignature6) self.assertFalse(testSig3.matchRequiresTest()) self.assertTrue(testSig4.matchRequiresTest()) self.assertTrue(testSig5.matchRequiresTest()) # Must not match without testcase provided self.assertFalse(testSig4.matches(crashInfo)) self.assertFalse(testSig5.matches(crashInfo)) self.assertFalse(testSig6.matches(crashInfo)) # Attach testcase crashInfo.testcase = testCase1 # Must match with testcase provided self.assertTrue(testSig4.matches(crashInfo)) self.assertTrue(testSig5.matches(crashInfo)) # This one does not match at all self.assertFalse(testSig6.matches(crashInfo))
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 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 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_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_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 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-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 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 runTest(self): 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) self.assertTrue(testSig.matches(crashInfoPos))
def runTest(self): config = ProgramConfiguration("test", "x86-64", "linux") crashInfoPos = CrashInfo.fromRawCrashData([], [], config, auxCrashData=testAsanStackOverflow.splitlines()) testSig = crashInfoPos.createCrashSignature() # Check matches appropriately self.assertTrue(testSig.matches(crashInfoPos))
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-64", "linux") crashInfoPos = CrashInfo.fromRawCrashData([], [], config, auxCrashData=testAsanFailedAlloc.splitlines()) testSig = crashInfoPos.createCrashSignature() self.assertIn("/AddressSanitizer failed to allocate", str(testSig)) self.assertTrue(testSig.matches(crashInfoPos)) self.assertTrue(isinstance(testSig.symptoms[1], StackFramesSymptom))
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_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 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", "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 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 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") 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 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", "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 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'] = '0x%x' % 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") h = hashlib.new('sha1') if testcase_isbinary: testcase = base64.b64decode(testcase) h.update(testcase) else: h.update(repr(testcase).encode("utf-8")) 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") crashSignature1 = '{ "symptoms" : [ { "type" : "output", "value" : "test" } ] }' crashSignature1Neg = '{ "symptoms" : [ { "type" : "output", "src" : "stderr", "value" : "test" } ] }' crashSignature2 = '{ "symptoms" : [ { "type" : "output", "src" : "stderr", "value" : { "value" : "^fest$", "matchType" : "pcre" } } ] }' outputSignature1 = CrashSignature(crashSignature1) outputSignature1Neg = CrashSignature(crashSignature1Neg) outputSignature2 = CrashSignature(crashSignature2) gdbOutput = [] stdout = [] stderr = [] stdout.append("Foo") stdout.append("Bartester") stdout.append("Baz") stderr.append("hackfest") crashInfo = CrashInfo.fromRawCrashData(stdout, stderr, config, auxCrashData=gdbOutput) self.assertIsInstance(crashInfo, NoCrashInfo) # Ensure we match on stdout/err if nothing is specified self.assert_(outputSignature1.matches(crashInfo)) # Don't match stdout if stderr is specified self.assertFalse(outputSignature1Neg.matches(crashInfo)) # Check that we're really using PCRE self.assertFalse(outputSignature2.matches(crashInfo)) # Add something the PCRE should match, then retry stderr.append("fest") crashInfo = CrashInfo.fromRawCrashData(stdout, stderr, config, auxCrashData=gdbOutput) self.assert_(outputSignature2.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 runTest(self): config = ProgramConfiguration("test", "x86-64", "linux") crashSignature1 = '{ "symptoms" : [ { "type" : "crashAddress", "address" : "< 0x1000" } ] }' crashSignature1Neg = '{ "symptoms" : [ { "type" : "crashAddress", "address" : "0x1000" } ] }' addressSig1 = CrashSignature(crashSignature1) addressSig1Neg = CrashSignature(crashSignature1Neg) crashInfo1 = CrashInfo.fromRawCrashData([], [], config, auxCrashData=gdbSampleTrace1.splitlines()) crashInfo3 = CrashInfo.fromRawCrashData([], [], config, auxCrashData=gdbSampleTrace3.splitlines()) self.assertIsInstance(crashInfo1, GDBCrashInfo) self.assert_(addressSig1.matches(crashInfo1)) self.assertFalse(addressSig1Neg.matches(crashInfo1)) # For crashInfo3, we don't have a crash address. Ensure we don't match self.assertFalse(addressSig1.matches(crashInfo3)) self.assertFalse(addressSig1Neg.matches(crashInfo3))
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 OnFault(self, run, test, variationCount, monitorData, actionValues): # Setup FuzzManager with information about target and platform data. program_configuration = ProgramConfiguration.fromBinary( self.target_binary) # Prepare FuzzManager with target and crash information. stdout = self._get_value_by_key(monitorData, "stdout.txt", "N/A") stderr = self._get_value_by_key(monitorData, "stderr.txt", "N/A") auxdat = self._get_value_by_key(monitorData, "auxdat.txt", "N/A") crash_info = CrashInfo.fromRawCrashData(stdout, stderr, program_configuration, auxdat) collector = Collector(tool="peach") # Write testcase content and any additional meta information to a temporary ZIP archive. buffer = StringIO.StringIO() zip_buffer = zipfile.ZipFile(buffer, 'w') # Collect |actionValues| crash information from Peach. for i in range(len(actionValues)): if len(actionValues[i]) > 2: data = actionValues[i][2] fileName = "data_%d_%s_%s.txt" % (i, actionValues[i][1], actionValues[i][0]) zip_buffer.writestr(fileName, data) if len(actionValues[i] ) > 3 and actionValues[i][1] != 'output': data = repr(actionValues[i][3]) fileName = "data_%d_%s_%s_action.txt" % ( i, actionValues[i][1], actionValues[i][0]) zip_buffer.writestr(fileName, data) if len(actionValues[i] ) > 3 and actionValues[i][1] == 'output': fileName = "data_%d_%s_%s_fileName.txt" % ( i, actionValues[i][1], actionValues[i][0]) data = actionValues[i][3] zip_buffer.writestr(fileName, data) # Collect |monitorData| crash information from Peach. for k, v in monitorData.items(): zip_buffer.writestr(k, v) zip_buffer.close() with tempfile.NamedTemporaryFile(delete=False, suffix='.zip') as testcase: buffer.seek(0) testcase.write(buffer.getvalue()) testcase.close() # Submit crash report with testcase to FuzzManager. collector.submit(crash_info, testcase.name, metaData=None)
def getCrashInfo(self, attachTestcase=False): # 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) crashInfo = CrashInfo.fromRawCrashData(self.rawStdout, self.rawStderr, configuration, self.rawCrashData) if attachTestcase and self.testcase != None and not self.testcase.isBinary: self.testcase.loadTest() crashInfo.testcase = self.testcase.content return crashInfo
def runTest(self): config = ProgramConfiguration("test", "x86-64", "linux") crashInfoPos = CrashInfo.fromRawCrashData([], [], config, auxCrashData=testTraceWithAuxMessage.splitlines()) crashInfoNeg = CrashInfo.fromRawCrashData([], [], config, auxCrashData=testTraceWithAuxAndAbortMessage.splitlines()) crashSignaturePos = crashInfoPos.createCrashSignature() crashSignatureNeg = crashInfoNeg.createCrashSignature() # Check that the first crash signature has ASan symptoms but # the second does not because it has a program abort message self.assertIn("/ERROR: AddressSanitizer", str(crashSignaturePos)) self.assertIn("/READ of size", str(crashSignaturePos)) self.assertNotIn("/ERROR: AddressSanitizer", str(crashSignatureNeg)) self.assertNotIn("/READ of size", str(crashSignatureNeg)) # Check matches appropriately self.assertTrue(crashSignaturePos.matches(crashInfoPos)) self.assertTrue(crashSignaturePos.matches(crashInfoNeg)) self.assertFalse(crashSignatureNeg.matches(crashInfoPos)) self.assertTrue(crashSignatureNeg.matches(crashInfoNeg))
def runTest(self): config = ProgramConfiguration("test", "x86", "linux") crashInfo = CrashInfo.fromRawCrashData([], [], config, auxCrashData=testTrace2.splitlines()) testSig = crashInfo.createCrashSignature() # Ensure that the last frame with a symbol is at the right place and there is nothing else, # espcially no wildcard, following afterwards. self.assertTrue(isinstance(testSig.symptoms[0], StackFramesSymptom)) self.assertEqual(str(testSig.symptoms[0].functionNames[6]), "js::jit::CheckOverRecursedWithExtra") self.assertEqual(len(testSig.symptoms[0].functionNames), 7)
def runTest(self): config = ProgramConfiguration("test", "x86-64", "linux") crashSignature1 = '{ "symptoms" : [ { "type" : "instruction", "registerNames" : ["r14"] } ] }' crashSignature1Neg = '{ "symptoms" : [ { "type" : "instruction", "registerNames" : ["r14", "rax"] } ] }' crashSignature2 = '{ "symptoms" : [ { "type" : "instruction", "instructionName" : "mov" } ] }' crashSignature2Neg = '{ "symptoms" : [ { "type" : "instruction", "instructionName" : "cmp" } ] }' crashSignature3 = '{ "symptoms" : [ { "type" : "instruction", "instructionName" : "mov", "registerNames" : ["r14", "rbx"] } ] }' crashSignature3Neg = '{ "symptoms" : [ { "type" : "instruction", "instructionName" : "mov", "registerNames" : ["r14", "rax"] } ] }' instructionSig1 = CrashSignature(crashSignature1) instructionSig1Neg = CrashSignature(crashSignature1Neg) instructionSig2 = CrashSignature(crashSignature2) instructionSig2Neg = CrashSignature(crashSignature2Neg) instructionSig3 = CrashSignature(crashSignature3) instructionSig3Neg = CrashSignature(crashSignature3Neg) crashInfo2 = CrashInfo.fromRawCrashData([], [], config, auxCrashData=gdbSampleTrace2.splitlines()) crashInfo3 = CrashInfo.fromRawCrashData([], [], config, auxCrashData=gdbSampleTrace3.splitlines()) self.assertIsInstance(crashInfo2, GDBCrashInfo) self.assertIsInstance(crashInfo3, GDBCrashInfo) self.assert_(instructionSig1.matches(crashInfo2)) self.assertFalse(instructionSig1Neg.matches(crashInfo2)) self.assert_(instructionSig2.matches(crashInfo2)) self.assertFalse(instructionSig2Neg.matches(crashInfo2)) self.assert_(instructionSig3.matches(crashInfo2)) self.assertFalse(instructionSig3Neg.matches(crashInfo2)) # Crash info3 doesn't have register information, ensure we don't match any self.assertFalse(instructionSig1.matches(crashInfo3)) self.assertFalse(instructionSig2.matches(crashInfo3)) self.assertFalse(instructionSig3.matches(crashInfo3))
def runTest(self): config = ProgramConfiguration("test", "x86-64", "linux") crashInfo = CrashInfo.fromRawCrashData([], [], config, auxCrashData=tsanSimpleLeakReport.splitlines()) testSignature = crashInfo.createCrashSignature() self.assertTrue(testSignature.matches(crashInfo)) found = False for symptom in testSignature.symptoms: if isinstance(symptom, OutputSymptom): self.assertEqual(symptom.src, "crashdata") self.assertEqual(symptom.output.value, "WARNING: ThreadSanitizer: thread leak") found = True self.assertTrue(found, msg="Expected correct OutputSymptom in signature")
def test_collector_generate_search(tmpdir): '''Test sigcache generation and search''' # create a cache dir cache_dir = tmpdir.mkdir('sigcache').strpath # create a collector collector = Collector(sigCacheDir=cache_dir) # generate a signature from the crash data config = ProgramConfiguration('mozilla-central', 'x86-64', 'linux', version='ba0bc4f26681') crashInfo = CrashInfo.fromRawCrashData([], asanTraceCrash.splitlines(), config) sig = collector.generate(crashInfo, False, False, 8) assert {f.strpath for f in tmpdir.join('sigcache').listdir()} == {sig} # search the sigcache and see that it matches the original sigMatch, meta = collector.search(crashInfo) assert sigMatch == sig assert meta is None # write metadata and make sure that's returned if it exists sigBase, _ = os.path.splitext(sig) with open(sigBase + '.metadata', 'w') as f: f.write('{}') sigMatch, meta = collector.search(crashInfo) assert sigMatch == sig assert meta == {} # make sure another crash doesn't match crashInfo = CrashInfo.fromRawCrashData([], [], config) sigMatch, meta = collector.search(crashInfo) assert sigMatch is None assert meta is None # returns None if sig generation fails result = collector.generate(crashInfo, True, True, 8) assert result is None
def add_fault(self): # Setup FuzzManager with target information and platform data. program_configuration = ProgramConfiguration.fromBinary(self.binary) # Prepare FuzzManager with crash information. stdout = "N/A" # Todo: There is no plain stdout logger yet. stderr = "N/A" # Todo: There is no plain stderr logger yet. auxdat = self.bucket.get("crashlog", "N/A").get("data", "N/A") metaData = None testcase = self.save_bucket_as_zip(self.bucket) crash_info = CrashInfo.fromRawCrashData(stdout, stderr, program_configuration, auxdat) # Submit crash report with testcase to FuzzManager. collector = Collector(tool="dharma") collector.submit(crash_info, testcase, metaData)
def runTest(self): config = ProgramConfiguration("test", "x86", "linux") crashInfo = CrashInfo.fromRawCrashData([], [], config, auxCrashData=testTrace1.splitlines()) testSig1 = CrashSignature(testSignatureStackFrames1) testSig2 = CrashSignature(testSignatureStackFrames2) testSig3 = CrashSignature(testSignatureStackFrames3) testSig4 = CrashSignature(testSignatureStackFrames4) testSig5 = CrashSignature(testSignatureStackFrames5) self.assertTrue(testSig1.matches(crashInfo)) self.assertTrue(testSig2.matches(crashInfo)) self.assertTrue(testSig3.matches(crashInfo)) self.assertTrue(testSig4.matches(crashInfo)) self.assertFalse(testSig5.matches(crashInfo))
def runTest(self): collector = Collector(self.tmpCacheDir, serverHost='127.0.0.1', serverPort='8000', serverProtocol='http', serverUser=testAuthCreds[0], serverPass=testAuthCreds[1], clientId='test-fuzzer1') config = ProgramConfiguration("mozilla-central", "x86-64", "linux", version="ba0bc4f26681") crashInfo = CrashInfo.fromRawCrashData([], asanTraceCrash.splitlines(), config) # TODO: This is only a rudimentary check to see if we submitted *something*. # We should check more precisely that the information submitted is correct. issueCount = self.getRemoteCrashEntryCount() collector.submit(crashInfo, exampleTestCase) self.assertEqual(self.getRemoteCrashEntryCount(), issueCount + 1)
def OnFault(self, run, test, variationCount, monitorData, actionValues): # Setup FuzzManager with information about target and platform data. program_configuration = ProgramConfiguration.fromBinary(self.target_binary) # Prepare FuzzManager with target and crash information. stdout = self._get_value_by_key(monitorData, "stdout.txt", "N/A") stderr = self._get_value_by_key(monitorData, "stderr.txt", "N/A") auxdat = self._get_value_by_key(monitorData, "auxdat.txt", "N/A") crash_info = CrashInfo.fromRawCrashData(stdout, stderr, program_configuration, auxdat) collector = Collector(tool="peach") # Write testcase content and any additional meta information to a temporary ZIP archive. buffer = StringIO.StringIO() zip_buffer = zipfile.ZipFile(buffer, 'w') # Collect |actionValues| crash information from Peach. for i in range(len(actionValues)): if len(actionValues[i]) > 2: data = actionValues[i][2] fileName = "data_%d_%s_%s.txt" % (i, actionValues[i][1], actionValues[i][0]) zip_buffer.writestr(fileName, data) if len(actionValues[i]) > 3 and actionValues[i][1] != 'output': data = repr(actionValues[i][3]) fileName = "data_%d_%s_%s_action.txt" % (i, actionValues[i][1], actionValues[i][0]) zip_buffer.writestr(fileName, data) if len(actionValues[i]) > 3 and actionValues[i][1] == 'output': fileName = "data_%d_%s_%s_fileName.txt" % (i, actionValues[i][1], actionValues[i][0]) data = actionValues[i][3] zip_buffer.writestr(fileName, data) # Collect |monitorData| crash information from Peach. for k, v in monitorData.items(): zip_buffer.writestr(k, v) zip_buffer.close() with tempfile.NamedTemporaryFile(delete=False, suffix='.zip') as testcase: buffer.seek(0) testcase.write(buffer.getvalue()) testcase.close() # Submit crash report with testcase to FuzzManager. collector.submit(crash_info, testcase.name, metaData=None)
def runTest(self): 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 self.assert_(crashSig1.matches(crashInfo)) self.assert_(crashSig2.matches(crashInfo)) self.assert_(crashSig3.matches(crashInfo)) # Check that the generated signatures look as expected self.assertEqual(json.loads(str(crashSig1)), json.loads(testSignature1)) self.assertEqual(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. self.assertEqual(json.loads(str(crashSig3)), json.loads(testSignature3))
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))