翼度科技»论坛 编程开发 .net 查看内容

记一次 .NET某实验室自动进样系统 崩溃分析

4

主题

4

帖子

12

积分

新手上路

Rank: 1

积分
12
一:背景

1. 讲故事

前些天有位朋友在微信上联系到我,说他们的程序在客户那边崩掉了,让我帮忙看下怎么回事,dump也拿到了,那就上手分析吧。
二:WinDbg 分析

1. 哪里的崩溃

既然是程序的崩溃,自然是有原因的,皮裤套棉裤,必定有缘故,不是皮裤太薄就是棉裤没毛,用 !analyze -v 观察下异常信息。
  1. 0:107> !analyze -v
  2. CONTEXT:  (.ecxr)
  3. rax=0000005e0dc7c4a0 rbx=0000005e0dc7c400 rcx=0000005e0dc7c4a0
  4. rdx=0000000000000000 rsi=0000005e0dc7c3f0 rdi=0000005e0dc7c4a0
  5. rip=00007ffb1ecfc223 rsp=0000005e0dc7c3c0 rbp=0000005e0dc7c4c0
  6. r8=00000000000004d0  r9=0000000000000000 r10=0000000000000000
  7. r11=0000005e0dc7c4a0 r12=0000000000000000 r13=000002079d450220
  8. r14=000002079b93aba0 r15=0000000000000000
  9. iopl=0         nv up ei pl nz na pe nc
  10. cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000200
  11. coreclr!EEPolicy::HandleFatalError+0x7f:
  12. 00007ffb`1ecfc223 488d442440      lea     rax,[rsp+40h]
  13. Resetting default scope
  14. EXCEPTION_RECORD:  (.exr -1)
  15. ExceptionAddress: 00007ffb1ec6d70f (coreclr!ProcessCLRException+0x00000000000d9f7f)
  16.    ExceptionCode: c0000005 (Access violation)
  17.   ExceptionFlags: 00000001
  18. NumberParameters: 0
复制代码
从卦中信息看这是一个经典的 访问违例,但崩溃在 EEPolicy::HandleFatalError 处就有点匪夷所思了,HandleFatalError 方法主要是用来在抛异常之前修整异常上下文的,这个方法固若金汤,一般不会出问题的,但不管怎么样,还是看下 rsp+40h 到底是什么东西。
  1. 0:107> dp rsp+40h L1
  2. 0000005e`0dc7c400  00000001`c0000005
复制代码
上面的 c0000005 很显然是访问违例,看样子这里有点混乱,也不是第一崩溃现场,这里就不过多纠结了,那怎么去找真正的崩溃点呢?还有一个方法就是去找 RaiseException 或者 KiUserExceptionDispatch 返回点之前的有用函数,参考如下:
  1. 0:107> .ecxr
  2. 0:107> k
  3.   *** Stack trace for last set context - .thread/.cxr resets it
  4. # Child-SP          RetAddr               Call Site
  5. 00 0000005e`0dc7c3c0 00007ffb`1ec6d72e     coreclr!EEPolicy::HandleFatalError+0x7f [D:\a\_work\1\s\src\coreclr\vm\eepolicy.cpp @ 776]
  6. 01 0000005e`0dc7c9d0 00007ffb`5235292f     coreclr!ProcessCLRException+0xd9f9e [D:\a\_work\1\s\src\coreclr\vm\exceptionhandling.cpp @ 1036]
  7. 02 0000005e`0dc7cc00 00007ffb`52302554     ntdll!RtlpExecuteHandlerForException+0xf
  8. 03 0000005e`0dc7cc30 00007ffb`5235143e     ntdll!RtlDispatchException+0x244
  9. 04 0000005e`0dc7d340 00000000`6c942893     ntdll!KiUserExceptionDispatch+0x2e
  10. 05 0000005e`0dc7daf0 00007ffa`c066ed7b     libxxx_manage!get_clean_xxx
  11. 06 0000005e`0dc7db70 00007ffa`c06b73a4     0x00007ffa`c066ed7b
  12. ...
复制代码
从卦中看,程序崩溃在 libxxx_manage!get_clean_xxx 中,看样子是一个 C++ 写的动态链接库,这就有点无语了。。。
2. C++ 库为什么会崩

