Ejemplo n.º 1
0
 def fReportCPUUsageBug(oExcessiveCPUUsageDetector):
   if bDebugOutput: print "@@@ Reporting excessive CPU usage bug...";
   oCdbWrapper = oExcessiveCPUUsageDetector.oCdbWrapper;
   sBugTypeId = "CPUUsage";
   sBugDescription = "The application was using %d%% CPU for %d seconds, which is considered excessive." % \
       (oExcessiveCPUUsageDetector.nTotalCPUPercent, dxBugIdConfig["nExcessiveCPUUsageCheckInterval"]);
   sSecurityImpact = None;
   oCdbWrapper.oBugReport = cBugReport.foCreate(oCdbWrapper, sBugTypeId, sBugDescription, sSecurityImpact);
   if not oCdbWrapper.bCdbRunning: return;
   oCdbWrapper.fRemoveBreakpoint(oExcessiveCPUUsageDetector.uBugBreakpointId);
   oExcessiveCPUUsageDetector.uBugBreakpointId = None;
   if not oCdbWrapper.bCdbRunning: return;
Ejemplo n.º 2
0
 def fReportCPUUsageBug(oExcessiveCPUUsageDetector):
     oCdbWrapper = oExcessiveCPUUsageDetector.oCdbWrapper
     oExcessiveCPUUsageDetector.oLock.acquire()
     try:
         uStackPointer = oCdbWrapper.fuGetValue("@$csp")
         if not oCdbWrapper.bCdbRunning: return
         uInstructionPointer = oCdbWrapper.fuGetValue("@$ip")
         if not oCdbWrapper.bCdbRunning: return
         # This is a sanity check: the instruction pointer should be equal to the address at which we set the breakpoint.
         assert uInstructionPointer == oExcessiveCPUUsageDetector.uNextBreakpointAddress, \
             "Expected to hit breakpoint at 0x%X, but got 0x%X instead !?" % (oExcessiveCPUUsageDetector.uNextBreakpointAddress, uInstructionPointer)
         # The code we're expecting to return to may actually be *called* in recursive code. We can detect this by checking
         # if the stackpointer has increased or not. If not, we have not yet returned and will ignore this breakpoint.
         if uStackPointer < oExcessiveCPUUsageDetector.uLastStackPointer:
             oExcessiveCPUUsageDetector.fWormDebugOutput(
                 "Ignored bug breakpoint at IP=%p, SP=%p: SP but must be >=%p",
                 uInstructionPointer, uStackPointer,
                 oExcessiveCPUUsageDetector.uLastStackPointer)
             if not oCdbWrapper.bCdbRunning: return
             return
         if bDebugOutput: print "@@@ Reporting excessive CPU usage bug..."
         oExcessiveCPUUsageDetector.fWormDebugOutput(
             "Bug breakpoint at IP=%p, SP=%p is hit, removing breakpoint and reporting bug...",
             uInstructionPointer,
             uStackPointer,
         )
         # Remove the breakpoint
         oCdbWrapper.fRemoveBreakpoint(
             oExcessiveCPUUsageDetector.uBugBreakpointId)
         if not oCdbWrapper.bCdbRunning: return
         oExcessiveCPUUsageDetector.uBugBreakpointId = None
         # Report a bug
         sBugTypeId = "CPUUsage"
         sBugDescription = "The application was using %d%% CPU for %d seconds, which is considered excessive." % \
             (oExcessiveCPUUsageDetector.nTotalCPUUsagePercent, dxBugIdConfig["nExcessiveCPUUsageCheckInterval"])
         sSecurityImpact = None
         if not oCdbWrapper.bCdbRunning: return
         oCdbWrapper.oBugReport = cBugReport.foCreate(
             oCdbWrapper, sBugTypeId, sBugDescription, sSecurityImpact)
         if not oCdbWrapper.bCdbRunning: return
         oCdbWrapper.oBugReport.bRegistersRelevant = False
     finally:
         oExcessiveCPUUsageDetector.oLock.release()
 def fReportCPUUsageBug(oExcessiveCPUUsageDetector):
   oCdbWrapper = oExcessiveCPUUsageDetector.oCdbWrapper;
   oExcessiveCPUUsageDetector.oLock.acquire();
   try:
     oCdbWrapper.fSelectProcessAndThread(oExcessiveCPUUsageDetector.uProcessId, oExcessiveCPUUsageDetector.uThreadId);
     oWormProcess = oCdbWrapper.oCurrentProcess;
     uStackPointer = oWormProcess.fuGetValueForRegister("$csp", "Get stack pointer");
     uInstructionPointer = oWormProcess.fuGetValueForRegister("$ip", "Get instruction pointer");
     # This is a sanity check: the instruction pointer should be equal to the address at which we set the breakpoint.
     assert uInstructionPointer == oExcessiveCPUUsageDetector.uNextBreakpointAddress, \
         "Expected to hit breakpoint at 0x%X, but got 0x%X instead !?" % (oExcessiveCPUUsageDetector.uNextBreakpointAddress, uInstructionPointer);
     # The code we're expecting to return to may actually be *called* in recursive code. We can detect this by checking
     # if the stackpointer has increased or not. If not, we have not yet returned and will ignore this breakpoint.
     if uStackPointer < oExcessiveCPUUsageDetector.uLastStackPointer:
       oExcessiveCPUUsageDetector.fWormDebugOutput(
         "Ignored bug breakpoint at IP=%p, SP=%p: SP but must be >=%p",
         uInstructionPointer, uStackPointer, oExcessiveCPUUsageDetector.uLastStackPointer
       );
       return;
     if bDebugOutput: print "@@@ Reporting excessive CPU usage bug...";
     oExcessiveCPUUsageDetector.fWormDebugOutput(
       "Bug breakpoint at IP=%p, SP=%p is hit, removing breakpoint and reporting bug...",
       uInstructionPointer, uStackPointer,
     );
     # Remove the breakpoint
     oCdbWrapper.fRemoveBreakpoint(oExcessiveCPUUsageDetector.uBugBreakpointId);
     oExcessiveCPUUsageDetector.uBugBreakpointId = None;
     # Report a bug
     sBugTypeId = "CPUUsage";
     sBugDescription = "The application was using %d%% CPU for %d seconds, which is considered excessive." % \
         (oExcessiveCPUUsageDetector.nTotalCPUUsagePercent, dxConfig["nExcessiveCPUUsageCheckInterval"]);
     sSecurityImpact = None;
     oBugReport = cBugReport.foCreate(oWormProcess, sBugTypeId, sBugDescription, sSecurityImpact);
     oBugReport.bRegistersRelevant = False;
     oBugReport.fReport(oCdbWrapper);
     oCdbWrapper.bFatalBugDetected = True;
   finally:
     oExcessiveCPUUsageDetector.oLock.release();
