1""" 2Test that ASan memory history provider returns correct stack traces 3""" 4 5 6import lldb 7from lldbsuite.test.decorators import * 8from lldbsuite.test.lldbtest import * 9from lldbsuite.test import lldbplatform 10from lldbsuite.test import lldbutil 11from lldbsuite.test_event.build_exception import BuildError 12 13class AsanTestCase(TestBase): 14 @skipIfFreeBSD # llvm.org/pr21136 runtimes not yet available by default 15 @expectedFailureNetBSD 16 @skipUnlessAddressSanitizer 17 def test(self): 18 self.build(make_targets=["asan"]) 19 self.asan_tests() 20 21 @skipIf(oslist=no_match(["macosx"])) 22 def test_libsanitizers_asan(self): 23 try: 24 self.build(make_targets=["libsanitizers"]) 25 except BuildError as e: 26 self.skipTest("failed to build with libsanitizers") 27 self.libsanitizer_tests() 28 29 def setUp(self): 30 # Call super's setUp(). 31 TestBase.setUp(self) 32 self.line_malloc = line_number("main.c", "// malloc line") 33 self.line_malloc2 = line_number("main.c", "// malloc2 line") 34 self.line_free = line_number("main.c", "// free line") 35 self.line_breakpoint = line_number("main.c", "// break line") 36 37 # Test line numbers: rdar://126237493 38 def libsanitizer_tests(self): 39 target = self.createTestTarget() 40 41 self.runCmd( 42 "env SanitizersAddress=1 MallocSanitizerZone=1 MallocSecureAllocator=0" 43 ) 44 45 self.runCmd("run") 46 47 # In libsanitizers, memory history is not supported until a report has been generated 48 self.expect( 49 "thread list", 50 "Process should be stopped due to ASan report", 51 substrs=["stopped", "stop reason = Use of deallocated memory"], 52 ) 53 54 # test the 'memory history' command 55 self.expect( 56 "memory history 'pointer'", 57 substrs=[ 58 "Memory deallocated by Thread", 59 "a.out`f2", 60 "main.c", 61 "Memory allocated by Thread", 62 "a.out`f1", 63 "main.c", 64 ], 65 ) 66 67 # do the same using SB API 68 process = self.dbg.GetSelectedTarget().process 69 val = ( 70 process.GetSelectedThread().GetSelectedFrame().EvaluateExpression("pointer") 71 ) 72 addr = val.GetValueAsUnsigned() 73 threads = process.GetHistoryThreads(addr) 74 self.assertEqual(threads.GetSize(), 2) 75 76 history_thread = threads.GetThreadAtIndex(0) 77 self.assertTrue(history_thread.num_frames >= 2) 78 self.assertEqual( 79 history_thread.frames[1].GetLineEntry().GetFileSpec().GetFilename(), 80 "main.c", 81 ) 82 83 history_thread = threads.GetThreadAtIndex(1) 84 self.assertTrue(history_thread.num_frames >= 2) 85 self.assertEqual( 86 history_thread.frames[1].GetLineEntry().GetFileSpec().GetFilename(), 87 "main.c", 88 ) 89 90 # let's free the container (SBThreadCollection) and see if the 91 # SBThreads still live 92 threads = None 93 self.assertTrue(history_thread.num_frames >= 2) 94 self.assertEqual( 95 history_thread.frames[1].GetLineEntry().GetFileSpec().GetFilename(), 96 "main.c", 97 ) 98 99 def asan_tests(self): 100 target = self.createTestTarget() 101 102 self.registerSanitizerLibrariesWithTarget(target) 103 104 self.runCmd("breakpoint set -f main.c -l %d" % self.line_breakpoint) 105 106 # "memory history" command should not work without a process 107 self.expect( 108 "memory history 0", 109 error=True, 110 substrs=["Command requires a current process"], 111 ) 112 113 self.runCmd("run") 114 115 stop_reason = ( 116 self.dbg.GetSelectedTarget().process.GetSelectedThread().GetStopReason() 117 ) 118 if stop_reason == lldb.eStopReasonExec: 119 # On OS X 10.10 and older, we need to re-exec to enable 120 # interceptors. 121 self.runCmd("continue") 122 123 # the stop reason of the thread should be breakpoint. 124 self.expect( 125 "thread list", 126 STOPPED_DUE_TO_BREAKPOINT, 127 substrs=["stopped", "stop reason = breakpoint"], 128 ) 129 130 # test that the ASan dylib is present 131 self.expect( 132 "image lookup -n __asan_describe_address", 133 "__asan_describe_address should be present", 134 substrs=["1 match found"], 135 ) 136 137 # test the 'memory history' command 138 self.expect( 139 "memory history 'pointer'", 140 substrs=[ 141 "Memory deallocated by Thread", 142 "a.out`f2", 143 "main.c:%d" % self.line_free, 144 "Memory allocated by Thread", 145 "a.out`f1", 146 "main.c:%d" % self.line_malloc, 147 ], 148 ) 149 150 # do the same using SB API 151 process = self.dbg.GetSelectedTarget().process 152 val = ( 153 process.GetSelectedThread().GetSelectedFrame().EvaluateExpression("pointer") 154 ) 155 addr = val.GetValueAsUnsigned() 156 threads = process.GetHistoryThreads(addr) 157 self.assertEqual(threads.GetSize(), 2) 158 159 history_thread = threads.GetThreadAtIndex(0) 160 self.assertGreaterEqual(history_thread.num_frames, 2) 161 self.assertEqual( 162 history_thread.frames[1].GetLineEntry().GetFileSpec().GetFilename(), 163 "main.c", 164 ) 165 self.assertEqual( 166 history_thread.frames[1].GetLineEntry().GetLine(), self.line_free 167 ) 168 169 history_thread = threads.GetThreadAtIndex(1) 170 self.assertGreaterEqual(history_thread.num_frames, 2) 171 self.assertEqual( 172 history_thread.frames[1].GetLineEntry().GetFileSpec().GetFilename(), 173 "main.c", 174 ) 175 self.assertEqual( 176 history_thread.frames[1].GetLineEntry().GetLine(), self.line_malloc 177 ) 178 179 # let's free the container (SBThreadCollection) and see if the 180 # SBThreads still live 181 threads = None 182 self.assertGreaterEqual(history_thread.num_frames, 2) 183 self.assertEqual( 184 history_thread.frames[1].GetLineEntry().GetFileSpec().GetFilename(), 185 "main.c", 186 ) 187 self.assertEqual( 188 history_thread.frames[1].GetLineEntry().GetLine(), self.line_malloc 189 ) 190 191 # ASan will break when a report occurs and we'll try the API then 192 self.runCmd("continue") 193 194 self.expect( 195 "thread list", 196 "Process should be stopped due to ASan report", 197 substrs=["stopped", "stop reason = Use of deallocated memory"], 198 ) 199 200 # make sure the 'memory history' command still works even when we're 201 # generating a report now 202 self.expect( 203 "memory history 'another_pointer'", 204 substrs=[ 205 "Memory allocated by Thread", 206 "a.out`f1", 207 "main.c:%d" % self.line_malloc2, 208 ], 209 ) 210