要想寻找答案,最好的办法就是观察 000000006c942893 处的汇编代码,参考如下:
  1. 0:107> ub 00000000`6c942893
  2. libxxx_manage!get_clean_xxx:
  3. 00000000`6c942876 55              push    rbp
  4. 00000000`6c942877 53              push    rbx
  5. 00000000`6c942878 4883ec68        sub     rsp,68h
  6. 00000000`6c94287c 488dac2480000000 lea     rbp,[rsp+80h]
  7. 00000000`6c942884 48894d00        mov     qword ptr [rbp],rcx
  8. 00000000`6c942888 c745dc00000000  mov     dword ptr [rbp-24h],0
  9. 00000000`6c94288f 488b4500        mov     rax,qword ptr [rbp]
  10. 0:107> u 00000000`6c942893
  11. 00000000`6c942893 488b00          mov     rax,qword ptr [rax]
  12. 0:107> dp rbp L1
  13. 0000005e`0dc7c4c0  00000000`00000000
复制代码
从上面的汇编代码来看,这是 get_clean_xxx 方法的序幕代码,问题出在 rbp 的内容为0上,但 rbp 又来自于 rcx,根据 x64调用协定,rcx 即方法的第一个参数,看样子是这个参数为 null 导致的,参考如下:
  1. 0:107> !address rcx
  2. Usage:                  Stack
  3. Base Address:           0000005e`0dc78000
  4. End Address:            0000005e`0dc80000
  5. Region Size:            00000000`00008000 (  32.000 kB)
  6. State:                  00001000          MEM_COMMIT
  7. Protect:                00000004          PAGE_READWRITE
  8. Type:                   00020000          MEM_PRIVATE
  9. Allocation Base:        0000005e`0db00000
  10. Allocation Protect:     00000004          PAGE_READWRITE
  11. More info:              ~107k
  12. 0:107> dp rcx L1
  13. 0000005e`0dc7c4a0  00000000`00000000
复制代码
3. get_clean_xxx 参数为null吗

这个问题比较简单,继续用 !clrstack 观察下 Pinvoke 之上的 C# 代码。
  1. 0:107> !clrstack
  2. OS Thread Id: 0x3508 (107)
  3.         Child SP               IP Call Site
  4. 0000005E0DC7DBA0 00007ffac066ed7b [InlinedCallFrame: 0000005e0dc7dba0] xxx_LibPInvoke.xxx_clean_query(IntPtr)
  5. 0000005E0DC7DB70 00007ffac066ed7b ILStubClass.IL_STUB_PInvoke(IntPtr)
  6. 0000005E0DC7DC30 00007ffac06b73a4 xx+c__DisplayClass11_0.<xxxQueryClean>b__0(IntPtr)
  7. ...
复制代码
接下来就是看下托管层的 C# 代码是如何写的,截图如下:

从图中可以清楚的看到,xxxChannel 传给C++ 的时候没有判断是否为null,导致崩溃的发生,那还有没有其他的佐证呢?其实也是有的,如果符号给力还可以使用 !clrstack -a 去找到 xxxChannel 传下去的值。
  1. 0:107> !clrstack -a
  2. OS Thread Id: 0x3508 (107)
  3.         Child SP               IP Call Site
  4. 0000005E0DC7DBA0 00007ffac066ed7b [InlinedCallFrame: 0000005e0dc7dba0] xxx_LibPInvoke.xxx_clean_query(IntPtr)
  5. 0000005E0DC7DB70 00007ffac066ed7b ILStubClass.IL_STUB_PInvoke(IntPtr)
  6.     PARAMETERS:
  7.         <no data>
  8. 0000005E0DC7DC30 00007ffac06b73a4 xxx+c__DisplayClass11_0.<xxxQueryClean>b__0(IntPtr)
  9.     PARAMETERS:
  10.         this (0x0000005E0DC7DC80) = 0x0000020a9d9ca8d8
  11.         xxxChannel (0x0000005E0DC7DC88) = 0x0000000000000000
  12.     LOCALS:
  13.         0x0000005E0DC7DC6C = 0x0000000000000000
  14.         0x0000005E0DC7DC68 = 0x0000000000000000
复制代码
可以清楚的看到确实是 0,到这里就一切真相大白,对参数加一个判断即可,那这东西到底是谁的责任呢?我觉得双方都有问题吧。

  • 写托管层的人有点飘。
  • 写非托管层的人未作防御性编程,还是年轻太相信人了。
三:总结

这次生产事故彻底破坏了两个语言团队之间的相互合作的信任度,信任重建可就难了,不怕神一样的对手,就怕猪猪一样的队友,放在这里还是挺合适的,哈哈,开个小玩笑。

来源:https://www.cnblogs.com/huangxincheng/p/18382459
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

举报 回复 使用道具