Ejemplo n.º 4
0
def foDetectAndCreateBugReportForASan(oCdbWrapper, uExceptionCode):
    # Sample ASan outputs
    # |=================================================================
    # |==1796:1960==ERROR: AddressSanitizer: use-after-poison on address 0x0dd092c4 at pc 0x1aaa90d1 bp 0x0103b8dc sp 0x0103b8d0
    # |READ of size 4 at 0x0dd092c4 thread T0
    # |==1796:1960==WARNING: Failed to use and restart external symbolizer!
    # |==1796:1960==*** WARNING: Failed to initialize DbgHelp!              ***
    # |==1796:1960==*** Most likely this means that the app is already      ***
    # |==1796:1960==*** using DbgHelp, possibly with incompatible flags.    ***
    # |==1796:1960==*** Due to technical reasons, symbolization might crash ***
    # |==1796:1960==*** or produce wrong results.                           ***
    # |    #0 0x1aaa90d0 in blink::Element::setAttribute C:\b\c\b\win_asan_release\src\third_party\WebKit\Source\core\dom\Element.cpp:1326
    # |    #1 0x19dc7c9a in blink::V8Element::idAttributeSetterCallback C:\b\c\b\Win_ASan_Release\src\out\Release\gen\blink\bindings\core\v8\V8Element.cpp:2097
    # |
    # |Address 0x0dd092c4 is a wild pointer.
    # |SUMMARY: AddressSanitizer: use-after-poison C:\b\c\b\win_asan_release\src\third_party\WebKit\Source\core\dom\Element.cpp:1326 in blink::Element::setAttribute
    # |Shadow bytes around the buggy address:
    # |  0x31ba1200: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    # |  0x31ba1210: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    # |  0x31ba1220: 00 00 00 00 00 00 04 f7 f7 f7 f7 f7 f7 f7 f7 f7
    # |  0x31ba1230: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
    # |  0x31ba1240: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
    # |=>0x31ba1250: f7 f7 f7 00 00 00 00 04[f7]f7 f7 f7 f7 f7 f7 f7
    # |  0x31ba1260: f7 f7 00 00 00 00 00 00 04 00 00 00 00 04 00 00
    # |  0x31ba1270: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    # |  0x31ba1280: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    # |  0x31ba1290: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    # |  0x31ba12a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    # |Shadow byte legend (one shadow byte represents 8 application bytes):
    # |  Addressable:           00
    # |  Partially addressable: 01 02 03 04 05 06 07
    # |  Heap left redzone:       fa
    # |  Freed heap region:       fd
    # |  Stack left redzone:      f1
    # |  Stack mid redzone:       f2
    # |  Stack right redzone:     f3
    # |  Stack after return:      f5
    # |  Stack use after scope:   f8
    # |  Global redzone:          f9
    # |(704.7a8): Break instruction exception - code 80000003 (first chance)
    # |  Global init order:       f6
    # |  Poisoned by user:        f7
    # |  Container overflow:      fc
    # |  Array cookie:            ac
    # |  Intra object redzone:    bb
    # |  ASan internal:           fe
    # |  Left alloca redzone:     ca
    # |  Right alloca redzone:    cb
    # |==1796:1960==ABORTING
    #############################################################################
    # |=================================================================
    # |==4720:4264==ERROR: AddressSanitizer: global-buffer-overflow on address 0x21a74f3c at pc 0x1601151a bp 0x00efafdc sp 0x00efafd0
    # |READ of size 4 at 0x21a74f3c thread T0
    # |==4720:4264==WARNING: Failed to use and restart external symbolizer!
    # |==4720:4264==*** WARNING: Failed to initialize DbgHelp!              ***
    # |==4720:4264==*** Most likely this means that the app is already      ***
    # |==4720:4264==*** using DbgHelp, possibly with incompatible flags.    ***
    # |==4720:4264==*** Due to technical reasons, symbolization might crash ***
    # |==4720:4264==*** or produce wrong results.                           ***
    # |    #0 0x16011519 in blink::ElementData::Attributes C:\b\c\b\win_asan_release\src\third_party\WebKit\Source\core\dom\ElementData.h:200
    # |    #1 0x1a9a8bcd in blink::Element::setAttribute C:\b\c\b\win_asan_release\src\third_party\WebKit\Source\core\dom\Element.cpp:1322
    # |    #2 0x19cc7c9a in blink::V8Element::idAttributeSetterCallback C:\b\c\b\Win_ASan_Release\src\out\Release\gen\blink\bindings\core\v8\V8Element.cpp:2097
    # |
    # |0x21a74f3c is located 36 bytes to the left of global variable 'WTF::g_global_empty16_bitStorage' defined in '../../third_party/WebKit/Source/platform/wtf/text/StringImpl.cpp:166:1' (0x21a74f60) of size 12
    # |0x21a74f3c is located 16 bytes to the right of global variable 'WTF::g_global_emptyStorage' defined in '../../third_party/WebKit/Source/platform/wtf/text/StringImpl.cpp:165:1' (0x21a74f20) of size 12
    # |SUMMARY: AddressSanitizer: global-buffer-overflow C:\b\c\b\win_asan_release\src\third_party\WebKit\Source\core\dom\ElementData.h:200 in blink::ElementData::Attributes
    # |Shadow bytes around the buggy address:
    # |  0x3434e990: f9 f9 f9 f9 04 f9 f9 f9 f9 f9 f9 f9 04 f9 f9 f9
    # |  0x3434e9a0: f9 f9 f9 f9 00 f9 f9 f9 f9 f9 f9 f9 04 f9 f9 f9
    # |  0x3434e9b0: f9 f9 f9 f9 04 f9 f9 f9 f9 f9 f9 f9 00 f9 f9 f9
    # |  0x3434e9c0: f9 f9 f9 f9 04 f9 f9 f9 f9 f9 f9 f9 00 04 f9 f9
    # |  0x3434e9d0: f9 f9 f9 f9 04 f9 f9 f9 f9 f9 f9 f9 04 f9 f9 f9
    # |=>0x3434e9e0: f9 f9 f9 f9 00 04 f9[f9]f9 f9 f9 f9 00 04 f9 f9
    # |  0x3434e9f0: f9 f9 f9 f9 00 00 f9 f9 f9 f9 f9 f9 04 f9 f9 f9
    # |  0x3434ea00: f9 f9 f9 f9 04 f9 f9 f9 f9 f9 f9 f9 04 f9 f9 f9
    # |  0x3434ea10: f9 f9 f9 f9 04 f9 f9 f9 f9 f9 f9 f9 04 f9 f9 f9
    # |  0x3434ea20: f9 f9 f9 f9 04 f9 f9 f9 f9 f9 f9 f9 04 f9 f9 f9
    # |  0x3434ea30: f9 f9 f9 f9 04 f9 f9 f9 f9 f9 f9 f9 04 f9 f9 f9
    # |Shadow byte legend (one shadow byte represents 8 application bytes):
    # |  Addressable:           00
    # |  Partially addressable: 01 02 03 04 05 06 07
    # |  Heap left redzone:       fa
    # |  Freed heap region:       fd
    # |  Stack left redzone:      f1
    # |  Stack mid redzone:       f2
    # |  Stack right redzone:     f3
    # |  Stack after return:      f5
    # |  Stack use after scope:   f8
    # |  Global redzone:          f9
    # |  Global init order:       f6
    # |  Poisoned by user:        f7
    # |  Container overflow:      fc
    # |  Array cookie:            ac
    # |  Intra object redzone:    bb
    # |  ASan internal:           fe
    # |  Left alloca redzone:     ca
    # |  Right alloca redzone:    cb
    # |==4720:4264==ABORTING
    ##############################################################################
    # |=================================================================
    # |==3012:3984==ERROR: AddressSanitizer: heap-use-after-free on address 0x0636d770 at pc 0x14e45df9 bp 0x00c9a7c8 sp 0x00c9a7bc
    # |READ of size 1 at 0x0636d770 thread T0
    # |    #0 0x14e45df8 in std::_Hash<std::_Uset_traits<device::BluetoothAdapter::Observer *,std::_Uhash_compare<device::BluetoothAdapter::Observer *,std::hash<device::BluetoothAdapter::Observer *>,std::equal_to<device::BluetoothAdapter::Observer *> >,std::allocator<device::BluetoothAdapter::Observer *>,0> >::equal_range c:\b\c\win_toolchain\vs_files\f53e4598951162bad6330f7a167486c7ae5db1e5\vc\include\xhash:636
    # |    #1 0x15142422 in std::_Hash<std::_Umap_traits<int,ui::AXPlatformNode *,std::_Uhash_compare<int,base_hash::hash<int>,std::equal_to<int> >,std::allocator<std::pair<const int,ui::AXPlatformNode *> >,0> >::erase c:\b\c\win_toolchain\vs_files\f53e4598951162bad6330f7a167486c7ae5db1e5\vc\include\xhash:563
    # |    #2 0x15147750 in ui::AXPlatformNodeWin::Destroy C:\b\c\b\win_asan_release\src\ui\accessibility\platform\ax_platform_node_win.cc:556
    # |    #3 0x10529055 in content::BrowserAccessibilityWin::~BrowserAccessibilityWin C:\b\c\b\win_asan_release\src\content\browser\accessibility\browser_accessibility_win.h:29
    # |    #4 0x104c9fba in content::BrowserAccessibility::NativeReleaseReference C:\b\c\b\win_asan_release\src\content\browser\accessibility\browser_accessibility.cc:556
    # |    #5 0x1051014c in content::BrowserAccessibilityManager::OnNodeWillBeDeleted C:\b\c\b\win_asan_release\src\content\browser\accessibility\browser_accessibility_manager.cc:1134
    # |    #6 0x15116137 in ui::AXTree::DestroyNodeAndSubtree C:\b\c\b\win_asan_release\src\ui\accessibility\ax_tree.cc:501
    # |    #7 0x15119afb in ui::AXTree::DestroySubtree C:\b\c\b\win_asan_release\src\ui\accessibility\ax_tree.cc:494
    # |    #8 0x1511a03b in ui::AXTree::UpdateNode C:\b\c\b\win_asan_release\src\ui\accessibility\ax_tree.cc:406
    # |    #9 0x15117cb9 in ui::AXTree::Unserialize C:\b\c\b\win_asan_release\src\ui\accessibility\ax_tree.cc:258
    # |    #10 0x1050c39b in content::BrowserAccessibilityManager::OnAccessibilityEvents C:\b\c\b\win_asan_release\src\content\browser\accessibility\browser_accessibility_manager.cc:364
    # |    #11 0x10b436b5 in content::RenderFrameHostImpl::OnAccessibilityEvents C:\b\c\b\win_asan_release\src\content\browser\frame_host\render_frame_host_impl.cc:2388
    # |    #12 0x10b427af in IPC::MessageT<AccessibilityHostMsg_Events_Meta,std::tuple<std::vector<AccessibilityHostMsg_EventParams,std::allocator<AccessibilityHostMsg_EventParams> >,int,int>,void>::Dispatch<content::RenderFrameHostImpl,content::RenderFrameHostImpl,void,void (content::RenderFrameHostImpl::*)(const std::vector<AccessibilityHostMsg_EventParams,std::allocator<AccessibilityHostMsg_EventParams> > &, int, int) __attribute__((thiscall))> C:\b\c\b\win_asan_release\src\ipc\ipc_message_templates.h:120
    # |    #13 0x10b23853 in content::RenderFrameHostImpl::OnMessageReceived C:\b\c\b\win_asan_release\src\content\browser\frame_host\render_frame_host_impl.cc:890
    # |    #14 0x11183345 in content::RenderProcessHostImpl::OnMessageReceived C:\b\c\b\win_asan_release\src\content\browser\renderer_host\render_process_host_impl.cc:2831
    # |    #15 0x145f0109 in IPC::ChannelProxy::Context::OnDispatchMessage C:\b\c\b\win_asan_release\src\ipc\ipc_channel_proxy.cc:329
    # |    #16 0x1153d5fe in base::internal::Invoker<base::internal::BindState<base::internal::IgnoreResultHelper<bool (content::UtilityProcessHostClient::*)(const IPC::Message &) __attribute__((thiscall))>,scoped_refptr<content::UtilityProcessHostClient>,IPC::Message>,void ()>::Run C:\b\c\b\win_asan_release\src\base\bind_internal.h:317
    # |    #17 0x1291b01a in base::debug::TaskAnnotator::RunTask C:\b\c\b\win_asan_release\src\base\debug\task_annotator.cc:57
    # |    #18 0x12762259 in base::MessageLoop::RunTask C:\b\c\b\win_asan_release\src\base\message_loop\message_loop.cc:422
    # |    #19 0x1276363b in base::MessageLoop::DeferOrRunPendingTask C:\b\c\b\win_asan_release\src\base\message_loop\message_loop.cc:433
    # |    #20 0x127642f3 in base::MessageLoop::DoWork C:\b\c\b\win_asan_release\src\base\message_loop\message_loop.cc:540
    # |    #21 0x12920256 in base::MessagePumpForUI::DoRunLoop C:\b\c\b\win_asan_release\src\base\message_loop\message_pump_win.cc:173
    # |    #22 0x1291f155 in base::MessagePumpWin::Run C:\b\c\b\win_asan_release\src\base\message_loop\message_pump_win.cc:56
    # |    #23 0x12760ec4 in base::MessageLoop::Run C:\b\c\b\win_asan_release\src\base\message_loop\message_loop.cc:369
    # |    #24 0x1283d5ed in base::RunLoop::Run C:\b\c\b\win_asan_release\src\base\run_loop.cc:111
    # |    #25 0x123ef3d1 in ChromeBrowserMainParts::MainMessageLoopRun C:\b\c\b\win_asan_release\src\chrome\browser\chrome_browser_main.cc:1967
    # |    #26 0x107205e4 in content::BrowserMainLoop::RunMainMessageLoopParts C:\b\c\b\win_asan_release\src\content\browser\browser_main_loop.cc:1170
    # |    #27 0x10729dfd in content::BrowserMainRunnerImpl::Run C:\b\c\b\win_asan_release\src\content\browser\browser_main_runner.cc:142
    # |    #28 0x107133b9 in content::BrowserMain C:\b\c\b\win_asan_release\src\content\browser\browser_main.cc:46
    # |    #29 0x1211d5d6 in content::RunNamedProcessTypeMain C:\b\c\b\win_asan_release\src\content\app\content_main_runner.cc:408
    # |    #30 0x1211e95d in content::ContentMainRunnerImpl::Run C:\b\c\b\win_asan_release\src\content\app\content_main_runner.cc:687
    # |    #31 0x12189d05 in service_manager::Main C:\b\c\b\win_asan_release\src\services\service_manager\embedder\main.cc:469
    # |    #32 0x1211d2cc in content::ContentMain C:\b\c\b\win_asan_release\src\content\app\content_main.cc:19
    # |    #33 0xf961320 in ChromeMain C:\b\c\b\win_asan_release\src\chrome\app\chrome_main.cc:139
    # |    #34 0xdb9f24 in MainDllLoader::Launch C:\b\c\b\win_asan_release\src\chrome\app\main_dll_loader_win.cc:199
    # |    #35 0xdb1c53 in main C:\b\c\b\win_asan_release\src\chrome\app\chrome_exe_main_win.cc:268
    # |    #36 0x111f72a in __scrt_common_main_seh f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl:253
    # |    #37 0x74b39ba3 in BaseThreadInitThunk+0x23 (C:\Windows\System32\KERNEL32.DLL+0x68919ba3)
    # |    #38 0x76fcac9a in RtlCheckRegistryKey+0xfba (C:\Windows\SYSTEM32\ntdll.dll+0x6a26ac9a)
    # |    #39 0x76fcac6e in RtlCheckRegistryKey+0xf8e (C:\Windows\SYSTEM32\ntdll.dll+0x6a26ac6e)
    # |
    # |0x0636d770 is located 64 bytes inside of 140-byte region [0x0636d730,0x0636d7bc)
    # |freed by thread T0 here:
    # |    #0 0x110e378 in free e:\b\build\slave\win_upload_clang\build\src\third_party\llvm\projects\compiler-rt\lib\asan\asan_malloc_win.cc:44
    # |    #1 0x105296f9 in ATL::CComObject<content::BrowserAccessibilityComWin>::`vector deleting destructor' c:\b\c\win_toolchain\vs_files\f53e4598951162bad6330f7a167486c7ae5db1e5\vc\atlmfc\include\atlcom.h
    # |    #2 0x1515b6fb in ATL::CComObject<ui::AXPlatformNodeWin>::Release c:\b\c\win_toolchain\vs_files\f53e4598951162bad6330f7a167486c7ae5db1e5\vc\atlmfc\include\atlcom.h:2934
    # |    #3 0x1514765e in ui::AXPlatformNodeWin::Dispose C:\b\c\b\win_asan_release\src\ui\accessibility\platform\ax_platform_node_win.cc:550
    # |    #4 0x15147707 in ui::AXPlatformNodeWin::Destroy C:\b\c\b\win_asan_release\src\ui\accessibility\platform\ax_platform_node_win.cc:555
    # |    #5 0x10529055 in content::BrowserAccessibilityWin::~BrowserAccessibilityWin C:\b\c\b\win_asan_release\src\content\browser\accessibility\browser_accessibility_win.h:29
    # |    #6 0x104c9fba in content::BrowserAccessibility::NativeReleaseReference C:\b\c\b\win_asan_release\src\content\browser\accessibility\browser_accessibility.cc:556
    # |    #7 0x1051014c in content::BrowserAccessibilityManager::OnNodeWillBeDeleted C:\b\c\b\win_asan_release\src\content\browser\accessibility\browser_accessibility_manager.cc:1134
    # |    #8 0x15116137 in ui::AXTree::DestroyNodeAndSubtree C:\b\c\b\win_asan_release\src\ui\accessibility\ax_tree.cc:501
    # |    #9 0x15119afb in ui::AXTree::DestroySubtree C:\b\c\b\win_asan_release\src\ui\accessibility\ax_tree.cc:494
    # |    #10 0x1511a03b in ui::AXTree::UpdateNode C:\b\c\b\win_asan_release\src\ui\accessibility\ax_tree.cc:406
    # |    #11 0x15117cb9 in ui::AXTree::Unserialize C:\b\c\b\win_asan_release\src\ui\accessibility\ax_tree.cc:258
    # |    #12 0x1050c39b in content::BrowserAccessibilityManager::OnAccessibilityEvents C:\b\c\b\win_asan_release\src\content\browser\accessibility\browser_accessibility_manager.cc:364
    # |    #13 0x10b436b5 in content::RenderFrameHostImpl::OnAccessibilityEvents C:\b\c\b\win_asan_release\src\content\browser\frame_host\render_frame_host_impl.cc:2388
    # |    #14 0x10b427af in IPC::MessageT<AccessibilityHostMsg_Events_Meta,std::tuple<std::vector<AccessibilityHostMsg_EventParams,std::allocator<AccessibilityHostMsg_EventParams> >,int,int>,void>::Dispatch<content::RenderFrameHostImpl,content::RenderFrameHostImpl,void,void (content::RenderFrameHostImpl::*)(const std::vector<AccessibilityHostMsg_EventParams,std::allocator<AccessibilityHostMsg_EventParams> > &, int, int) __attribute__((thiscall))> C:\b\c\b\win_asan_release\src\ipc\ipc_message_templates.h:120
    # |    #15 0x10b23853 in content::RenderFrameHostImpl::OnMessageReceived C:\b\c\b\win_asan_release\src\content\browser\frame_host\render_frame_host_impl.cc:890
    # |    #16 0x11183345 in content::RenderProcessHostImpl::OnMessageReceived C:\b\c\b\win_asan_release\src\content\browser\renderer_host\render_process_host_impl.cc:2831
    # |    #17 0x145f0109 in IPC::ChannelProxy::Context::OnDispatchMessage C:\b\c\b\win_asan_release\src\ipc\ipc_channel_proxy.cc:329
    # |    #18 0x1153d5fe in base::internal::Invoker<base::internal::BindState<base::internal::IgnoreResultHelper<bool (content::UtilityProcessHostClient::*)(const IPC::Message &) __attribute__((thiscall))>,scoped_refptr<content::UtilityProcessHostClient>,IPC::Message>,void ()>::Run C:\b\c\b\win_asan_release\src\base\bind_internal.h:317
    # |    #19 0x1291b01a in base::debug::TaskAnnotator::RunTask C:\b\c\b\win_asan_release\src\base\debug\task_annotator.cc:57
    # |    #20 0x12762259 in base::MessageLoop::RunTask C:\b\c\b\win_asan_release\src\base\message_loop\message_loop.cc:422
    # |    #21 0x1276363b in base::MessageLoop::DeferOrRunPendingTask C:\b\c\b\win_asan_release\src\base\message_loop\message_loop.cc:433
    # |    #22 0x127642f3 in base::MessageLoop::DoWork C:\b\c\b\win_asan_release\src\base\message_loop\message_loop.cc:540
    # |    #23 0x12920256 in base::MessagePumpForUI::DoRunLoop C:\b\c\b\win_asan_release\src\base\message_loop\message_pump_win.cc:173
    # |    #24 0x1291f155 in base::MessagePumpWin::Run C:\b\c\b\win_asan_release\src\base\message_loop\message_pump_win.cc:56
    # |    #25 0x12760ec4 in base::MessageLoop::Run C:\b\c\b\win_asan_release\src\base\message_loop\message_loop.cc:369
    # |    #26 0x1283d5ed in base::RunLoop::Run C:\b\c\b\win_asan_release\src\base\run_loop.cc:111
    # |    #27 0x123ef3d1 in ChromeBrowserMainParts::MainMessageLoopRun C:\b\c\b\win_asan_release\src\chrome\browser\chrome_browser_main.cc:1967
    # |    #28 0x107205e4 in content::BrowserMainLoop::RunMainMessageLoopParts C:\b\c\b\win_asan_release\src\content\browser\browser_main_loop.cc:1170
    # |
    # |previously allocated by thread T0 here:
    # |    #0 0x110e45c in malloc e:\b\build\slave\win_upload_clang\build\src\third_party\llvm\projects\compiler-rt\lib\asan\asan_malloc_win.cc:60
    # |    #1 0x19f475bb in operator new f:\dd\vctools\crt\vcstartup\src\heap\new_scalar.cpp:19
    # |    #2 0x19f47cae in operator new f:\dd\vctools\crt\vcstartup\src\heap\new_scalar_nothrow.cpp:17
    # |    #3 0x10528bf2 in ATL::CComObject<content::BrowserAccessibilityComWin>::CreateInstance c:\b\c\win_toolchain\vs_files\f53e4598951162bad6330f7a167486c7ae5db1e5\vc\atlmfc\include\atlcom.h:2966
    # |    #4 0x10528a2f in content::BrowserAccessibilityWin::BrowserAccessibilityWin C:\b\c\b\win_asan_release\src\content\browser\accessibility\browser_accessibility_win.cc:19
    # |    #5 0x10528962 in content::BrowserAccessibility::Create C:\b\c\b\win_asan_release\src\content\browser\accessibility\browser_accessibility_win.cc:14
    # |    #6 0x10510613 in content::BrowserAccessibilityManager::OnNodeCreated C:\b\c\b\win_asan_release\src\content\browser\accessibility\browser_accessibility_manager.cc:1166
    # |    #7 0x105261e3 in content::BrowserAccessibilityManagerWin::OnNodeCreated C:\b\c\b\win_asan_release\src\content\browser\accessibility\browser_accessibility_manager_win.cc:249
    # |    #8 0x1511ac76 in ui::AXTree::CreateNode C:\b\c\b\win_asan_release\src\ui\accessibility\ax_tree.cc:330
    # |    #9 0x15119dda in ui::AXTree::UpdateNode C:\b\c\b\win_asan_release\src\ui\accessibility\ax_tree.cc:360
    # |    #10 0x15117cb9 in ui::AXTree::Unserialize C:\b\c\b\win_asan_release\src\ui\accessibility\ax_tree.cc:258
    # |    #11 0x1050a873 in content::BrowserAccessibilityManager::Initialize C:\b\c\b\win_asan_release\src\content\browser\accessibility\browser_accessibility_manager.cc:177
    # |    #12 0x105250e4 in content::BrowserAccessibilityManagerWin::BrowserAccessibilityManagerWin C:\b\c\b\win_asan_release\src\content\browser\accessibility\browser_accessibility_manager_win.cc:44
    # |    #13 0x11234704 in content::RenderWidgetHostViewAura::CreateBrowserAccessibilityManager C:\b\c\b\win_asan_release\src\content\browser\renderer_host\render_widget_host_view_aura.cc:1144
    # |    #14 0x10b5a82a in content::RenderFrameHostImpl::GetOrCreateBrowserAccessibilityManager C:\b\c\b\win_asan_release\src\content\browser\frame_host\render_frame_host_impl.cc:3595
    # |(bc4.f90): Break instruction exception - code 80000003 (first chance)
    # |    #15 0x10b42c8a in content::RenderFrameHostImpl::OnAccessibilityEvents C:\b\c\b\win_asan_release\src\content\browser\frame_host\render_frame_host_impl.cc:2360
    # |    #16 0x10b427af in IPC::MessageT<AccessibilityHostMsg_Events_Meta,std::tuple<std::vector<AccessibilityHostMsg_EventParams,std::allocator<AccessibilityHostMsg_EventParams> >,int,int>,void>::Dispatch<content::RenderFrameHostImpl,content::RenderFrameHostImpl,void,void (content::RenderFrameHostImpl::*)(const std::vector<AccessibilityHostMsg_EventParams,std::allocator<AccessibilityHostMsg_EventParams> > &, int, int) __attribute__((thiscall))> C:\b\c\b\win_asan_release\src\ipc\ipc_message_templates.h:120
    # |    #17 0x10b23853 in content::RenderFrameHostImpl::OnMessageReceived C:\b\c\b\win_asan_release\src\content\browser\frame_host\render_frame_host_impl.cc:890
    # |    #18 0x11183345 in content::RenderProcessHostImpl::OnMessageReceived C:\b\c\b\win_asan_release\src\content\browser\renderer_host\render_process_host_impl.cc:2831
    # |    #19 0x145f0109 in IPC::ChannelProxy::Context::OnDispatchMessage C:\b\c\b\win_asan_release\src\ipc\ipc_channel_proxy.cc:329
    # |    #20 0x1153d5fe in base::internal::Invoker<base::internal::BindState<base::internal::IgnoreResultHelper<bool (content::UtilityProcessHostClient::*)(const IPC::Message &) __attribute__((thiscall))>,scoped_refptr<content::UtilityProcessHostClient>,IPC::Message>,void ()>::Run C:\b\c\b\win_asan_release\src\base\bind_internal.h:317
    # |    #21 0x1291b01a in base::debug::TaskAnnotator::RunTask C:\b\c\b\win_asan_release\src\base\debug\task_annotator.cc:57
    # |    #22 0x12762259 in base::MessageLoop::RunTask C:\b\c\b\win_asan_release\src\base\message_loop\message_loop.cc:422
    # |    #23 0x1276363b in base::MessageLoop::DeferOrRunPendingTask C:\b\c\b\win_asan_release\src\base\message_loop\message_loop.cc:433
    # |    #24 0x127642f3 in base::MessageLoop::DoWork C:\b\c\b\win_asan_release\src\base\message_loop\message_loop.cc:540
    # |    #25 0x12920256 in base::MessagePumpForUI::DoRunLoop C:\b\c\b\win_asan_release\src\base\message_loop\message_pump_win.cc:173
    # |    #26 0x1291f155 in base::MessagePumpWin::Run C:\b\c\b\win_asan_release\src\base\message_loop\message_pump_win.cc:56
    # |    #27 0x12760ec4 in base::MessageLoop::Run C:\b\c\b\win_asan_release\src\base\message_loop\message_loop.cc:369
    # |    #28 0x1283d5ed in base::RunLoop::Run C:\b\c\b\win_asan_release\src\base\run_loop.cc:111
    # |
    # |SUMMARY: AddressSanitizer: heap-use-after-free c:\b\c\win_toolchain\vs_files\f53e4598951162bad6330f7a167486c7ae5db1e5\vc\include\xhash:636 in std::_Hash<std::_Uset_traits<device::BluetoothAdapter::Observer *,std::_Uhash_compare<device::BluetoothAdapter::Observer *,std::hash<device::BluetoothAdapter::Observer *>,std::equal_to<device::BluetoothAdapter::Observer *> >,std::allocator<device::BluetoothAdapter::Observer *>,0> >::equal_range
    # |Shadow bytes around the buggy address:
    # |  0x30c6da90: fa fa fa fa fa fa fa fa 00 00 00 00 00 00 00 00
    # |  0x30c6daa0: 00 00 00 00 00 00 00 00 00 04 fa fa fa fa fa fa
    # |  0x30c6dab0: fa fa fd fd fd fd fd fd fd fd fd fd fd fd fd fd
    # |  0x30c6dac0: fd fd fd fd fa fa fa fa fa fa fa fa 00 00 00 00
    # |  0x30c6dad0: 00 00 00 00 00 00 00 00 00 00 00 00 00 04 fa fa
    # |=>0x30c6dae0: fa fa fa fa fa fa fd fd fd fd fd fd fd fd[fd]fd
    # |  0x30c6daf0: fd fd fd fd fd fd fd fd fa fa fa fa fa fa fa fa
    # |  0x30c6db00: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
    # |  0x30c6db10: fd fd fa fa fa fa fa fa fa fa fd fd fd fd fd fd
    # |  0x30c6db20: fd fd fd fd fd fd fd fd fd fd fd fd fa fa fa fa
    # |  0x30c6db30: fa fa fa fa 00 00 00 00 00 00 00 00 00 00 00 00
    # |Shadow byte legend (one shadow byte represents 8 application bytes):
    # |  Addressable:           00
    # |  Partially addressable: 01 02 03 04 05 06 07
    # |  Heap left redzone:       fa
    # |  Freed heap region:       fd
    # |  Stack left redzone:      f1
    # |  Stack mid redzone:       f2
    # |  Stack right redzone:     f3
    # |  Stack after return:      f5
    # |  Stack use after scope:   f8
    # |  Global redzone:          f9
    # |  Global init order:       f6
    # |  Poisoned by user:        f7
    # |  Container overflow:      fc
    # |  Array cookie:            ac
    # |  Intra object redzone:    bb
    # |  ASan internal:           fe
    # |  Left alloca redzone:     ca
    # |  Right alloca redzone:    cb
    # |==3012:3984==ABORTING
    asAsanBugReport = []
    uMemoryDumpStartAddress = None
    uMemoryDumpEndAddress = None
    atxMemoryRemarks = []
    for sLine in oCdbWrapper.asStdErrOutput:
        if not asAsanBugReport:
            oSummaryMatch = re.match(
                r"^==(\d+):(\d+)==ERROR: AddressSanitizer: (.*) on address 0x([0-9`a-f]+) at pc 0x([0-9`a-f]+) bp 0x([0-9`a-f]+) sp 0x([0-9`a-f]+)$",
                sLine, re.I)
            if not oSummaryMatch:
                continue
            sProcessId, sThreadId, sASanBugType, sAddressHex, sIPHex, sBPHex, sSPHex = oSummaryMatch.groups(
            )
            uProcessId = long(sProcessId)
            uThreadId = long(sThreadId)
            uProblemAddress = long(sAddressHex, 16)
            atxMemoryRemarks.append(
                ("Address for which ASan reported this problem",
                 uProblemAddress, None))
        elif sLine == "==%d:%d==ABORTING" % (uProcessId, uThreadId):
            break
        else:
            oHeapInfoMatch = re.match(
                r"0x([0-9`a-f]+) is located (\d+) bytes (?:inside|to the (?:left|right)) of (\d+)\-byte region \[0x([0-9`a-f]+),0x([0-9`a-f]+)\)",
                sLine, re.I)
            if oHeapInfoMatch:
                sAddressHex, sOffset, sBlockSize, sBlockStartAddressHex, sBlockEndAddressHex = oHeapInfoMatch.groups(
                )
                assert uProblemAddress == long(sAddressHex, 16), \
                    "Problem reported at address 0x%X, but information provided for address 0x%s" % (uProblemAddress, sAddressHex)
                uBlockStartAddress = long(sBlockStartAddressHex, 16)
                uBlockEndAddress = long(sBlockEndAddressHex, 16)
                uBlockSize = long(sBlockSize)
                assert uBlockEndAddress - uBlockStartAddress == uBlockSize, \
                    "The memory block start (0x%X) and end (0x%X) address suggest a size (0x%X) that do not agree with the reported size (0x%X)" % \
                    (uBlockStartAddress, uBlockEndAddress, uBlockEndAddress - uBlockStartAddress, uBlockSize)
                if uMemoryDumpStartAddress is None or uBlockStartAddress < uMemoryDumpStartAddress:
                    uMemoryDumpStartAddress = uBlockStartAddress
                if uMemoryDumpEndAddress is None or uBlockEndAddress > uMemoryDumpEndAddress:
                    uMemoryDumpEndAddress = uBlockEndAddress
                atxMemoryRemarks.append(("Memory block according to ASan",
                                         uBlockStartAddress, uBlockSize), )
            oHeapInfoMatch = re.match(
                r"0x([0-9`a-f]+) is located (\d+) bytes (?:inside|to the (?:left|right)) of global variable '(.+)' defined in '.+' \(0x([0-9`a-f]+)\) of size (\d+)",
                sLine, re.I)
            if oHeapInfoMatch:
                sAddressHex, sOffset, sVariableName, sVariableStartAddressHex, sVariableSize = oHeapInfoMatch.groups(
                )
                assert uProblemAddress == long(sAddressHex, 16), \
                    "Problem reported at address 0x%X, but information provided for address 0x%s" % (uProblemAddress, sAddressHex)
                uVariableStartAddress = long(sVariableStartAddressHex, 16)
                uVariableSize = long(sVariableSize)
                if uMemoryDumpStartAddress is None or uVariableStartAddress < uMemoryDumpStartAddress:
                    uMemoryDumpStartAddress = uVariableStartAddress
                if uMemoryDumpEndAddress is None or uVariableStartAddress + uVariableSize > uMemoryDumpEndAddress:
                    uMemoryDumpEndAddress = uVariableStartAddress + uVariableSize
                atxMemoryRemarks.append(
                    ("global variable %s" % sVariableName,
                     uMemoryDumpStartAddress, uVariableSize), )
            oHeapInfoMatch = re.match(
                r"(READ|WRITE) of size (\d+) at 0x([0-9`a-f]+) thread T\d+",
                sLine, re.I)
            if oHeapInfoMatch:
                sAction, sSize, sAddressHex = oHeapInfoMatch.groups()
                uSize = long(sSize)
                uAddress = long(sAddressHex, 16)
                atxMemoryRemarks.append(
                    ("Attempt to %s %d bytes from 0x%X" %
                     (sAction.lower(), uSize, uAddress), uAddress, uSize), )
        asAsanBugReport.append(sLine)
    else:
        if asAsanBugReport:
            # We found the start of a bug report, but not the end. It may be the application was interrupted while ASan was
            # busy collecting and outputting information. We will ignore this exception, as there should be another one when
            # ASan is finished, at which point all relevant information is available for us in  our report.
            return
        # We did not find a bug report.
        return
    oCdbWrapper.fSelectProcess(uProcessId)
    oProcess = oCdbWrapper.oCurrentProcess
    sBugTypeId = "ASan:%s" % sASanBugType
    sBugDescription = "AddressSanitizer reported a %s on address 0x%X." % (
        sASanBugType, uProblemAddress)
    sSecurityImpact = dsSecurityImpact_by_sASanBugType.get(
        sASanBugType, "Unknown: this type of bug has not been analyzed before")
    oHeapManagerData = oProcess.foGetHeapManagerDataForAddress(uProblemAddress)
    if oHeapManagerData:
        atxMemoryRemarks.extend(oHeapManagerData.fatxMemoryRemarks())
        # Make sure entire page heap block is included in the memory dump.
        if oHeapManagerData.uMemoryDumpStartAddress < uMemoryDumpStartAddress:
            uMemoryDumpStartAddress = oHeapManagerData.uMemoryDumpStartAddress
            assert uMemoryDumpEndAddress - uMemoryDumpStartAddress == uMemoryDumpSize, \
                "Something, somewhere went wrong because 0x%X - 0x%X == 0x%X and not 0x%X" % \
                (uMemoryDumpEndAddress, uMemoryDumpStartAddress, uMemoryDumpEndAddress - uMemoryDumpStartAddress, uMemoryDumpSize)
        if oHeapManagerData.uMemoryDumpEndAddress > uMemoryDumpEndAddress:
            uMemoryDumpEndAddress = ooHeapManagerData.uMemoryDumpEndAddress

    oBugReport = cBugReport.foCreate(oProcess, sBugTypeId, sBugDescription,
                                     sSecurityImpact)
    if oCdbWrapper.bGenerateReportHTML:
        if uMemoryDumpStartAddress is None:
            # We know nothing about the layout of the memory region for which the problem was reported, but we do want to
            # dump it in the report, so we will output a region of a size that will hopefully be useful, but not too large
            # so as to bloat the report with irrelevant data.
            uMemoryDumpStartAddress = uProblemAddress - 0x100
            uMemoryDumpSize = 0x200
        else:
            # Make sure the problem address is in the memory dump
            if uMemoryDumpStartAddress > uProblemAddress:
                uMemoryDumpStartAddress = uProblemAddress
            elif uMemoryDumpEndAddress < uProblemAddress:
                uMemoryDumpEndAddress = uProblemAddress
            uMemoryDumpStartAddress, uMemoryDumpSize = ftuLimitedAndAlignedMemoryDumpStartAddressAndSize(
                uProblemAddress,
                oProcess.uPointerSize,
                uMemoryDumpStartAddress,
                uMemoryDumpEndAddress - uMemoryDumpStartAddress,
            )
        # Dump memory
        oBugReport.fAddMemoryDump(
            uMemoryDumpStartAddress,
            uMemoryDumpStartAddress + uMemoryDumpSize,
            "Memory near heap block at 0x%X" % uMemoryDumpStartAddress,
        )
        for (sDescription, uAddress, uSize) in atxMemoryRemarks:
            print "$$$ 0x%08X+%s %s" % (uAddress, uSize is None and "????"
                                        or "%04X" % uSize, sDescription)
        oBugReport.atxMemoryRemarks.extend(atxMemoryRemarks)
        # Add ASan output to file
        sASanOutputHTML = sBlockHTMLTemplate % {
            "sName":
            "ASan bug report",
            "sCollapsed":
            "Collapsed",
            "sContent":
            "<pre>%s</pre>" % "\r\n".join([
                oCdbWrapper.fsHTMLEncode(s, uTabStop=8)
                for s in asAsanBugReport
            ])
        }
        oBugReport.asExceptionSpecificBlocksHTML.append(sASanOutputHTML)
    return oBugReport
