经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » ASP.net » 查看文章
记一次 .NET某上位视觉程序 离奇崩溃分析
来源:cnblogs  作者:一线码农  时间:2024/7/12 10:23:35  对本文有异议

一:背景

1. 讲故事

前段时间有位朋友找到我,说他们有一个崩溃的dump让我帮忙看下怎么回事,确实有太多的人在网上找各种故障分析最后联系到了我,还好我一直都是免费分析,不收取任何费用,造福社区。

话不多说,既然有 dump 来了,那就上 windbg 说话吧。

二:WinDbg 分析

1. 为什么会崩溃

说实话windbg非常强大,双击打开dump就能第一时间帮你显示出简略的异常信息,输出如下:

  1. This dump file has an exception of interest stored in it.
  2. The stored exception information can be accessed via .ecxr.
  3. (bf8.5dc4): Access violation - code c0000005 (first/second chance not available)
  4. For analysis of this file, run !analyze -v
  5. clr!WKS::gc_heap::mark_object_simple1+0x220:
  6. 00007ffb`380453c4 833a00 cmp dword ptr [rdx],0 ds:00007ffa`35451300=????????

从卦中又看到了经典的 mark_object_simple1 方法,这个方法是GC用来做对象标记之用的,所以大概率又是托管堆损坏,真是无语了,接下来用 !verifyheap 检查下托管堆。

  1. 0:083> !verifyheap
  2. object 00000218e96963d8: bad member 00000218E9696450 at 00000218E9696420
  3. Last good object: 00000218E96963C0.
  4. Could not request method table data for object 00000218E9696450 (MethodTable: 00007FFA35451300).
  5. Last good object: 00000218E96963D8.

一看这卦就很不吉利,真的是有对象的mt是不对的,至此我们把崩溃的直接原因给找到了。

2. 为什么对象损坏了

要找到这个答案就需要深挖 00000218e96963d8 对象,分别使用 !do 命令以及 dp 来观察内存地址。

  1. 0:083> !do 00000218e96963d8
  2. Name: System.Threading.Tasks.Task+DelayPromise
  3. MethodTable: 00007ffb3542b3e8
  4. EEClass: 00007ffb3567c7c0
  5. Size: 120(0x78) bytes
  6. File: C:\Windows\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
  7. Fields:
  8. ...
  9. 00007ffb35451300 40035d5 48 ...m.Threading.Timer 0 instance 00000218e9696450 Timer
  10. 0:083> dp 00000218e9696450 L6
  11. 00000218`e9696450 00007ffa`35451301 00000000`00000000
  12. 00000218`e9696460 00000218`e96964c8 00000000`00000000
  13. 00000218`e9696470 00007ffb`353e4b51 00000218`e9696368

仔细观察卦中对象 00000218e9696450 所显示的mt,你会发现一个是 00007ffb35451300,一个是 00007ffa35451301,很显然前者是对的,后者是错的,可以分别用 !dumpmt 做个验证。

  1. 0:083> !dumpmt 00007ffb35451300
  2. EEClass: 00007ffb356942f0
  3. Module: 00007ffb353b1000
  4. Name: System.Threading.Timer
  5. mdToken: 0000000002000504
  6. File: C:\Windows\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
  7. BaseSize: 0x20
  8. ComponentSize: 0x0
  9. Slots in VTable: 23
  10. Number of IFaces in IFaceMap: 1
  11. 0:083> !dumpmt 00007ffa35451301
  12. 00007ffa35451301 is not a MethodTable

细心的朋友会发现虽然两个mt地址不一样,但已经非常相近,看样子又是一例经典的bit位翻转,我去,用 .formats 转成二进制观察一下,截图如下:

从卦中可以清晰的看到当前地址有两个 bit 的翻转,分别是第0位和第32位,接下来就要洞察为什么会有两个bit位的翻转?

3. 真的存在两个bit位翻转吗

接下来我们逐一来聊一下。

  1. bit 0 为什么会翻转

熟悉 coreclr 底层的朋友应该知道,gc 在标记的过程中会给 mt 的第0位设置为1,表示当前对象在深度优先中已经标记过,防止重复标记,当然这个也是有源码作证的,简化后的代码如下:

  1. inline BOOL gc_heap::gc_mark(uint8_t* o, uint8_t* low, uint8_t* high, int condemned_gen)
  2. {
  3. if ((o >= low) && (o < high))
  4. {
  5. BOOL already_marked = marked(o);
  6. if (already_marked)
  7. {
  8. return FALSE;
  9. }
  10. set_marked(o);
  11. return TRUE;
  12. }
  13. }
  14. #define marked(i) header(i)->IsMarked()
  15. BOOL IsMarked() const
  16. {
  17. return !!(((size_t)RawGetMethodTable()) & GC_MARKED);
  18. }

有了这段源码,这个 bit 为什么为 1 就能轻松的解释了,所以这个翻转是一个正常情况。

  1. bit 32 为什么会翻转

这个是我无法解释的,也正是因为这个 bit32 的翻转导致 gc 认为这个 obj 是一个损坏的对象,到底是什么原因呢?民间众说纷纭,在我的过往分析旅程中我已见过两例,但我不敢确定自己又遇到了辐射类的奇葩情况,所以也第一时间找朋友确认程序周边是否存在辐射环境。

朋友反馈过来附近有 伺服电机 类,说实话工控的东西我是真的不太懂,只能上网搜搜这玩意是否有辐射,截图如下:

到底是不是这玩意导致的,其实我心里也没底,跟朋友的沟通后说是只出现过一次,这就更加玄乎了。

不管怎么说,我只能给出如下两个方案:

  • 上 ECC 纠错内存
  • 远离辐射环境

三:总结

在大工控领域里,这是我见过第三例bit位翻转导致的程序崩溃,太无语了,恶魔到底是不是旁边的 伺服电机 ? 希望领域内的同行们留言讨论下,让我长长见识,感谢!

图片名称

原文链接:https://www.cnblogs.com/huangxincheng/p/18297750

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号