成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

C#托管堆遭破壞問題溯源分析

開發 前端
從代碼邏輯看,只要 Alloc(str)? 的線程棧上觸發了 GC 就是第一現場,Task 下的 GC.Collect(); 是第二現場,如果是前者目的就達到了。

一、背景

1. 講故事

年前遇到了好幾例托管堆被損壞的案例,有些運氣好一些,從被破壞的托管堆內存現場能觀測出大概是什么問題,但更多的情況下是無法做出準確判斷的,原因就在于生成的dump是第二現場,借用之前文章的一張圖,大家可以理解一下。

圖片圖片

為了幫助更多受此問題困擾的朋友,這篇來整理一下如何 快狠準 的抓取第一現場。

二、抓取第一現場

1. 思路分析

要想抓到第一現場,只需要讓破壞托管堆的那個線程在修改完之后,回到 CLR Pinvoke 層的時候主動觸發GC,因為這時候托管堆已經是損壞狀態了,程序也就會立即崩潰,破壞線程也就被捉jian在床,畫個圖如下:

圖片圖片

那如何讓 CLR:PInvoke 主動觸發GC呢?這就需要借助微軟的 MDA 托管調試助手,它有一個 gcUnmanagedToManaged 配置項就是專門做這件事情的,參考網址:https://learn.microsoft.com/zh-cn/dotnet/framework/debug-trace-profile/gcunmanagedtomanaged-mda

2. 如何配置 MDA

MDA 的配置非常簡單,大體上分兩步:

  1. 提交注冊表開啟MDA

這里使用注冊表的方式,需要注意的是,程序和操作系統位數一致的話采用如下方式。

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework]
"MDA"="1"

如果不一致,采用如下配置,比如 32bit 程序跑在 64bit 系統上。

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\.NETFramework]
"MDA"="1"

這里我用的是第二段內容,按照官方文檔描述,將內容保存到 MDAEnable.reg 中,然后在 注冊表編輯器 上導入即可。

圖片圖片

開啟應用程序級捕獲

為了能夠讓 gcUnmanagedToManaged 生效,需要新建應用程序打頭的配置文件,比如:Example_16_1_2.exe.mda.config,內容如下:

<mdaConfig>
 <assistants>
  <gcUnmanagedToManaged/>
 </assistants>
</mdaConfig>

完整截圖:

圖片圖片

這樣就算配置好了,當程序在 PInvoke 時,CLR 會讀取注冊表的 MDA 值,如果開啟的話就會讀取 config 中 gcUnmanagedToManaged 子節做相應的邏輯。

tips:如果配置不生效,保守一點的話,建議重啟下機器。

3. 一個托管堆破壞的測試案例

為了演示托管堆損壞,我準備將一個 string 傳給 C++,然后讓 C++ 溢出它來實現托管堆破壞。

C# 代碼如下:

namespace Example_16_1_2
{
    internal class Program
    {
        [DllImport("Example_16_1_3.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
        public extern static void Alloc(string str);

        static void Main(string[] args)
        {
            Test();

            Task.Factory.StartNew(() =>
            {
                Thread.Sleep(3000);
                GC.Collect();
            });

            Console.ReadLine();
        }

        static void Test()
        {
            var str = "hello";
            var str2 = "world!";

            Alloc(str);

        }
    }
}

C++ 代碼如下:

extern "C"
{
 _declspec(dllexport) void Alloc(wchar_t* c);
}

#include "iostream"
#include <Windows.h>
using namespace std;

void Alloc(wchar_t* c)
{
 for (size_t i = 0; i < 10; i++)
 {
  *c++ = 'a';
 }

 wprintf(L"%s \n", c);
}

從代碼邏輯看,只要 Alloc(str) 的線程棧上觸發了 GC 就是第一現場,Task 下的 GC.Collect(); 是第二現場,如果是前者目的就達到了。

激動人心的時刻到了,把程序跑起來后,由于程序崩潰,procdump 立即給我抓了一個 crash dump,截圖如下:

圖片圖片

接下來打開 windbg,從序幕信息看果然是 GC 清掃的時候出的問題,托管堆也是損壞狀態,信息如下:

Debug session time: Sun Jan 29 10:14:21.000 2023 (UTC + 8:00)
System Uptime: 0 days 1:14:11.423
Process Uptime: not available
.................................
Loading unloaded module list
..
This dump file has an exception of interest stored in it.
The stored exception information can be accessed via .ecxr.
(4460.52ac): Access violation - code c0000005 (first/second chance not available)
For analysis of this file, run !analyze -v
eax=00610060 ebx=00000000 ecx=02da23a4 edx=00000001 esi=02da2370 edi=02da2388
eip=79a6f2d1 esp=00d3ef64 ebp=00d3f104 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010206
clr!WKS::gc_heap::plan_phase+0x79b:
79a6f2d1 f70000000080    test    dword ptr [eax],80000000h ds:002b:00610060=????????

0:000> !VerifyHeap
Could not request method table data for object 02DA1228 (MethodTable: 0000000C).
Last good object: 02DA121C.
object 03da1020: bad member 02DA1228 at 03DA1098
Last good object: 03DA1010.
object 03da2338: bad member 02DA1228 at 03DA2340
Last good object: 03DA2328.
object 03da3568: bad member 02DA2364 at 03DA357C
Last good object: 03DA3558.
Failed to request SyncBlk at index 1.

那是不是主線程引發的GC呢?切過去便知。

0:000> ~0s
eax=00610060 ebx=00000000 ecx=02da23a4 edx=00000001 esi=02da2370 edi=02da2388
eip=79a6f2d1 esp=00d3ef64 ebp=00d3f104 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010206
clr!WKS::gc_heap::plan_phase+0x79b:
79a6f2d1 f70000000080    test    dword ptr [eax],80000000h ds:002b:00610060=????????
0:000> !clrstack
OS Thread Id: 0x52ac (0)
Child SP       IP Call Site
00d3f220 79a6f2d1 [HelperMethodFrame: 00d3f220] System.StubHelpers.StubHelpers.TriggerGCForMDA()
00d3f294 02bc0aa7 DomainBoundILStubClass.IL_STUB_PInvoke(System.String)
00d3f298 02bc09c9 [InlinedCallFrame: 00d3f298] Example_16_1_2.Program.Alloc(System.String)
00d3f2e0 02bc09c9 Example_16_1_2.Program.Test() [D:\testdump\Example\Example_16_1_2\Program.cs @ 35]
00d3f2f0 02bc0900 Example_16_1_2.Program.Main(System.String[]) [D:\testdump\Example\Example_16_1_2\Program.cs @ 19]
00d3f490 7996f036 [GCFrame: 00d3f490] 
0:000> k 10
 # ChildEBP RetAddr      
00 00d3f104 79a68153     clr!WKS::gc_heap::plan_phase+0x79b
01 00d3f124 79a6847b     clr!WKS::gc_heap::gc1+0xbc
02 00d3f13c 79a68585     clr!WKS::gc_heap::garbage_collect+0x367
03 00d3f15c 79b1ddbd     clr!WKS::GCHeap::GarbageCollectGeneration+0x1bd
04 00d3f16c 79b1de34     clr!WKS::GCHeap::GarbageCollectTry+0x71
05 00d3f198 79d20aed     clr!WKS::GCHeap::GarbageCollect+0xac
06 00d3f204 79d066c0     clr!TriggerGCForMDAInternal+0x7d
07 00d3f28c 02bc0aa7     clr!StubHelpers::TriggerGCForMDA+0x61
WARNING: Frame IP not in any known module. Following frames may be wrong.
08 00d3f2d8 02bc09c9     0x2bc0aa7
09 00d3f2e8 02bc0900     Example_16_1_2!Example_16_1_2.Program.Test+0x39 [D:\testdump\Example\Example_16_1_2\Program.cs @ 35] 
0a 00d3f318 7996f036     Example_16_1_2!Example_16_1_2.Program.Main+0x30 [D:\testdump\Example\Example_16_1_2\Program.cs @ 19] 
0b 00d3f324 799722da     clr!CallDescrWorkerInternal+0x34
0c 00d3f378 7997859b     clr!CallDescrWorkerWithHandler+0x6b
0d 00d3f3ec 79b1b11b     clr!MethodDescCallSite::CallTargetWorker+0x16a
0e 00d3f510 79b1b7fa     clr!RunMain+0x1b3
0f 00d3f77c 79b1b727     clr!Assembly::ExecuteMainMethod+0xf7

從線程棧上的 clr!StubHelpers::TriggerGCForMDA 來看,在 Pinvoke 層果然主動觸發了 GC,成功將 Program.Alloc 這個非托管方法捉jian在床。

責任編輯:武曉燕 來源: 一線碼農聊技術
相關推薦

2023-07-24 10:54:58

CLR優化空間

2023-11-01 08:07:42

.NETC#

2009-09-02 16:02:52

C#引用托管對象

2009-08-19 10:25:18

C#托管資源

2009-09-02 10:39:00

C#釋放托管資源

2009-08-28 16:43:08

AutoCAD托管C#

2014-01-22 09:54:09

網絡布線數據中心

2011-05-18 17:56:38

C#C++

2016-12-02 16:09:52

數據中心災難恢復

2010-09-29 14:21:22

2011-05-18 18:05:47

C#C++

2009-08-17 13:49:20

C#中調用Window

2023-01-03 11:22:23

C#代碼SQL Server

2010-06-11 17:13:34

MySQL表索引

2009-08-19 11:21:02

C# ListBox控

2009-08-26 16:46:06

C# ThreadSt

2009-08-27 13:27:50

C# this保留字

2009-08-26 10:34:59

C# Hashtabl

2009-09-01 09:16:57

C#使用SharpZi

2011-06-13 10:10:58

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 一区免费看 | www.国产.com| 丝袜毛片 | 精品亚洲一区二区三区四区五区 | 老司机精品福利视频 | 国产精品99久 | 亚洲欧美综合网 | 精品久久99 | 国产精品人人做人人爽 | 成人在线视 | 国产精品国产三级国产aⅴ无密码 | 日日噜噜噜夜夜爽爽狠狠视频97 | 久草.com| 久久久.com | 中文字幕日韩欧美一区二区三区 | h视频免费在线观看 | 国产精品不卡一区二区三区 | 欧美成人一区二区三区 | 亚洲国产欧美国产综合一区 | 欧美精品成人影院 | 国产日韩欧美在线 | 超碰网址 | 国产精品国产三级国产a | 中文字幕亚洲精品 | 国产精品久久久久无码av | 91网站视频在线观看 | 成人精品鲁一区一区二区 | 欧美日韩国产一区 | 91一区二区三区在线观看 | 中文字幕一区二区三区日韩精品 | 久久夜视频 | 99热播精品 | 欧美精品国产一区二区 | 每日更新av| 国产一区二区a | 国产xxx在线观看 | 国产精品成人在线播放 | 国产成人一区二区三区精 | 国产在线观看一区二区 | 欧美国产日韩在线 | 99福利|