Ejemplo n.º 5
0
def foDetectAndCreateBugReportForVERIFIER_STOP(oCdbWrapper, uExceptionCode,
                                               asCdbOutput):
    if uExceptionCode not in [STATUS_BREAKPOINT, STATUS_WX86_BREAKPOINT]:
        return None
        # Not a VERIFIER STOP
    uErrorNumber = None
    uProcessId = None
    sMessage = None
    uVerifierStopHeapBlockAddress = None
    uVerifierStopHeapBlockSize = None
    uVerifierStopHeapBlockHandle = None
    uVerifierStopHeapHandle = None
    # This is what is being used, which may differ from the actual heap handle of the block
    uCorruptedStamp = None
    uCorruptionAddress = None
    asRelevantLines = []

    for sLine in asCdbOutput:
        # Ignore exmpty lines
        if not sLine:
            continue
        # Look for the first VERIFIER STOP message and gather information
        if uErrorNumber is None:
            oErrorMessageMatch = re.match(
                r"^VERIFIER STOP ([0-9A-F]+): pid 0x([0-9A-F]+): (.*?)\s*$",
                sLine)
            if oErrorMessageMatch:
                sErrorNumber, sProcessId, sMessage = oErrorMessageMatch.groups(
                )
                uErrorNumber = long(sErrorNumber, 16)
                uProcessId = long(sProcessId, 16)
                asRelevantLines.append(sLine)
                oCdbWrapper.fSelectProcess(uProcessId)
                assert uProcessId == oCdbWrapper.oCurrentProcess.uId, \
                    "VERIFIER STOP in process with id %d/0x%X, but current process id is %d/0x%X" % \
                    (uProcessId, uProcessId, oCdbWrapper.oCurrentProcess.uId, oCdbWrapper.oCurrentProcess.uId)
            continue
        # A VERIFIER STOP message has been detected, gather what information verifier provides:
        oInformationMatch = re.match(r"^\t([0-9A-F]+) : (.*?)\s*$", sLine)
        if oInformationMatch:
            asRelevantLines.append(sLine)
            sValue, sDescription = oInformationMatch.groups()
            uValue = long(sValue, 16)
            sDescription = sDescription.lower()
            # Both "Corruption address" and "corruption address" are used :(
            if sDescription == "heap block":
                uVerifierStopHeapBlockAddress = uValue
            elif sDescription == "block size":
                uVerifierStopHeapBlockSize = uValue
            elif sDescription == "corrupted stamp":
                uCorruptedStamp = uValue
            elif sDescription == "corruption address":
                uCorruptionAddress = uValue
            elif sDescription == "corruption address":
                uCorruptionAddress = uValue
            elif sDescription == "heap handle":
                uVerifierStopHeapBlockHandle = uValue
                uVerifierStopHeapHandle = uValue
            elif sDescription == "heap used in the call":
                uVerifierStopHeapHandle = uValue
            elif sDescription == "heap owning the block":
                uVerifierStopHeapBlockHandle = uValue
        else:
            assert sLine.strip().replace("=", "") == "", \
                "Unknown VERIFIER STOP message line: %s\r\n%s" % (repr(sLine), "\r\n".join(asCdbOutput))
            break
    else:
        assert uErrorNumber is None, \
            "Detected the start of a VERIFIER STOP message but not the end\r\n%s" % "\r\n".join(asCdbOutput)
        return None
        # No VERIFIER STOP in the output.
    if uErrorNumber == 0x303:
        # |=======================================
        # |VERIFIER STOP 0000000000000303: pid 0xB2C: NULL handle passed as parameter. A valid handle must be used.
        # |
        # |0000000000000000 : Not used.
        # |0000000000000000 : Not used.
        # |0000000000000000 : Not used.
        # |0000000000000000 : Not used.
        # |
        # |=======================================
        return None
        # This is a VERIFIER STOP, but not an interesting one and we can continue the application.

    assert uVerifierStopHeapBlockAddress is not None, \
        "The heap block start address was not found in the verifier stop message.\r\n%s" % "\r\n".join(asRelevantLines)
    assert uVerifierStopHeapBlockSize is not None, \
        "The heap block size was not found in the verifier stop message.\r\n%s" % "\r\n".join(asRelevantLines)

    if sMessage == "block already freed":
        # It appears there is a bug in verifier that causes it to report the wrong address: rather than the address
        # of the block that was already freed, it reports the address of some struct. Luckily it appears that this struct
        # does contain the address of the heap block:
        #   PVOID pUnknown_0
        #   PVOID pUnknown_1
        #   PVOID pUnknown_2
        #   PVOID pUnknown_3
        #   PVOID pHeapBlock;  // A pointer to the start of the heap block.
        #   PVOID pVitualAllocation;  // A pointer to the virtual allocation that contains the heap block and page heap data.
        #   ULONG uVirtualAllocationSize;  // The size of the virtual allocations for the heap block and guard page.
        #   PVOID pUnknown_7;
        #   ULONG uHeapBlockSize; // The size of the heap block
        # This means we can read the address of the heap block by reading a pointer sized value at offset 4 * sizeof(PVOID).
        puHeapBlockAddress = uVerifierStopHeapBlockAddress + 4 * oCdbWrapper.oCurrentProcess.uPointerSize
        uHeapBlockAddress = oCdbWrapper.oCurrentProcess.fuGetValue(
            "poi(0x%X)" % puHeapBlockAddress,
            "Get heap block address from verifier data")
    else:
        # In all other cases, we trust verifier to provide the correct value:
        uHeapBlockAddress = uVerifierStopHeapBlockAddress
    oPageHeapAllocation = cPageHeapAllocation.foGetForAddress(
        oCdbWrapper, uHeapBlockAddress)
    assert oPageHeapAllocation, \
        "No page heap report avaiable for address 0x%X" % uHeapBlockAddress

    uMemoryDumpStartAddress = None
    atxMemoryRemarks = []
    if sMessage in ["corrupted start stamp", "corrupted end stamp"] \
        and oPageHeapAllocation.uBlockStartAddress != uVerifierStopHeapBlockAddress:
        # When the application attempts to free (heap pointer + offset), Verifier does not detect this and will assume
        # the application provided pointer is correct. This causes it to look for the start and end stamp in the wrong
        # location and report this bug as memory corruption. When the page heap data shows no signs of corruption, we
        # can special case it.
        sHeapBlockAndOffsetId, sHeapBlockAndOffsetDescription = ftsGetHeapBlockAndOffsetIdAndDescription(
            uVerifierStopHeapBlockAddress, oPageHeapAllocation)
        sBugTypeId = "MisalignedFree%s" % sHeapBlockAndOffsetId
        sBugDescription = "The application attempted to free memory using a pointer that was %s." % sHeapBlockAndOffsetDescription
        sSecurityImpact = "Unknown: this type of bug has not been analyzed before"
        if oCdbWrapper.bGenerateReportHTML and oPageHeapAllocation:
            sPageHeapHTMLOutputHeader = "Page heap output for heap block near 0x%X" % uVerifierStopHeapBlockAddress
    elif sMessage == "block already freed":
        # |===========================================================
        # |VERIFIER STOP 00000007: pid 0x1358: block already freed
        # |
        # |        02F71000 : Heap handle
        # |        152F016C : Heap block
        # |        000004C8 : Block size
        # |        00000000 :
        # |===========================================================
        # |This verifier stop is not continuable. Process will be terminated
        # |when you use the `go' debugger command.
        # |===========================================================
        if oPageHeapAllocation.uBlockSize is None:
            sBugTypeId = "DoubleFree[?]"
            sBugDescription = "The application attempted to free a heap block of unknown size at address 0x%X twice" % \
                (uVerifierStopHeapBlockAddress)
            if oCdbWrapper.bGenerateReportHTML and oPageHeapAllocation:
                sPageHeapHTMLOutputHeader = "Page heap output for heap block near 0x%X" % uVerifierStopHeapBlockAddress
        else:
            sBugTypeId = "DoubleFree[%s]" % fsGetNumberDescription(
                oPageHeapAllocation.uBlockSize)
            sBugDescription = "The application attempted to free a %d/0x%X byte heap block at address 0x%X twice" % \
                (oPageHeapAllocation.uBlockSize, oPageHeapAllocation.uBlockSize, oPageHeapAllocation.uBlockStartAddress)
            if oCdbWrapper.bGenerateReportHTML and oPageHeapAllocation:
                sPageHeapHTMLOutputHeader = "Page heap output for heap block at 0x%X" % oPageHeapAllocation.uBlockStartAddress
        sSecurityImpact = "Potentially exploitable security issue, if the attacker can force the application to " \
            "allocate memory between the two frees"
    elif sMessage == "corrupted heap pointer or using wrong heap":
        # |===========================================================
        # |VERIFIER STOP 00000006: pid 0x144C: corrupted heap pointer or using wrong heap
        # |
        # |        077F1000 : Heap used in the call
        # |        43742FE0 : Heap block
        # |        00000020 : Block size
        # |        277F1000 : Heap owning the block
        # |===========================================================
        # |This verifier stop is not continuable. Process will be terminated
        # |when you use the `go' debugger command.
        # |===========================================================
        assert uVerifierStopHeapHandle is not None, \
            "Missing 'Heap used in the call' value in the VERIFIER STOP message.\r\n%s" % "\r\n".join(asCdbOutput)
        assert uVerifierStopHeapBlockHandle is not None, \
            "Missing 'Heap owning the block' value in the VERIFIER STOP message.\r\n%s" % "\r\n".join(asCdbOutput)
        if oPageHeapAllocation.uBlockSize is None:
            sBugTypeId = "IncorrectHeap[?]"
            sBugDescription = "The application provided an incorrect heap handle (0x%X) for a heap block of unknown size " \
                "near address 0x%X which belongs to another heap (handle 0x%X)" % \
                (uVerifierStopHeapHandle, uVerifierStopHeapBlockAddress, uVerifierStopHeapBlockHandle)
            if oCdbWrapper.bGenerateReportHTML and oPageHeapAllocation:
                sPageHeapHTMLOutputHeader = "Page heap output for heap block near 0x%X" % uVerifierStopHeapBlockAddress
        else:
            sBugTypeId = "IncorrectHeap[%s]" % (fsGetNumberDescription(
                oPageHeapAllocation.uBlockSize))
            sBugDescription = "The application provided an incorrect heap handle (0x%X) for a %d/0x%X byte heap block at " \
                "address 0x%X which belongs to another heap (handle 0x%X)" % \
                (uVerifierStopHeapHandle, oPageHeapAllocation.uBlockSize, oPageHeapAllocation.uBlockSize, \
                oPageHeapAllocation.uBlockStartAddress, uVerifierStopHeapBlockHandle)
            if oCdbWrapper.bGenerateReportHTML and oPageHeapAllocation:
                sPageHeapHTMLOutputHeader = "Page heap output for heap block at 0x%X" % oPageHeapAllocation.uBlockStartAddress
        sSecurityImpact = "Unknown: this type of bug has not been analyzed before"
    else:
        # Check the page heap data near the heap block for signs of corruption:
        oCorruptionDetector = cCorruptionDetector.foCreateForPageHeapAllocation(
            oPageHeapAllocation)

        if oCdbWrapper.bGenerateReportHTML:
            uMemoryDumpStartAddress = oPageHeapAllocation.uBlockStartAddress
            uMemoryDumpSize = oPageHeapAllocation.uBlockSize
            if oPageHeapAllocation:
                atxMemoryRemarks.extend(
                    oPageHeapAllocation.fatxMemoryRemarks())

        # Handle various VERIFIER STOP messages.
        if sMessage in ["corrupted start stamp", "corrupted end stamp"]:
            assert uCorruptionAddress is None, \
                "We do not expect the corruption address to be provided in this VERIFIER STOP message\r\n%s" % \
                    "\r\n".join(asRelevantLines)
            sBugTypeId = "OOBW"
            uCorruptionStartAddress = sMessage == "corrupted start stamp" \
                and oPageHeapAllocation.uStartStampAddress \
                or oPageHeapAllocation.uEndStampAddress
            uCorruptionEndAddress = uCorruptionStartAddress + 4
            # Stamps are 4 bytes
        elif sMessage in ["corrupted suffix pattern", "corrupted header"]:
            assert uCorruptionAddress is not None, \
                "The corruption address is expected to be provided in this VERIFIER STOP message:\r\n%s" % \
                    "\r\n".join(asRelevantLines)
            # Page heap stores the heap as close as possible to the edge of a page, taking into account that the start of the
            # heap block must be properly aligned. Bytes between the heap block and the end of the page are initialized to
            # 0xD0. Verifier has detected that one of the bytes changed value, which indicates an out-of-bounds write. BugId
            # will try to find all bytes that were changed:
            sBugTypeId = "OOBW"
            uCorruptionStartAddress = uCorruptionAddress
            uCorruptionEndAddress = uCorruptionStartAddress + 1
            # We do not know the size, so assume one byte.
        elif sMessage == "corrupted infix pattern":
            assert uCorruptionAddress is not None, \
                "The corruption address is expected to be provided in the VERIFIER STOP message:\r\n%s" % \
                    "\r\n".join(asRelevantLines)
            # Page heap sometimes does not free a heap block immediately, but overwrites the bytes with 0xF0. Verifier has
            # detected that one of the bytes changed value, which indicates a write-after-free. BugId will try to find all
            # bytes that were changed:
            oCorruptionDetector.fbDetectCorruption(
                oPageHeapAllocation.uBlockStartAddress,
                [0xF0 for x in xrange(oPageHeapAllocation.uBlockSize)])
            uCorruptionStartAddress = uCorruptionAddress
            uCorruptionEndAddress = uCorruptionStartAddress + 1
            # We do not know the size, so assume one byte.
            sBugTypeId = "UAFW"
        else:
            raise AssertionError("Unhandled VERIFIER STOP message: %s" %
                                 sMessage)
        # If we detected the reported corruption ourselves, we can get a better idea of the start and end address:
        # (One might expect that when a VERIFIER STOP reported memory corruption, we can always detect this as well, but
        # unfortunately, this is not always true. I do not know why a VERIFIER STOP would report corruption when there is
        # no sign of it, but by trusting it is correct, we at least get a report so I can find out what is going on).
        if oCorruptionDetector.bCorruptionDetected:
            uCorruptionStartAddress = oCorruptionDetector.uCorruptionStartAddress
            uCorruptionEndAddress = oCorruptionDetector.uCorruptionEndAddress
            if oCdbWrapper.bGenerateReportHTML:
                atxMemoryRemarks.extend(
                    oCorruptionDetector.fatxMemoryRemarks())
        uCorruptionSize = uCorruptionEndAddress - uCorruptionStartAddress

        # If the corruption starts before or ends after the heap block, expand the memory dump to include the entire
        # corrupted region.
        if oCdbWrapper.bGenerateReportHTML:
            if uCorruptionStartAddress < uMemoryDumpStartAddress:
                uMemoryDumpSize += uMemoryDumpStartAddress - uCorruptionStartAddress
                uMemoryDumpStartAddress = uCorruptionStartAddress
            if uCorruptionEndAddress < uMemoryDumpStartAddress + uMemoryDumpSize:
                uMemoryDumpSize += uCorruptionEndAddress - (
                    uMemoryDumpStartAddress + uMemoryDumpSize)
        # Get a human readable description of the start offset of corruption relative to the heap block, where corruption
        # starting before or inside the heap block will be relative to the start, and corruption after it to the end.
        sHeapBlockAndOffsetId, sHeapBlockAndOffsetDescription = \
            ftsGetHeapBlockAndOffsetIdAndDescription(uCorruptionStartAddress, oPageHeapAllocation)
        sBugTypeId += sHeapBlockAndOffsetId
        sBugDescription = "Page heap detected heap corruption at 0x%X; %s." % (
            uCorruptionStartAddress, sHeapBlockAndOffsetDescription)
        if uCorruptionStartAddress == oPageHeapAllocation.uBlockEndAddress:
            sBugDescription += " This appears to be a classic buffer-overrun vulnerability."
            sSecurityImpact = "Potentially highly exploitable security issue."
        else:
            sSecurityImpact = "Potentially exploitable security issue, if the corruption is attacker controlled."
        # If we detected corruption by scanning certain bytes in the applications memory, make sure this matches what
        # verifier reported and save all bytes that were affected: so far, we only saved the bytes that had an unexpected
        # value, but there is a chance that a byte was overwritten with the same value it has before, in which case it was
        # not saved. This can be detect if it is surrounded by bytes that did change value. This code reads the value of all
        # bytes between the first and last byte that we detected was corrupted:
        if oCorruptionDetector.bCorruptionDetected:
            asCorruptedBytes = oCorruptionDetector.fasCorruptedBytes()
            sBugDescription += " The following byte values were written to the corrupted area: %s." % ", ".join(
                asCorruptedBytes)
            sBugTypeId += oCorruptionDetector.fsCorruptionId() or ""
        if oCdbWrapper.bGenerateReportHTML and oPageHeapAllocation:
            sPageHeapHTMLOutputHeader = "Page heap output for heap block near 0x%X" % uCorruptionStartAddress

    oBugReport = cBugReport.foCreate(oCdbWrapper, sBugTypeId, sBugDescription,
                                     sSecurityImpact)
    if oCdbWrapper.bGenerateReportHTML:
        if uMemoryDumpStartAddress:
            oBugReport.fAddMemoryDump(
                uMemoryDumpStartAddress,
                uMemoryDumpStartAddress + uMemoryDumpSize,
                "Memory near heap block at 0x%X" % uMemoryDumpStartAddress,
            )
        oBugReport.atxMemoryRemarks.extend(atxMemoryRemarks)
    # Output the VERIFIER STOP message for reference
    if oCdbWrapper.bGenerateReportHTML:
        sVerifierStopMessageHTML = sBlockHTMLTemplate % {
            "sName":
            "VERIFIER STOP message",
            "sCollapsed":
            "Collapsed",
            "sContent":
            "<pre>%s</pre>" % "\r\n".join([
                oCdbWrapper.fsHTMLEncode(s, uTabStop=8)
                for s in asRelevantLines
            ])
        }
        oBugReport.asExceptionSpecificBlocksHTML.append(
            sVerifierStopMessageHTML)
        # Output the page heap information for reference
        if oPageHeapAllocation:
            sPageHeapOutputHTML = sBlockHTMLTemplate % {
                "sName":
                sPageHeapHTMLOutputHeader,
                "sCollapsed":
                "Collapsed",
                "sContent":
                "<pre>%s</pre>" % "\r\n".join([
                    oCdbWrapper.fsHTMLEncode(s, uTabStop=8)
                    for s in oPageHeapAllocation.asPageHeapOutput
                ])
            }
            oBugReport.asExceptionSpecificBlocksHTML.append(
                sPageHeapOutputHTML)

    oBugReport.bRegistersRelevant = False
    return oBugReport
