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 search(self, crashInfo): ''' Searches within the local signature cache directory for a signature matching the given crash. @type crashInfo: CrashInfo @param crashInfo: CrashInfo instance obtained from L{CrashInfo.fromRawCrashData} @rtype: tuple @return: Tuple containing filename of the signature and metadata matching, or None if no match. ''' cachedSigFiles = os.listdir(self.sigCacheDir) for sigFile in cachedSigFiles: if not sigFile.endswith('.signature'): continue sigFile = os.path.join(self.sigCacheDir, sigFile) if not os.path.isdir(sigFile): with open(sigFile) as f: sigData = f.read() crashSig = CrashSignature(sigData) if crashSig.matches(crashInfo): metadataFile = sigFile.replace('.signature', '.metadata') metadata = None if os.path.exists(metadataFile): with open(metadataFile) as m: metadata = json.loads(m.read()) return (sigFile, metadata) return (None, None)
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-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') crashInfo = CrashInfo.fromRawCrashData( ["(«f => (generator.throw(f))», «undefined»)"], [], config) testSignature = CrashSignature( '{"symptoms": [{"src": "stdout", "type": "output", "value": "x"}]}' ) self.assertFalse(testSignature.matches(crashInfo))
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", "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): 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): collector = Collector(self.tmpCacheDir, serverHost='127.0.0.1', serverPort='8000', serverProtocol='http', serverUser=testAuthCreds[0], serverPass=testAuthCreds[1], clientId='test-fuzzer1') collector.refresh() receivedSignatures = False for sigFile in os.listdir(self.tmpCacheDir): receivedSignatures = True CrashSignature.fromFile(os.path.join(self.tmpCacheDir, sigFile)) if not receivedSignatures: self.skipTest("Server did not provide signatures")
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 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 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 getOptimizedSignature(self): return CrashSignature(self.optimizedSignature)
def getSignature(self): return CrashSignature(self.signature)
class CrashInfo(): ''' Abstract base class that provides a method to instantiate the right sub class. It also supports generating a CrashSignature based on the stored information. ''' __metaclass__ = ABCMeta def __init__(self): # Store the raw data self.rawStdout = [] self.rawStderr = [] self.rawCrashData = [] # Store processed data self.backtrace = [] self.registers = {} self.crashAddress = None self.crashInstruction = None # Store configuration data (platform, product, os, etc.) self.configuration = None # This is an optional testcase that is not stored with the crashInfo but # can be "attached" before matching signatures that might require the # testcase. self.testcase = None # This can be used to record failures during signature creation self.failureReason = None def __str__(self): buf = [] buf.append("Crash trace:") buf.append("") for idx, frame in enumerate(self.backtrace): buf.append("# %02d %s" % (idx, frame)) buf.append("") if self.crashAddress: buf.append("Crash address: %s" % self.crashAddress) if self.crashInstruction: buf.append("Crash instruction: %s" % self.crashInstruction) if self.crashAddress or self.crashInstruction: buf.append("") buf.append("Last 5 lines on stderr:") buf.extend(self.rawStderr[-5:]) return "\n".join(buf) def toCacheObject(self): ''' Create a cache object for restoring the class instance later on without parsing the crash data again. This object includes all class fields except for the storage heavy raw objects like stdout, stderr and raw crashdata. @rtype: dict @return: Dictionary containing expensive class fields ''' cacheObject = {} cacheObject['backtrace'] = self.backtrace cacheObject['registers'] = self.registers if self.crashAddress is not None: cacheObject['crashAddress'] = long(self.crashAddress) else: cacheObject['crashAddress'] = None cacheObject['crashInstruction'] = self.crashInstruction cacheObject['failureReason'] = self.failureReason return cacheObject @staticmethod def fromRawCrashData(stdout, stderr, configuration, auxCrashData=None, cacheObject=None): ''' Create appropriate CrashInfo instance from raw crash data @type stdout: List of strings @param stdout: List of lines as they appeared on stdout @type stderr: List of strings @param stderr: List of lines as they appeared on stderr @type configuration: ProgramConfiguration @param configuration: Exact program configuration that is associated with the crash @type auxCrashData: List of strings @param auxCrashData: Optional additional crash output (e.g. GDB). If not specified, stderr is used. @type cacheObject: Dictionary @param cacheObject: The cache object that should be used to restore the class fields instead of parsing the crash data. The appropriate object can be created by calling the toCacheObject method. @rtype: CrashInfo @return: Crash information object ''' assert stdout == None or isinstance(stdout, list) or isinstance( stdout, basestring) assert stderr == None or isinstance(stderr, list) or isinstance( stderr, basestring) assert auxCrashData == None or isinstance( auxCrashData, list) or isinstance(auxCrashData, basestring) assert isinstance(configuration, ProgramConfiguration) if isinstance(stdout, basestring): stdout = stdout.splitlines() if isinstance(stderr, basestring): stderr = stderr.splitlines() if isinstance(auxCrashData, basestring): auxCrashData = auxCrashData.splitlines() if cacheObject is not None: c = CrashInfo() if stdout != None: c.rawStdout.extend(stdout) if stderr != None: c.rawStderr.extend(stderr) if auxCrashData != None: c.rawCrashData.extend(auxCrashData) c.configuration = configuration c.backtrace = cacheObject['backtrace'] c.registers = cacheObject['registers'] c.crashAddress = cacheObject['crashAddress'] c.crashInstruction = cacheObject['crashInstruction'] c.failureReason = cacheObject['failureReason'] return c asanString = "ERROR: AddressSanitizer:" gdbString = "Program received signal " gdbCoreString = "Program terminated with signal " ubsanString = "SUMMARY: AddressSanitizer: undefined-behavior" appleString = "OS Version: Mac OS X" # Use two strings for detecting Minidumps to avoid false positives minidumpFirstString = "OS|" minidumpSecondString = "CPU|" minidumpFirstDetected = False # Search both crashData and stderr, but prefer crashData lines = [] if auxCrashData != None: lines.extend(auxCrashData) if stderr != None: lines.extend(stderr) for line in lines: if ubsanString in line: return UBSanCrashInfo(stdout, stderr, configuration, auxCrashData) elif asanString in line: return ASanCrashInfo(stdout, stderr, configuration, auxCrashData) elif appleString in line: return AppleCrashInfo(stdout, stderr, configuration, auxCrashData) elif gdbString in line or gdbCoreString in line: return GDBCrashInfo(stdout, stderr, configuration, auxCrashData) elif not minidumpFirstDetected and minidumpFirstString in line: # Only match Minidump output if the *next* line also contains # the second search string defined above. minidumpFirstDetected = True elif minidumpFirstDetected and minidumpSecondString in line: return MinidumpCrashInfo(stdout, stderr, configuration, auxCrashData) elif minidumpFirstDetected: minidumpFirstDetected = False # Default fallback to be used if there is neither ASan nor GDB output. # This is still useful in case there is no crash but we want to match # e.g. stdout/stderr output with signatures. return NoCrashInfo(stdout, stderr, configuration, auxCrashData) def createShortSignature(self): ''' @rtype: String @return: A string representing this crash (short signature) ''' # See if we have an abort message and if so, use that as short signature abortMsg = AssertionHelper.getAssertion(self.rawStderr, True) if abortMsg != None: return abortMsg # See if we have an abort message in our crash data maybe, e.g. for UBSan if self.rawCrashData: abortMsg = AssertionHelper.getAssertion(self.rawCrashData, True) if abortMsg != None: return abortMsg if not len(self.backtrace): return "No crash detected" return "[@ %s]" % self.backtrace[0] def createCrashSignature(self, forceCrashAddress=False, forceCrashInstruction=False, maxFrames=8, minimumSupportedVersion=13): ''' @param forceCrashAddress: If True, the crash address will be included in any case @type forceCrashAddress: bool @param forceCrashInstruction: If True, the crash instruction will be included in any case @type forceCrashInstruction: bool @param maxFrames: How many frames (at most) should be included in the signature @type maxFrames: int @param minimumSupportedVersion: The minimum crash signature standard version that the generated signature should be valid for (10 => 1.0, 13 => 1.3) @type minimumSupportedVersion: int @rtype: CrashSignature @return: A crash signature object ''' # Determine the actual number of frames based on how many we got if len(self.backtrace) > maxFrames: numFrames = maxFrames else: numFrames = len(self.backtrace) # See if we have an abort message and if so, get a sanitized version of it abortMsg = AssertionHelper.getAssertion(self.rawStderr, True) abortMsgInCrashdata = False if abortMsg is None and minimumSupportedVersion >= 13: # Look for abort messages also inside crashdata (e.g. UBSan) # only on version 1.3 or higher, because the "crashdata" source # type for output matching was added in that version. abortMsg = AssertionHelper.getAssertion(self.rawCrashData, True) abortMsgInCrashdata = True if abortMsg != None: abortMsg = AssertionHelper.getSanitizedAssertionPattern(abortMsg) # Consider the first four frames as top stack topStackLimit = 4 symptomArr = [] if abortMsg != None: abortMsgSrc = "stderr" if abortMsgInCrashdata: abortMsgSrc = "crashdata" # Compose StringMatch object with PCRE pattern. # Versions below 1.2 only support the full object PCRE style, # for anything newer, use the short form with forward slashes # to increase the readability of the signatures. if minimumSupportedVersion < 12: stringObj = {"value": abortMsg, "matchType": "pcre"} symptomObj = { "type": "output", "src": abortMsgSrc, "value": stringObj } else: symptomObj = { "type": "output", "src": abortMsgSrc, "value": "/%s/" % abortMsg } symptomArr.append(symptomObj) # If we have less than topStackLimit frames available anyway, count the difference # between topStackLimit and the available frames already as missing. # E.g. if the trace has only three entries anyway, one will be considered missing # right from the start. This should prevent that very short stack frames are used # for signatures without additional crash information that narrows the signature. if numFrames >= topStackLimit: topStackMissCount = 0 else: topStackMissCount = topStackLimit - numFrames # StackFramesSymptom is only supported in 1.2 and higher, # for everything else, use multiple stackFrame symptoms if minimumSupportedVersion < 12: for idx in range(0, numFrames): functionName = self.backtrace[idx] if not functionName == "??": symptomObj = { "type": "stackFrame", "frameNumber": idx, "functionName": functionName } symptomArr.append(symptomObj) elif idx < 4: # If we're in the top 4, we count this as a miss topStackMissCount += 1 else: framesArray = [] for idx in range(0, numFrames): functionName = self.backtrace[idx] if not functionName == "??": framesArray.append(functionName) else: framesArray.append("?") if idx < 4: # If we're in the top 4, we count this as a miss topStackMissCount += 1 lastSymbolizedFrame = None for frameIdx in range(0, len(framesArray)): if str(framesArray[frameIdx]) != '?': lastSymbolizedFrame = frameIdx if lastSymbolizedFrame != None: # Remove all elements behind the last symbolized frame framesArray = framesArray[:lastSymbolizedFrame + 1] else: # We don't have a single symbolized frame, so it doesn't make sense # to keep any wildcards in case we added some for unsymbolized frames. framesArray = [] if framesArray: symptomArr.append({ "type": "stackFrames", "functionNames": framesArray }) # Missing too much of the top stack frames, add additional crash information stackIsInsufficient = topStackMissCount >= 2 and abortMsg == None includeCrashAddress = stackIsInsufficient or forceCrashAddress includeCrashInstruction = ( stackIsInsufficient and self.crashInstruction != None) or forceCrashInstruction if includeCrashAddress: if self.crashAddress == None: failureReason = self.failureReason self.failureReason = "No crash address available from crash data. Reason: %s" % failureReason return None if self.crashAddress != 0L and self.crashAddress < 0x100L: # Try to match crash addresses that are small but non-zero # with a generic range that is likely associated with null-deref. crashAddress = "< 0x100" else: crashAddress = hex(self.crashAddress).rstrip("L") crashAddressSymptomObj = { "type": "crashAddress", "address": crashAddress } symptomArr.append(crashAddressSymptomObj) if includeCrashInstruction: if self.crashInstruction == None: failureReason = self.failureReason self.failureReason = "No crash instruction available from crash data. Reason: %s" % failureReason return None crashInstructionSymptomObj = { "type": "instruction", "instructionName": self.crashInstruction } symptomArr.append(crashInstructionSymptomObj) sigObj = {"symptoms": symptomArr} return CrashSignature(json.dumps(sigObj, indent=2))
def createCrashSignature(self, forceCrashAddress=False, forceCrashInstruction=False, maxFrames=8, minimumSupportedVersion=12): ''' @param forceCrashAddress: If True, the crash address will be included in any case @type forceCrashAddress: bool @param forceCrashInstruction: If True, the crash instruction will be included in any case @type forceCrashInstruction: bool @param maxFrames: How many frames (at most) should be included in the signature @type maxFrames: int @param minimumSupportedVersion: The minimum crash signature standard version that the generated signature should be valid for (10 => 1.0, 12 => 1.2) @type minimumSupportedVersion: int @rtype: CrashSignature @return: A crash signature object ''' # Determine the actual number of frames based on how many we got if len(self.backtrace) > maxFrames: numFrames = maxFrames else: numFrames = len(self.backtrace) # See if we have an abort message and if so, get a sanitized version of it abortMsg = AssertionHelper.getAssertion(self.rawStderr, True) if abortMsg != None: abortMsg = AssertionHelper.getSanitizedAssertionPattern(abortMsg) # Consider the first four frames as top stack topStackLimit = 4 symptomArr = [] if abortMsg != None: # Compose StringMatch object with PCRE pattern. # Versions below 1.2 only support the full object PCRE style, # for anything newer, use the short form with forward slashes # to increase the readability of the signatures. if minimumSupportedVersion < 12: stringObj = {"value": abortMsg, "matchType": "pcre"} symptomObj = { "type": "output", "src": "stderr", "value": stringObj } else: symptomObj = { "type": "output", "src": "stderr", "value": "/%s/" % abortMsg } symptomArr.append(symptomObj) # If we have less than topStackLimit frames available anyway, count the difference # between topStackLimit and the available frames already as missing. # E.g. if the trace has only three entries anyway, one will be considered missing # right from the start. This should prevent that very short stack frames are used # for signatures without additional crash information that narrows the signature. if numFrames >= topStackLimit: topStackMissCount = 0 else: topStackMissCount = topStackLimit - numFrames # StackFramesSymptom is only supported in 1.2 and higher, # for everything else, use multiple stackFrame symptoms if minimumSupportedVersion < 12: for idx in range(0, numFrames): functionName = self.backtrace[idx] if not functionName == "??": symptomObj = { "type": "stackFrame", "frameNumber": idx, "functionName": functionName } symptomArr.append(symptomObj) elif idx < 4: # If we're in the top 4, we count this as a miss topStackMissCount += 1 else: framesArray = [] for idx in range(0, numFrames): functionName = self.backtrace[idx] if not functionName == "??": framesArray.append(functionName) else: framesArray.append("?") if idx < 4: # If we're in the top 4, we count this as a miss topStackMissCount += 1 lastSymbolizedFrame = None for frameIdx in range(0, len(framesArray)): if str(framesArray[frameIdx]) != '?': lastSymbolizedFrame = frameIdx if lastSymbolizedFrame != None: # Remove all elements behind the last symbolized frame framesArray = framesArray[:lastSymbolizedFrame + 1] else: # We don't have a single symbolized frame, so it doesn't make sense # to keep any wildcards in case we added some for unsymbolized frames. framesArray = [] if framesArray: symptomArr.append({ "type": "stackFrames", "functionNames": framesArray }) # Missing too much of the top stack frames, add additional crash information stackIsInsufficient = topStackMissCount >= 2 and abortMsg == None includeCrashAddress = stackIsInsufficient or forceCrashAddress includeCrashInstruction = ( stackIsInsufficient and self.crashInstruction != None) or forceCrashInstruction if includeCrashAddress: if self.crashAddress == None: failureReason = self.failureReason self.failureReason = "No crash address available from crash data. Reason: %s" % failureReason return None crashAddress = hex(self.crashAddress).rstrip("L") crashAddressSymptomObj = { "type": "crashAddress", "address": crashAddress } symptomArr.append(crashAddressSymptomObj) if includeCrashInstruction: if self.crashInstruction == None: failureReason = self.failureReason self.failureReason = "No crash instruction available from crash data. Reason: %s" % failureReason return None crashInstructionSymptomObj = { "type": "instruction", "instructionName": self.crashInstruction } symptomArr.append(crashInstructionSymptomObj) sigObj = {"symptoms": symptomArr} return CrashSignature(json.dumps(sigObj, indent=2))
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 test_SignatureStackFramesTest(): 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) assert testSig1.matches(crashInfo) assert testSig2.matches(crashInfo) assert testSig3.matches(crashInfo) assert testSig4.matches(crashInfo) assert not testSig5.matches(crashInfo)
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 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" : "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") 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') crashInfo = CrashInfo.fromRawCrashData(["(«f => (generator.throw(f))», «undefined»)"], [], config) testSignature = CrashSignature('{"symptoms": [{"src": "stdout", "type": "output", "value": "x"}]}') self.assertFalse(testSignature.matches(crashInfo))
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))