Ejemplo n.º 6
0
def foDetectAndCreateBugReportForVERIFIER_STOP(oCdbWrapper, uExceptionCode,
                                               asCdbOutput):
    uErrorNumber = None
    uProcessId = None
    sMessage = None
    uVerifierStopHeapBlockAddress = None
    uVerifierStopHeapBlockSize = None
    uVerifierStopHeapBlockHandle = None
    uVerifierStopHeapHandle = None
    # This is what is being used, which may differ from the actual heap handle of the block
    uCorruptedStamp = None
    uCorruptionAddress = None
    asRelevantLines = []

    for sLine in asCdbOutput:
        # Ignore exmpty lines
        if not sLine:
            continue
        # Look for the first VERIFIER STOP message and gather information
        if uErrorNumber is None:
            oErrorMessageMatch = re.match(
                r"^VERIFIER STOP ([0-9A-F]+): pid 0x([0-9A-F]+): (.*?)\s*$",
                sLine)
            if oErrorMessageMatch:
                sErrorNumber, sProcessId, sMessage = oErrorMessageMatch.groups(
                )
                uErrorNumber = long(sErrorNumber, 16)
                uProcessId = long(sProcessId, 16)
                asRelevantLines.append(sLine)
                oCdbWrapper.fSelectProcess(uProcessId)
                assert uProcessId == oCdbWrapper.oCurrentProcess.uId, \
                    "VERIFIER STOP in process with id %d/0x%X, but current process id is %d/0x%X" % \
                    (uProcessId, uProcessId, oCdbWrapper.oCurrentProcess.uId, oCdbWrapper.oCurrentProcess.uId)
            continue
        # A VERIFIER STOP message has been detected, gather what information verifier provides:
        oInformationMatch = re.match(r"^\t([0-9A-F]+) : (.*?)\s*$", sLine)
        if oInformationMatch:
            asRelevantLines.append(sLine)
            sValue, sDescription = oInformationMatch.groups()
            uValue = long(sValue, 16)
            sDescription = sDescription.lower()
            # Both "Corruption address" and "corruption address" are used :(
            if sDescription == "heap block":
                uVerifierStopHeapBlockAddress = uValue
            elif sDescription == "block size":
                uVerifierStopHeapBlockSize = uValue
            elif sDescription == "corrupted stamp":
                uCorruptedStamp = uValue
            elif sDescription == "corruption address":
                uCorruptionAddress = uValue
            elif sDescription == "corruption address":
                uCorruptionAddress = uValue
            elif sDescription == "heap handle":
                uVerifierStopHeapBlockHandle = uValue
                uVerifierStopHeapHandle = uValue
            elif sDescription == "heap used in the call":
                uVerifierStopHeapHandle = uValue
            elif sDescription == "heap owning the block":
                uVerifierStopHeapBlockHandle = uValue
        else:
            assert sLine.strip().replace("=", "") == "", \
                "Unknown VERIFIER STOP message line: %s\r\n%s" % (repr(sLine), "\r\n".join(asCdbOutput))
            break
    else:
        assert uErrorNumber is None, \
            "Detected the start of a VERIFIER STOP message but not the end\r\n%s" % "\r\n".join(asCdbOutput)
        return None
        # No VERIFIER STOP in the output.
    oCdbWrapper.fSelectProcess(uProcessId)
    oProcess = oCdbWrapper.oCurrentProcess
    if uErrorNumber == 0x303:
        # |=======================================
        # |VERIFIER STOP 0000000000000303: pid 0xB2C: NULL handle passed as parameter. A valid handle must be used.
        # |
        # |0000000000000000 : Not used.
        # |0000000000000000 : Not used.
        # |0000000000000000 : Not used.
        # |0000000000000000 : Not used.
        # |
        # |=======================================
        return None
        # This is a VERIFIER STOP, but not an interesting one and we can continue the application.

    assert uVerifierStopHeapBlockAddress is not None, \
        "The heap block start address was not found in the verifier stop message.\r\n%s" % "\r\n".join(asRelevantLines)
    assert uVerifierStopHeapBlockSize is not None, \
        "The heap block size was not found in the verifier stop message.\r\n%s" % "\r\n".join(asRelevantLines)

    oPageHeapManagerData = oProcess.foGetHeapManagerDataForAddress(
        uVerifierStopHeapBlockAddress, sType="page heap")
    if oPageHeapManagerData is None and sMessage == "block already freed":
        # There is a bug in application verifier where it reports the address of the DPH_HEAP_BLOCK structure instead of
        # the heap block in certain situations. In this case, "!heap" will not return any information and
        # oPageHeapManagerData will be None. We can still find the correct information though:
        oPageHeapManagerData = cPageHeapManagerData.foGetForProcessAndAllocationInformationStartAddress(
            oProcess,
            uVerifierStopHeapBlockAddress,
        )

    uMemoryDumpStartAddress = None
    atxMemoryRemarks = []
    if sMessage == "corrupted start stamp" and uCorruptedStamp == 0xABCDBBBA and not oPageHeapManagerData.oVirtualAllocation.bReserved:
        # Verifier sometimes reports this and I am not sure what is going on. Page heap will report that the heap block was
        # freed by the current function on the stack, while verifier reports this error almost immediately after that (from
        # that same stack). However, the start stamp reported in the verifier error is a valid value indicating the memory
        # was freed, so it makes no sense that it is reported as being corrupt. Also, there is no memory allocated at the
        # given address; it is only reserved, so it cannot actually contain a value.
        # I cannot think of any situation in which all this information is correct, and I suspect that verifier is
        # reporting the wrong kind of error. However, I have been unable to find a reliable repro for this to debug, so I
        # do not know exactly what is going on and what to report...
        (sHeapBlockAndOffsetId, sHeapBlockAndOffsetDescription) = \
            oPageHeapManagerData.ftsGetIdAndDescriptionForAddress(uVerifierStopHeapBlockAddress)
        sBugTypeId = "UnknownVerifierError%s" % sHeapBlockAndOffsetId
        sBugDescription = "Application verifier reported a corrupt memory block, but no memory is allocated at the " \
            "address provided. This suggests that the verfier report is incorrect. However, it is likely that the " \
            "application does have some kind of memory corruption bug that triggers this report, but it is unknown " \
            "what kind of bug that might be. If you can reliably reproduce this, please contact the author of BugId " \
            "so this situation can be analyzed and BugId can be improved to report something more helpful here."
        sSecurityImpact = "Unknown: this type of bug has not been analyzed before"
    elif sMessage in ["corrupted start stamp", "corrupted end stamp"] \
        and oPageHeapManagerData.uHeapBlockStartAddress != uVerifierStopHeapBlockAddress:
        # When the application attempts to free (heap pointer + offset), Verifier does not detect this and will assume
        # the application provided pointer is correct. This causes it to look for the start and end stamp in the wrong
        # location and report this bug as memory corruption. When the page heap data shows no signs of corruption, we
        # can special case it.
        (sHeapBlockAndOffsetId, sHeapBlockAndOffsetDescription) = \
            oPageHeapManagerData.ftsGetIdAndDescriptionForAddress(uVerifierStopHeapBlockAddress)
        sBugTypeId = "MisalignedFree%s" % sHeapBlockAndOffsetId
        sBugDescription = "The application attempted to free memory using a pointer that was %s." % sHeapBlockAndOffsetDescription
        sSecurityImpact = "Unknown: this type of bug has not been analyzed before"
    elif sMessage == "block already freed":
        # |===========================================================
        # |VERIFIER STOP 00000007: pid 0x1358: block already freed
        # |
        # |        02F71000 : Heap handle
        # |        152F016C : Heap block
        # |        000004C8 : Block size
        # |        00000000 :
        # |===========================================================
        # |This verifier stop is not continuable. Process will be terminated
        # |when you use the `go' debugger command.
        # |===========================================================
        sBugTypeId = "DoubleFree[%s]" % fsGetNumberDescription(
            oPageHeapManagerData.uHeapBlockSize)
        sBugDescription = "The application attempted to free a %d/0x%X byte heap block at address 0x%X twice" % \
            (oPageHeapManagerData.uHeapBlockSize, oPageHeapManagerData.uHeapBlockSize, oPageHeapManagerData.uHeapBlockStartAddress)
        sSecurityImpact = "Potentially exploitable security issue, if the attacker can force the application to " \
            "allocate memory between the two frees"
    elif sMessage == "corrupted heap pointer or using wrong heap":
        # |===========================================================
        # |VERIFIER STOP 00000006: pid 0x144C: corrupted heap pointer or using wrong heap
        # |
        # |        077F1000 : Heap used in the call
        # |        43742FE0 : Heap block
        # |        00000020 : Block size
        # |        277F1000 : Heap owning the block
        # |===========================================================
        # |This verifier stop is not continuable. Process will be terminated
        # |when you use the `go' debugger command.
        # |===========================================================
        assert uVerifierStopHeapHandle is not None, \
            "Missing 'Heap used in the call' value in the VERIFIER STOP message.\r\n%s" % "\r\n".join(asCdbOutput)
        assert uVerifierStopHeapBlockHandle is not None, \
            "Missing 'Heap owning the block' value in the VERIFIER STOP message.\r\n%s" % "\r\n".join(asCdbOutput)
        if oPageHeapManagerData.uHeapBlockSize is None:
            sBugTypeId = "IncorrectHeap[?]"
            sBugDescription = "The application provided an incorrect heap handle (0x%X) for a heap block of unknown size " \
                "near address 0x%X which belongs to another heap (handle 0x%X)" % \
                (uVerifierStopHeapHandle, uVerifierStopHeapBlockAddress, uVerifierStopHeapBlockHandle)
        else:
            sBugTypeId = "IncorrectHeap[%s]" % (fsGetNumberDescription(
                oPageHeapManagerData.uHeapBlockSize))
            sBugDescription = "The application provided an incorrect heap handle (0x%X) for a %d/0x%X byte heap block at " \
                "address 0x%X which belongs to another heap (handle 0x%X)" % \
                (uVerifierStopHeapHandle, oPageHeapManagerData.uHeapBlockSize, oPageHeapManagerData.uHeapBlockSize, \
                oPageHeapManagerData.uHeapBlockStartAddress, uVerifierStopHeapBlockHandle)
        sSecurityImpact = "Unknown: this type of bug has not been analyzed before"
    else:
        if oCdbWrapper.bGenerateReportHTML:
            uMemoryDumpStartAddress = oPageHeapManagerData.uMemoryDumpStartAddress
            uMemoryDumpEndAddress = oPageHeapManagerData.uMemoryDumpEndAddress
            if oPageHeapManagerData:
                atxMemoryRemarks.extend(
                    oPageHeapManagerData.fatxMemoryRemarks())

        # Handle various VERIFIER STOP messages.
        if sMessage in ["corrupted start stamp", "corrupted end stamp"]:
            assert uCorruptionAddress is None, \
                "We do not expect the corruption address to be provided in this VERIFIER STOP message\r\n%s" % \
                    "\r\n".join(asRelevantLines)
            if not oPageHeapManagerData.oHeapBlockHeader:
                # This makes no sense: there is no heap block header because the memory is really freed by verifier but left
                # reserved to prevent it from being reallocated. So the start and end stamp no longer exist...
                assert oPageHeapManagerData.oVirtualAllocation.bReserved, \
                    "This is even more unexpected."
                sBugTypeId = "UnknownVerifierError"
                uCorruptionStartAddress = None
                uCorruptionEndAddress = None
            else:
                sBugTypeId = "OOBW"
                if sMessage == "corrupted start stamp":
                    uCorruptionStartAddress = oPageHeapManagerData.fuHeapBlockHeaderFieldAddress(
                        "StartStamp")
                    uCorruptionEndAddress = uCorruptionStartAddress + oPageHeapManagerData.fuHeapBlockHeaderFieldSize(
                        "StartStamp")
                else:
                    uCorruptionStartAddress = oPageHeapManagerData.fuHeapBlockHeaderFieldAddress(
                        "EndStamp")
                    uCorruptionEndAddress = uCorruptionStartAddress + oPageHeapManagerData.fuHeapBlockHeaderFieldSize(
                        "EndStamp")
        elif sMessage in ["corrupted suffix pattern", "corrupted header"]:
            assert uCorruptionAddress is not None, \
                "The corruption address is expected to be provided in this VERIFIER STOP message:\r\n%s" % \
                    "\r\n".join(asRelevantLines)
            # Page heap stores the heap as close as possible to the edge of a page, taking into account that the start of the
            # heap block must be properly aligned. Bytes between the heap block and the end of the page are initialized to
            # 0xD0. Verifier has detected that one of the bytes changed value, which indicates an out-of-bounds write. BugId
            # will try to find all bytes that were changed:
            sBugTypeId = "OOBW"
            uCorruptionStartAddress = uCorruptionAddress
            uCorruptionEndAddress = uCorruptionStartAddress + 1
            # We do not know the size, so assume one byte.
        elif sMessage == "corrupted infix pattern":
            assert uCorruptionAddress is not None, \
                "The corruption address is expected to be provided in the VERIFIER STOP message:\r\n%s" % \
                    "\r\n".join(asRelevantLines)
            # Page heap sometimes does not free a heap block immediately, but overwrites the bytes with 0xF0. Verifier has
            # detected that one of the bytes changed value, which indicates a write-after-free. BugId will try to find all
            # bytes that were changed:
            uCorruptionStartAddress = uCorruptionAddress
            uCorruptionEndAddress = uCorruptionStartAddress + 1
            # We do not know the size, so assume one byte.
            sBugTypeId = "UAFW"
        else:
            raise AssertionError("Unhandled VERIFIER STOP message: %s" %
                                 sMessage)
        # We cannot trust the information in the VERIFIER STOP message: if you run the test "OutOfBounds Heap Write 1 -1 1"
        # (meaning you allocate 1 byte, write 1 byte at offset -1) you would expect it to report that the "end stamp" of
        # the heap block header was corrupted. HOWEVER IT INCORRECTLY REPORTS THAT THE START STAMP WAS CORRUPTED. So, only
        # if we cannot detect any corruption ourselves will we use the information we got from the VERIFIER STOP message.
        if oPageHeapManagerData.bCorruptionDetected:
            uCorruptionStartAddress = oPageHeapManagerData.uCorruptionStartAddress
            uCorruptionEndAddress = oPageHeapManagerData.uCorruptionEndAddress

        # If the corruption starts before or ends after the heap block, expand the memory dump to include the entire
        # corrupted region.
        if uCorruptionStartAddress and oCdbWrapper.bGenerateReportHTML:
            # Limit memory dump size
            uMemoryDumpStartAddress, uMemoryDumpSize = ftuLimitedAndAlignedMemoryDumpStartAddressAndSize(
                uCorruptionStartAddress, oProcess.uPointerSize,
                uCorruptionStartAddress,
                uMemoryDumpEndAddress - uMemoryDumpStartAddress)
        # Get a human readable description of the start offset of corruption relative to the heap block, where corruption
        # starting before or inside the heap block will be relative to the start, and corruption after it to the end.
        if uCorruptionStartAddress is None:
            (sHeapBlockId, sHeapBlockDescription) = \
                oPageHeapManagerData.ftsGetIdAndDescription()
            sBugTypeId += sHeapBlockId
            sBugDescription = "Heap corruption reported but not detected in %s." % sHeapBlockDescription
            sSecurityImpact = "Unknown - Application Verifier reported this but it could not be confirmed."
        else:
            (sHeapBlockAndOffsetId, sHeapBlockAndOffsetDescription) = \
                oPageHeapManagerData.ftsGetIdAndDescriptionForAddress(uCorruptionStartAddress)
            sBugTypeId += sHeapBlockAndOffsetId
            sBugDescription = "Heap corruption detected at 0x%X; %s." % (
                uCorruptionStartAddress, sHeapBlockAndOffsetDescription)
            if uCorruptionStartAddress == oPageHeapManagerData.uHeapBlockEndAddress:
                sBugDescription += " This appears to be a classic buffer-overrun vulnerability."
                sSecurityImpact = "Potentially highly exploitable security issue."
            else:
                sSecurityImpact = "Potentially exploitable security issue, if the corruption is attacker controlled."
        if oPageHeapManagerData.bCorruptionDetected:
            sBugTypeId += oPageHeapManagerData.sCorruptionId

    oBugReport = cBugReport.foCreate(oProcess, sBugTypeId, sBugDescription,
                                     sSecurityImpact)
    if oCdbWrapper.bGenerateReportHTML and uMemoryDumpStartAddress:
        oBugReport.fAddMemoryDump(
            uMemoryDumpStartAddress,
            uMemoryDumpStartAddress + uMemoryDumpSize,
            "Memory at 0x%X" % uMemoryDumpStartAddress,
        )
        oBugReport.atxMemoryRemarks.extend(atxMemoryRemarks)
    # Output the VERIFIER STOP message for reference
    if oCdbWrapper.bGenerateReportHTML:
        sVerifierStopMessageHTML = sBlockHTMLTemplate % {
            "sName":
            "VERIFIER STOP message",
            "sCollapsed":
            "Collapsed",
            "sContent":
            "<pre>%s</pre>" % "\r\n".join([
                oCdbWrapper.fsHTMLEncode(s, uTabStop=8)
                for s in asRelevantLines
            ])
        }
        oBugReport.asExceptionSpecificBlocksHTML.append(
            sVerifierStopMessageHTML)

    oBugReport.bRegistersRelevant = False
    return oBugReport
def cCdbWrapper_fCdbStdInOutThread(oCdbWrapper):
  # Get a lock on cdb so the "interrupt on timeout" thread does not attempt to interrupt cdb while we execute commands.
  oCdbWrapper.oCdbLock.acquire();
  try:
    # Create a list of commands to set up event handling. The default for any exception not explicitly mentioned is to
    # be handled as a second chance exception.
    asExceptionHandlingCommands = ["sxd *"];
    # request second chance debugger break for certain exceptions that indicate the application has a bug.
    for sCommand, axExceptions in daxExceptionHandling.items():
      for xException in axExceptions:
        sException = isinstance(xException, str) and xException or ("0x%08X" % xException);
        asExceptionHandlingCommands.append("%s %s" % (sCommand, sException));
    sExceptionHandlingCommands = ";".join(asExceptionHandlingCommands);
    
    # Read the initial cdb output related to starting/attaching to the first process.
    asIntialCdbOutput = oCdbWrapper.fasReadOutput();
    if not oCdbWrapper.bCdbRunning: return;
    # Turn off prompt information as it is not useful most of the time, but can clutter output.
    oCdbWrapper.fasSendCommandAndReadOutput(".prompt_allow -dis -ea -reg -src -sym", bIsRelevantIO = False);
    if not oCdbWrapper.bCdbRunning: return;
    
    # Exception handlers need to be set up.
    oCdbWrapper.bExceptionHandlersHaveBeenSet = False;
    # Only fire fApplicationRunningCallback if the application was started for the first time or resumed after it was
    # paused to analyze an exception. 
    bInitialApplicationRunningCallbackFired = False;
    bDebuggerNeedsToResumeAttachedProcesses = len(oCdbWrapper.auProcessIdsPendingAttach) > 0;
    bApplicationWasPausedToAnalyzeAnException = False;
    # An bug report will be created when needed; it is returned at the end
    oBugReport = None;
    # Memory can be allocated to be freed later in case the system has run low on memory when an analysis needs to be
    # performed. This is done only if dxBugIdConfig["uReserveRAM"] > 0. The memory is allocated at the start of
    # debugging, freed right before an analysis is performed and reallocated if the exception was not fatal.
    bReserveRAMAllocated = False;
    while asIntialCdbOutput or len(oCdbWrapper.auProcessIdsPendingAttach) + len(oCdbWrapper.auProcessIds) > 0:
      # If requested, reserve some memory in cdb that can be released later to make analysis under low memory conditions
      # more likely to succeed.
      if dxBugIdConfig["uReserveRAM"] and not bReserveRAMAllocated:
        uBitMask = 2 ** 31;
        while uBitMask >= 1:
          sBit = dxBugIdConfig["uReserveRAM"] & uBitMask and "A" or "";
          if bReserveRAMAllocated:
            oCdbWrapper.fasSendCommandAndReadOutput("aS /c RAM .printf \"${RAM}{$RAM}%s\";" % sBit,
                bIsRelevantIO = False);
          elif sBit:
            oCdbWrapper.fasSendCommandAndReadOutput("aS RAM \"%s\";" % sBit, bIsRelevantIO = False);
            bReserveRAMAllocated = True;
          if not oCdbWrapper.bCdbRunning: return;
          uBitMask /= 2;
      # Discard any cached information about modules loaded in the current process, as this may be about to change
      # during execution of the application.
      oCdbWrapper.doModules_by_sCdbId = None;
      if asIntialCdbOutput:
        # First parse the intial output
        asCdbOutput = asIntialCdbOutput;
        asIntialCdbOutput = None;
      else:
        # Then attach to a process, or start or resume the application
        if ( # If we've just started the application or we've attached to all processes and are about to resume them...
          not bInitialApplicationRunningCallbackFired and len(oCdbWrapper.auProcessIdsPendingAttach) == 0
        ) or ( # ... or if we've paused the application and are about to resume it ...
          bApplicationWasPausedToAnalyzeAnException
        ):
          # ...report that the application is about to start running.
          oCdbWrapper.fApplicationRunningCallback and oCdbWrapper.fApplicationRunningCallback();
          bInitialApplicationRunningCallbackFired = True;
        # Mark the time when the application was resumed.
        oCdbWrapper.nApplicationResumeTime = time.clock();
        # Release the lock on cdb so the "interrupt on timeout" thread can attempt to interrupt cdb while the
        # application is running.
        if len(oCdbWrapper.auProcessIdsPendingAttach) == 0:
          oCdbWrapper.oCdbLock.release();
        try:
          asCdbOutput = oCdbWrapper.fasSendCommandAndReadOutput("g", bMayContainApplicationOutput = True);
          if not oCdbWrapper.bCdbRunning: return;
        finally:
          # Get a lock on cdb so the "interrupt on timeout" thread does not attempt to interrupt cdb while we execute
          # commands.
          if len(oCdbWrapper.auProcessIdsPendingAttach) == 0:
            oCdbWrapper.oCdbLock.acquire();
          # Let the interrupt-on-timeout thread know that the application has been interrupted, so that when it detects
          # another timeout should be fired, it will try to interrupt the application again.
          oCdbWrapper.bInterruptPending = False;
        # Add the time since the application was resumed to the total application run time, and mark the application as
        # suspended by setting nApplicationResumeTime to None. TODO there is a race condition between here and in
        # oCdbWrapper.fxSetTimeout that must be addressed.
        oCdbWrapper.nApplicationRunTime += time.clock() - oCdbWrapper.nApplicationResumeTime;
        oCdbWrapper.nApplicationResumeTime = None;
        # Execute any pending timeout callbacks
        for xTimeout in oCdbWrapper.axTimeouts:
          (nTimeoutTime, fTimeoutCallback) = xTimeout;
          if nTimeoutTime <= oCdbWrapper.nApplicationRunTime: # This timeout should be fired.
            oCdbWrapper.axTimeouts.remove(xTimeout);
            fTimeoutCallback();
      if oCdbWrapper.bGetDetailsHTML:
        # Save the current number of blocks of StdIO; if this exception is not relevant it can be used to remove all
        # blocks added while analyzing it. These blocks are not considered to contain useful information and removing
        # them can  reduce the risk of OOM when irrelevant exceptions happens very often. The last block contains a
        # prompt, which will become the first analysis command's block, so it is not saved.
        uOriginalHTMLCdbStdIOBlocks = len(oCdbWrapper.asCdbStdIOBlocksHTML) - 1;
      # If cdb is attaching to a process, make sure it worked.
      for sLine in asCdbOutput:
        oFailedAttachMatch = re.match(r"^Cannot debug pid \d+, Win32 error 0n\d+\s*$", sLine);
        assert not oFailedAttachMatch, "Failed to attach to process!\r\n%s" % "\r\n".join(asCdbOutput);
      # Find out what event caused the debugger break
      asLastEventOutput = oCdbWrapper.fasSendCommandAndReadOutput(".lastevent");
      if not oCdbWrapper.bCdbRunning: return;
      # Sample output:
      # |Last event: 3d8.1348: Create process 3:3d8                
      # |  debugger time: Tue Aug 25 00:06:07.311 2015 (UTC + 2:00)
      # - or -
      # |Last event: c74.10e8: Exit process 4:c74, code 0          
      # |  debugger time: Tue Aug 25 00:06:07.311 2015 (UTC + 2:00)
      bValidLastEventOutput = len(asLastEventOutput) == 2 and re.match(r"^\s*debugger time: .*$", asLastEventOutput[1]);
      oEventMatch = bValidLastEventOutput and re.match(
        "".join([
          r"^Last event: ([0-9a-f]+)\.[0-9a-f]+: ",
          r"(?:",
            r"(Create|Exit) process [0-9a-f]+\:([0-9a-f]+)(?:, code [0-9a-f]+)?",
          r"|",
            r"(.*?) \- code ([0-9a-f]+) \(!*\s*(first|second) chance\s*!*\)",
          r"|",
            r"Hit breakpoint (\d+)",
          r")\s*$",
        ]),
        asLastEventOutput[0],
        re.I
      );
      assert oEventMatch, "Invalid .lastevent output:\r\n%s" % "\r\n".join(asLastEventOutput);
      (
        sProcessIdHex,
          sCreateExitProcess, sCreateExitProcessIdHex,
          sExceptionDescription, sExceptionCode, sChance,
          sBreakpointId,
      ) = oEventMatch.groups();
      uProcessId = long(sProcessIdHex, 16);
      uExceptionCode = sExceptionCode and long(sExceptionCode, 16);
      uBreakpointId = sBreakpointId and long(sBreakpointId);
      if (
        uExceptionCode in (STATUS_BREAKPOINT, STATUS_WAKE_SYSTEM_DEBUGGER)
        and uProcessId not in oCdbWrapper.auProcessIds
      ):
        # This is assumed to be the initial breakpoint after starting/attaching to the first process or after a new
        # process was created by the application. This assumption may not be correct, in which case the code needs to
        # be modifed to check the stack to determine if this really is the initial breakpoint. But that comes at a
        # performance cost, so until proven otherwise, the code is based on this assumption.
        sCreateExitProcess = "Create";
        sCreateExitProcessIdHex = sProcessIdHex;
      if sCreateExitProcess:
        # Make sure the created/exited process is the current process.
        assert sProcessIdHex == sCreateExitProcessIdHex, "%s vs %s" % (sProcessIdHex, sCreateExitProcessIdHex);
        oCdbWrapper.fHandleCreateExitProcess(sCreateExitProcess, uProcessId);
        # If there are more processes to attach to, do so:
        if len(oCdbWrapper.auProcessIdsPendingAttach) > 0:
          asAttachToProcess = oCdbWrapper.fasSendCommandAndReadOutput(".attach 0n%d" % oCdbWrapper.auProcessIdsPendingAttach[0]);
          if not oCdbWrapper.bCdbRunning: return;
        else:
          # Set up exception handling if this has not been done yet.
          if not oCdbWrapper.bExceptionHandlersHaveBeenSet:
            # Note to self: when rewriting the code, make sure not to set up exception handling before the debugger has
            # attached to all processes. But do so before resuming the threads. Otherwise one or more of the processes
            # can end up having only one thread that has a suspend count of 2 and no amount of resuming will cause the
            # process to run. The reason for this is unknown, but if things are done in the correct order, this problem
            # is avoided.
            oCdbWrapper.bExceptionHandlersHaveBeenSet = True;
            oCdbWrapper.fasSendCommandAndReadOutput(sExceptionHandlingCommands, bIsRelevantIO = False);
            if not oCdbWrapper.bCdbRunning: return;
          # If the debugger attached to processes, mark that as done and resume threads in all processes.
          if bDebuggerNeedsToResumeAttachedProcesses:
            bDebuggerNeedsToResumeAttachedProcesses = False;
            for uProcessId in oCdbWrapper.auProcessIds:
              oCdbWrapper.fSelectProcessAndThread(uProcessId = uProcessId);
              if not oCdbWrapper.bCdbRunning: return;
              oCdbWrapper.fasSendCommandAndReadOutput("~*m", bIsRelevantIO = False);
              if not oCdbWrapper.bCdbRunning: return;
        if oCdbWrapper.bGetDetailsHTML:
          # As this is not relevant to the bug, remove the commands and their output from cdb IO and replace with a
          # description of the exception to remove clutter and reduce memory usage. 
          oCdbWrapper.asCdbStdIOBlocksHTML = (
            oCdbWrapper.asCdbStdIOBlocksHTML[0:uOriginalHTMLCdbStdIOBlocks] + # IO before analysis commands
            ["<span class=\"CDBIgnoredException\">%s process %d breakpoint.</span>" % (sCreateExitProcess, uProcessId)] + # Replacement for analysis commands
            oCdbWrapper.asCdbStdIOBlocksHTML[-1:] # Last block contains prompt and must be conserved.
          );
      elif uExceptionCode == DBG_CONTROL_BREAK:
        # Debugging the application was interrupted and the application suspended to fire some timeouts. This exception
        # is not a bug and should be ignored.
        pass;
      elif (
        uExceptionCode in [STATUS_WX86_BREAKPOINT, STATUS_BREAKPOINT]
        and dxBugIdConfig["bIgnoreFirstChanceBreakpoints"]
        and sChance == "first"
      ):
        pass;
      else:
        # Report that the application is paused for analysis...
        if oCdbWrapper.fExceptionDetectedCallback:
          if sBreakpointId:
            oCdbWrapper.fExceptionDetectedCallback(STATUS_BREAKPOINT, "A BugId breakpoint");
          else:
            oCdbWrapper.fExceptionDetectedCallback(uExceptionCode, sExceptionDescription);
        # And potentially report that the application is resumed later...
        bApplicationWasPausedToAnalyzeAnException = True;
        # If available, free previously allocated memory to allow analysis in low memory conditions.
        if bReserveRAMAllocated:
          # This command is not relevant to the bug, so it is hidden in the cdb IO to prevent OOM.
          oCdbWrapper.fasSendCommandAndReadOutput("ad RAM;", bIsRelevantIO = False);
          bReserveRAMAllocated = False;
        # Create a bug report, if the exception is fatal.
        oCdbWrapper.oBugReport = cBugReport.foCreate(oCdbWrapper, uExceptionCode, sExceptionDescription, uBreakpointId);
        if not oCdbWrapper.bCdbRunning: return;
        if oCdbWrapper.oBugReport is not None:
          if dxBugIdConfig["bSaveDump"]:
            sDumpFileName = fsCreateFileName(oCdbWrapper.oBugReport.sId);
            sOverwrite = dxBugIdConfig["bOverwriteDump"] and "/o" or "";
            oCdbWrapper.fasSendCommandAndReadOutput(".dump %s /ma \"%s.dmp\"" % (sOverwrite, sDumpFileName));
            if not oCdbWrapper.bCdbRunning: return;
          break;
        if oCdbWrapper.bGetDetailsHTML:
          # Otherwise remove the analysis commands from the cdb IO and replace with a description of the exception to
          # remove clutter and reduce memory usage.
          oCdbWrapper.asCdbStdIOBlocksHTML = (
            oCdbWrapper.asCdbStdIOBlocksHTML[0:uOriginalHTMLCdbStdIOBlocks] + # IO before analysis commands
            ["<span class=\"CDBIgnoredException\">Exception 0x%08X in process %d.</span>" % (uExceptionCode, uProcessId)] +
            oCdbWrapper.asCdbStdIOBlocksHTML[-1:] # Last block contains prompt and must be conserved.
          );
    # Terminate cdb.
    oCdbWrapper.bCdbWasTerminatedOnPurpose = True;
    oCdbWrapper.fasSendCommandAndReadOutput("q");
  finally:
    # Release the lock on cdb so the "interrupt on timeout" thread can notice cdb has terminated
    oCdbWrapper.oCdbLock.release();
  assert not oCdbWrapper.bCdbRunning, "Debugger did not terminate when requested";
Ejemplo n.º 8
0
def cCdbWrapper_fbDetectAndReportVerifierErrors(oCdbWrapper, asCdbOutput):
    uErrorNumber = None
    uProcessId = None
    sMessage = None
    uVerifierStopHeapBlockStartAddress = None
    uVerifierStopHeapBlockSize = None
    uCorruptedStamp = None
    uCorruptionAddress = None
    asRelevantLines = []

    for sLine in asCdbOutput:
        # Ignore exmpty lines
        if not sLine:
            continue
        # Look for the first VERIFIER STOP message and gather information
        if uErrorNumber is None:
            oErrorMessageMatch = re.match(
                r"^VERIFIER STOP ([0-9A-F]+): pid 0x([0-9A-F]+): (.*?)\s*$",
                sLine)
            if oErrorMessageMatch:
                sErrorNumber, sProcessId, sMessage = oErrorMessageMatch.groups(
                )
                uErrorNumber = long(sErrorNumber, 16)
                uProcessId = long(sProcessId, 16)
                asRelevantLines.append(sLine)
            continue
        # A VERIFIER STOP message has been detected, gather what information verifier provides:
        oInformationMatch = re.match(r"^\t([0-9A-F]+) : (.*?)\s*$", sLine)
        if oInformationMatch:
            asRelevantLines.append(sLine)
            sValue, sDescription = oInformationMatch.groups()
            uValue = long(sValue, 16)
            sDescription = sDescription.lower()
            # Both "Corruption address" and "corruption address" are used :(
            if sDescription == "heap block":
                uVerifierStopHeapBlockStartAddress = uValue
            elif sDescription == "block size":
                uVerifierStopHeapBlockSize = uValue
            elif sDescription == "corrupted stamp":
                uCorruptedStamp = uValue
            elif sDescription == "corruption address":
                uCorruptionAddress = uValue
        else:
            assert sLine.strip().replace("=", "") == "", \
                "Unknown VERIFIER STOP message line: %s\r\n%s" % (repr(sLine), "\r\n".join(asCdbOutput))
            break
    else:
        assert uErrorNumber is None, \
            "Detected the start of a VERIFIER STOP message but not the end\r\n%s" % "\r\n".join(asCdbOutput)
        return False
    if uErrorNumber == 0x303:
        # =======================================
        # VERIFIER STOP 0000000000000303: pid 0xB2C: NULL handle passed as parameter. A valid handle must be used.
        #
        # 0000000000000000 : Not used.
        # 0000000000000000 : Not used.
        # 0000000000000000 : Not used.
        # 0000000000000000 : Not used.
        #
        # =======================================
        # This is not interesting; do not report an error.
        return True

    assert uVerifierStopHeapBlockStartAddress is not None, \
        "The heap block start address was not found in the verifier stop message.\r\n%s" % "\r\n".join(asRelevantLines)
    assert uVerifierStopHeapBlockSize is not None, \
        "The heap block size was not found in the verifier stop message.\r\n%s" % "\r\n".join(asRelevantLines)

    oCorruptionDetector = cCorruptionDetector(oCdbWrapper)
    oPageHeapReport = cPageHeapReport.foCreate(
        oCdbWrapper, uVerifierStopHeapBlockStartAddress)
    if not oCdbWrapper.bCdbRunning: return None
    if oPageHeapReport:
        # Prefer page heap information over VERIFIER STOP info - the later has been known to be incorrect sometimes, for
        # instance when the application frees (heap pointer + offset), the VERIFIER STOP info will use that as the heap
        # block base, whereas the page heap report will correctly report (heap pointer) as the heap block base.
        uAllocationStartAddress = oPageHeapReport.uAllocationStartAddress
        uAllocationEndAddress = oPageHeapReport.uAllocationEndAddress
        # Check the page heap data near the heap block for signs of corruption:
        oPageHeapReport.fbCheckForCorruption(oCorruptionDetector)
        if not oCdbWrapper.bCdbRunning: return None
    if oPageHeapReport and oPageHeapReport.uBlockStartAddress:
        uHeapBlockStartAddress = oPageHeapReport.uBlockStartAddress
        uHeapBlockSize = oPageHeapReport.uBlockSize
        uHeapBlockEndAddress = uHeapBlockStartAddress + uHeapBlockSize
    else:
        uHeapBlockStartAddress = uVerifierStopHeapBlockStartAddress
        uHeapBlockSize = uVerifierStopHeapBlockSize
        uHeapBlockEndAddress = uVerifierStopHeapBlockStartAddress + uVerifierStopHeapBlockSize
    atxMemoryRemarks = []
    if oCdbWrapper.bGenerateReportHTML:
        uMemoryDumpStartAddress = uHeapBlockStartAddress
        uMemoryDumpSize = uVerifierStopHeapBlockSize
        if oPageHeapReport:
            atxMemoryRemarks.extend(oPageHeapReport.fatxMemoryRemarks())

    # Handle various VERIFIER STOP messages.
    sBugDescription = None
    if sMessage in ["corrupted start stamp", "corrupted end stamp"]:
        assert uCorruptionAddress is None, \
            "We do not expect the corruption address to be provided in this VERIFIER STOP message\r\n%s" % \
                "\r\n".join(asRelevantLines)
        if not oCorruptionDetector.bCorruptionDetected and uHeapBlockStartAddress != uVerifierStopHeapBlockStartAddress:
            # When the application attempts to free (heap pointer + offset), Verifier does not detect this and will assume
            # the application provided pointer is correct. This causes it to look for the start and end stamp in the wrong
            # location and report this bug as memory corruption. When the page heap data shows no signs of corruption, we
            # can special case it.
            iFreeAtOffset = uVerifierStopHeapBlockStartAddress - uHeapBlockStartAddress
            sBugTypeId = "MisalignedFree[%s]%s" % (fsGetNumberDescription(
                uHeapBlockSize), fsGetOffsetDescription(iFreeAtOffset))
            sOffsetBeforeOrAfter = iFreeAtOffset < 0 and "before" or "after"
            sBugDescription = "The application attempted to free memory using a pointer that was %d/0x%X bytes %s a " \
                "%d/0x%X byte heap block at address 0x%X" % (abs(iFreeAtOffset), abs(iFreeAtOffset), \
                sOffsetBeforeOrAfter, uHeapBlockSize, uHeapBlockSize, uHeapBlockStartAddress)
            sSecurityImpact = "Unknown: this type of bug has not been analyzed before"
        else:
            sBugTypeId = "OOBW[%s]" % (fsGetNumberDescription(uHeapBlockSize))
            assert oCorruptionDetector.bCorruptionDetected, \
                "Cannot find any sign of corruption"
    elif sMessage == "corrupted suffix pattern":
        assert uCorruptionAddress is not None, \
            "The corruption address is expected to be provided in this VERIFIER STOP message:\r\n%s" % \
                "\r\n".join(asRelevantLines)
        # Page heap stores the heap as close as possible to the edge of a page, taking into account that the start of the
        # heap block must be properly aligned. Bytes between the heap block and the end of the page are initialized to
        # 0xD0. Verifier has detected that one of the bytes changed value, which indicates an out-of-bounds write. BugId
        # will try to find all bytes that were changed:
        sBugTypeId = "OOBW[%s]" % (fsGetNumberDescription(uHeapBlockSize))
        assert oCorruptionDetector.bCorruptionDetected, \
            "Cannot find any sign of corruption"
    elif sMessage == "corrupted infix pattern":
        assert uCorruptionAddress is not None, \
            "The corruption address is expected to be provided in the VERIFIER STOP message:\r\n%s" % \
                "\r\n".join(asRelevantLines)
        # Page heap sometimes does not free a heap block immediately, but overwrites the bytes with 0xF0. Verifier has
        # detected that one of the bytes changed value, which indicates a write-after-free. BugId will try to find all
        # bytes that were changed:
        sBugTypeId = "UAFW[%s]" % (fsGetNumberDescription(uHeapBlockSize))
        # TODO add these checks to cPaheHeapReport if possible.
        oCorruptionDetector.fbDetectCorruption(
            uHeapBlockStartAddress, [0xF0 for x in xrange(uHeapBlockSize)])
        assert oCorruptionDetector.bCorruptionDetected, \
            "Cannot find any sign of corruption"
    else:
        sBugTypeId = "HeapCorrupt[%s]" % (
            fsGetNumberDescription(uHeapBlockSize))
    # sBugDescription is not set if this is a memory corruption

    # See if we have a better idea of where the corruption started and ended:
    if oCorruptionDetector.bCorruptionDetected:
        uCorruptionStartAddress = oCorruptionDetector.uCorruptionStartAddress
        uCorruptionEndAddress = oCorruptionDetector.uCorruptionEndAddress
        uCorruptionSize = uCorruptionEndAddress - uCorruptionStartAddress
        if oCdbWrapper.bGenerateReportHTML:
            atxMemoryRemarks.extend(oCorruptionDetector.fatxMemoryRemarks())


# I believe verifier may not check various memory areas in order from lowest to highest address. So it may not report
# the byte that got corrupted with the lowest address; therefore it may not make sense to compare what verifier
# reported with what our corruption detector found:
#    assert uCorruptionAddress is None or uCorruptionAddress == oCorruptionDetector.uCorruptionStartAddress, \
#        "Verifier reported corruption at address 0x%X but BugId detected corruption at address 0x%X\r\n%s" % \
#        (uCorruptionAddress, oCorruptionDetector.uCorruptionStartAddress, "\r\n".join(asRelevantLines));
        bCorruptionDetected = True
    elif uCorruptionAddress:
        if oCdbWrapper.bGenerateReportHTML:
            atxMemoryRemarks.append(
                ("Corrupted memory", uCorruptionAddress, None))
        uCorruptionStartAddress = uCorruptionAddress
        uCorruptionEndAddress = uCorruptionAddress
        bCorruptionDetected = True
    else:
        bCorruptionDetected = False

    if bCorruptionDetected:
        # If the corruption starts before or ends after the heap block, expand the memory dump to include the entire
        # corrupted region.
        if oCdbWrapper.bGenerateReportHTML:
            if uCorruptionStartAddress < uMemoryDumpStartAddress:
                uMemoryDumpSize += uMemoryDumpStartAddress - uCorruptionStartAddress
                uMemoryDumpStartAddress = uCorruptionStartAddress
            if uCorruptionEndAddress < uMemoryDumpStartAddress + uMemoryDumpSize:
                uMemoryDumpSize += uCorruptionEndAddress - (
                    uMemoryDumpStartAddress + uMemoryDumpSize)
        # Get a human readable description of the start offset of corruption relative to the heap block, where corruption
        # starting before or inside the heap block will be relative to the start, and corruption after it to the end.
        uCorruptionStartOffset = uCorruptionStartAddress - uHeapBlockStartAddress
        if uCorruptionStartOffset >= uHeapBlockSize:
            uCorruptionStartOffset -= uHeapBlockSize
            sCorruptionStartOffsetDescription = "%d/0x%X bytes beyond" % (
                uCorruptionStartOffset, uCorruptionStartOffset)
            sBugTypeId += fsGetOffsetDescription(uCorruptionStartOffset)
        elif uCorruptionStartOffset > 0:
            sCorruptionStartOffsetDescription = "%d/0x%X bytes into" % (
                uCorruptionStartOffset, uCorruptionStartOffset)
            sBugTypeId += "@%s" % fsGetNumberDescription(
                uCorruptionStartOffset)
        else:
            sCorruptionStartOffsetDescription = "%d/0x%X bytes before" % (
                -uCorruptionStartOffset, -uCorruptionStartOffset)
            sBugTypeId += fsGetOffsetDescription(uCorruptionStartOffset)
        sBugDescription = "Page heap detected heap corruption at 0x%X; %s a %d/0x%X byte heap block at address 0x%X" % \
            (uCorruptionStartAddress, sCorruptionStartOffsetDescription, uHeapBlockSize, uHeapBlockSize, uHeapBlockStartAddress)
        # If we detected corruption by scanning certain bytes in the applications memory, make sure this matches what
        # verifier reported and save all bytes that were affected: so far, we only saved the bytes that had an unexpected
        # value, but there is a chance that a byte was overwritten with the same value it has before, in which case it was
        # not saved. This can be detect if it is surrounded by bytes that did change value. This code reads the value of all
        # bytes between the first and last byte that we detected was corrupted:
        if oCorruptionDetector.bCorruptionDetected:
            asCorruptedBytes = oCorruptionDetector.fasCorruptedBytes()
            sBugDescription += " The following byte values were written to the corrupted area: %s." % ", ".join(
                asCorruptedBytes)
            sBugTypeId += oCorruptionDetector.fsCorruptionId() or ""
        sSecurityImpact = "Potentially exploitable security issue, if the corruption is attacker controlled"
    else:
        assert sBugDescription, \
            "sBugDescription should have been set"

    oBugReport = cBugReport.foCreate(oCdbWrapper, sBugTypeId, sBugDescription,
                                     sSecurityImpact)
    if not oCdbWrapper.bCdbRunning: return None
    if oCdbWrapper.bGenerateReportHTML:
        oBugReport.atxMemoryDumps.append(("Memory near heap block at 0x%X" % uMemoryDumpStartAddress, \
            uMemoryDumpStartAddress, uMemoryDumpSize))
        oBugReport.atxMemoryRemarks.extend(atxMemoryRemarks)
    # Output the VERIFIER STOP message for reference
    if oCdbWrapper.bGenerateReportHTML:
        sVerifierStopMessageHTML = sBlockHTMLTemplate % {
            "sName":
            "VERIFIER STOP message",
            "sCollapsed":
            "Collapsed",
            "sContent":
            "<pre>%s</pre>" % "\r\n".join([
                oCdbWrapper.fsHTMLEncode(s, uTabStop=8)
                for s in asRelevantLines
            ])
        }
        oBugReport.asExceptionSpecificBlocksHTML.append(
            sVerifierStopMessageHTML)
        # Output the page heap information for reference
        if oPageHeapReport:
            sPageHeapOutputHTML = sBlockHTMLTemplate % {
                "sName":
                "Page heap output for heap block at 0x%X" %
                uHeapBlockStartAddress,
                "sCollapsed":
                "Collapsed",
                "sContent":
                "<pre>%s</pre>" % "\r\n".join([
                    oCdbWrapper.fsHTMLEncode(s, uTabStop=8)
                    for s in oPageHeapReport.asPageHeapOutput
                ])
            }
            oBugReport.asExceptionSpecificBlocksHTML.append(
                sPageHeapOutputHTML)

    oBugReport.bRegistersRelevant = False
    oCdbWrapper.oBugReport = oBugReport
    return True
def cCdbWrapper_fbDetectAndReportVerifierErrors(oCdbWrapper, asCdbOutput):
    uErrorNumber = None
    uProcessId = None
    sMessage = None
    uHeapBlockAddress = None
    uHeapBlockSize = None
    uCorruptedStamp = None
    uCorruptionAddress = None
    asRelevantLines = []

    for sLine in asCdbOutput:
        # Ignore exmpty lines
        if not sLine:
            continue
        # Look for the first VERIFIER STOP message and gather information
        if uErrorNumber is None:
            oErrorMessageMatch = re.match(
                r"^VERIFIER STOP ([0-9A-F]+): pid 0x([0-9A-F]+): (.*?)\s*$",
                sLine)
            if oErrorMessageMatch:
                sErrorNumber, sProcessId, sMessage = oErrorMessageMatch.groups(
                )
                uErrorNumber = long(sErrorNumber, 16)
                uProcessId = long(sProcessId, 16)
            asRelevantLines.append(sLine)
            continue
        asRelevantLines.append(sLine)
        # A VERIFIER STOP message has been detected, gather what information verifier provides:
        oInformationMatch = re.match(r"\t([0-9A-F]+) : (.*?)\s*$", sLine)
        if oInformationMatch:
            sValue, sDescription = oInformationMatch.groups()
            uValue = long(sValue, 16)
            if sDescription == "Heap block": uHeapBlockAddress = uValue
            elif sDescription == "Block size": uHeapBlockSize = uValue
            elif sDescription == "Corrupted stamp": uCorruptedStamp = uValue
            elif sDescription == "corruption address":
                uCorruptionAddress = uValue
        else:
            assert sLine.strip().replace("=", "") == "", \
                "Unknown VERIFIER STOP message line: %s" % repr(sLine)
            break
    else:
        assert uErrorNumber is None, \
            "Detected the start of a VERIFIER STOP message but not the end\r\n%s" % "\r\n".join(asLines)
        return False

    uHeapBlockEndAddress = uHeapBlockAddress + uHeapBlockSize
    uHeapPageEndAddress = (uHeapBlockEndAddress | 0xFFF) + 1
    assert uHeapPageEndAddress >= uHeapBlockEndAddress, \
        "The heap block at 0x%X is expected to end at 0x%X, but the page is expected to end at 0x%X, which is impossible." % \
        (uHeapBlockAddress, uHeapBlockEndAddress, uHeapPageEndAddress)
    oCorruptionDetector = cCorruptionDetector(oCdbWrapper)
    # End of VERIFIER STOP message; report a bug.
    if sMessage in ["corrupted start stamp", "corrupted end stamp"]:
        assert uCorruptionAddress is None, \
            "We do not expect the corruption address to be provided in the VERIFIER STOP message"
        sBugTypeId = "OOBW"
        # Both the start and end stamp may have been corrupted and it appears that a bug in verifier causes a corruption
        # of the end stamp to be reported as a corruption of the start stamp, so we'll check both for unexpected values:
        uPointerSize = oCdbWrapper.fuGetValue("$ptrsize")
        if not oCdbWrapper.bCdbRunning: return
        # https://msdn.microsoft.com/en-us/library/ms220938(v=vs.90).aspx
        uEndStampAddress = uHeapBlockAddress - uPointerSize
        # ULONG with optional padding to pointer size
        if uPointerSize == 8:
            # End stamp comes immediately after other header values
            oCorruptionDetector.fDetectCorruption(uEndStampAddress, 0, 0, 0, 0,
                                                  [0xBA, 0xBB], 0xBB, 0xBA,
                                                  0xDC)
        else:
            oCorruptionDetector.fDetectCorruption(uEndStampAddress,
                                                  [0xBA, 0xBB], 0xBB, 0xBA,
                                                  0xDC)
        uStackTraceAddress = uEndStampAddress - uPointerSize
        # PVOID
        uFreeQueueAddress = uStackTraceAddress - 2 * uPointerSize
        # LIST_ENTRY
        uActualSizeAddress = uFreeQueueAddress - uPointerSize
        # size_t
        uRequestedSizeAddress = uActualSizeAddress - uPointerSize
        # size_t
        uHeapAddressAddress = uRequestedSizeAddress - uPointerSize
        # PVOID
        uStartStampAddress = uHeapAddressAddress - uPointerSize
        # ULONG with optional padding to pointer size
        if uPointerSize == 8:
            # End stamp comes immediately before other header values
            oCorruptionDetector.fDetectCorruption(uStartStampAddress,
                                                  [0xBA, 0xBB], 0xBB, 0xCD,
                                                  0xAB, 0, 0, 0, 0)
        else:
            oCorruptionDetector.fDetectCorruption(uStartStampAddress,
                                                  [0xBA, 0xBB], 0xBB, 0xCD,
                                                  0xAB)
        assert oCorruptionDetector.bCorruptionDetected, \
            "Cannot find any sign of corruption"
    elif sMessage == "corrupted suffix pattern":
        assert uCorruptionAddress is not None, \
            "The corruption address is expected to be provided in the VERIFIER STOP message:\r\n%s" % \
                "\r\n" % (asRelevantLines)
        # Page heap stores the heap as close as possible to the edge of a page, taking into account that the start of the
        # heap block must be properly aligned. Bytes between the heap block and the end of the page are initialized to
        # 0xD0. Verifier has detected that one of the bytes changed value, which indicates an out-of-bounds write. BugId
        # will try to find all bytes that were changed:
        sBugTypeId = "OOBW"
        uPaddingSize = uHeapPageEndAddress - uHeapBlockEndAddress
        oCorruptionDetector.fDetectCorruption(
            uHeapBlockEndAddress, *[0xD0 for x in xrange(uPaddingSize)])
        assert oCorruptionDetector.bCorruptionDetected, \
            "Cannot find any sign of corruption"
    elif sMessage == "corrupted infix pattern":
        assert uCorruptionAddress is not None, \
            "The corruption address is expected to be provided in the VERIFIER STOP message:\r\n%s" % \
                "\r\n" % (asRelevantLines)
        # Page heap sometimes does not free a heap block immediately, but overwrites the bytes with 0xF0. Verifier has
        # detected that one of the bytes changed value, which indicates a write-after-free. BugId will try to find all
        # bytes that were changed:
        sBugTypeId = "UAFW"
        oCorruptionDetector.fDetectCorruption(
            uHeapBlockAddress, *[0xF0 for x in xrange(uHeapBlockSize)])
        assert oCorruptionDetector.bCorruptionDetected, \
            "Cannot find any sign of corruption"
    else:
        sBugTypeId = "HeapCorrupt"
    if uHeapBlockSize is not None:
        sBugTypeId += "[%s]" % (fsGetNumberDescription(uHeapBlockSize))
    if oCorruptionDetector.bCorruptionDetected:
        if uCorruptionAddress is None:
            uCorruptionAddress = oCorruptionDetector.uCorruptionStartAddress
        else:
            assert uCorruptionAddress == oCorruptionDetector.uCorruptionStartAddress, \
                "Verifier reported corruption at address 0x%X but BugId detected corruption at address 0x%X" % \
                (uCorruptionAddress, oCorruptionDetector.uCorruptionStartAddress)
    if uCorruptionAddress is not None:
        sMessage = "heap corruption"
        uCorruptionOffset = uCorruptionAddress - uHeapBlockAddress
        if uCorruptionOffset >= uHeapBlockSize:
            uCorruptionOffset -= uHeapBlockSize
            sOffsetDescription = "%d/0x%X bytes beyond" % (uCorruptionOffset,
                                                           uCorruptionOffset)
            sBugTypeId += fsGetOffsetDescription(uCorruptionOffset)
        elif uCorruptionOffset > 0:
            sOffsetDescription = "%d/0x%X bytes into" % (uCorruptionOffset,
                                                         uCorruptionOffset)
            sBugTypeId += "@%s" % fsGetNumberDescription(uCorruptionOffset)
        else:
            sOffsetDescription = "%d/0x%X bytes before" % (-uCorruptionOffset,
                                                           -uCorruptionOffset)
            sBugTypeId += fsGetOffsetDescription(uCorruptionOffset)
        sBugDescription = "Page heap detected %s at 0x%X; %s a %d/0x%X byte heap block at address 0x%X" % \
            (sMessage, uCorruptionAddress, sOffsetDescription, uHeapBlockSize, uHeapBlockSize, uHeapBlockAddress)
        uRelevantAddress = uCorruptionAddress
    else:
        sBugDescription = "Page heap detected %s in a %d/0x%X byte heap block at address 0x%X." % \
            (sMessage, uHeapBlockSize, uHeapBlockSize, uHeapBlockAddress)
        uRelevantAddress = uHeapBlockAddress

    # If we detected corruption by scanning certain bytes in the applications memory, make sure this matches what
    # verifier reported and save all bytes that were affected: so far, we only saved the bytes that had an unexpected
    # value, but there is a chance that a byte was overwritten with the same value it has before, in which case it was
    # not saved. This can be detect if it is surrounded by bytes that did change value. This code reads the value of all
    # bytes between the first and last byte that we detected was corrupted:
    asCorruptedBytes = oCorruptionDetector.fasCorruptedBytes()
    if asCorruptedBytes:
        sBugDescription += " The following byte values were written to the corrupted area: %s." % ", ".join(
            asCorruptedBytes)
        sBugTypeId += oCorruptionDetector.fsCorruptionId() or ""

    sSecurityImpact = "Potentially exploitable security issue, if the corruption is attacker controlled"
    oCdbWrapper.oBugReport = cBugReport.foCreate(oCdbWrapper, sBugTypeId,
                                                 sBugDescription,
                                                 sSecurityImpact)
    oCdbWrapper.oBugReport.duRelevantAddress_by_sDescription \
        ["memory corruption at 0x%X" % uRelevantAddress] = uRelevantAddress
    oCdbWrapper.oBugReport.bRegistersRelevant = False
    return True