一:背景
1. 讲故事
前段时间有位朋友找到我,说他们有一个崩溃的dump让我帮忙看下怎么回事,确实有太多的人在网上找各种故障分析最后联系到了我,还好我一直都是免费分析,不收取任何费用,造福社区。
话不多说,既然有 dump 来了,那就上 windbg 说话吧。
二:WinDbg 分析
1. 为什么会崩溃
说实话windbg非常强大,双击打开dump就能第一时间帮你显示出简略的异常信息,输出如下:
从卦中又看到了经典的 mark_object_simple1
方法,这个方法是GC用来做对象标记之用的,所以大概率又是托管堆损坏,真是无语了,接下来用 !verifyheap
检查下托管堆。
一看这卦就很不吉利,真的是有对象的mt是不对的,至此我们把崩溃的直接原因给找到了。
2. 为什么对象损坏了
要找到这个答案就需要深挖 00000218e96963d8
对象,分别使用 !do
命令以及 dp
来观察内存地址。
仔细观察卦中对象 00000218e9696450
所显示的mt,你会发现一个是 00007ffb35451300
,一个是 00007ffa35451301
,很显然前者是对的,后者是错的,可以分别用 !dumpmt
做个验证。
细心的朋友会发现虽然两个mt地址不一样,但已经非常相近,看样子又是一例经典的bit位翻转,我去,用 .formats
转成二进制观察一下,截图如下:
从卦中可以清晰的看到当前地址有两个 bit 的翻转,分别是第0位
和第32位
,接下来就要洞察为什么会有两个bit位的翻转?
3. 真的存在两个bit位翻转吗
接下来我们逐一来聊一下。
- bit 0 为什么会翻转
熟悉 coreclr 底层的朋友应该知道,gc 在标记的过程中会给 mt 的第0位设置为1,表示当前对象在深度优先中已经标记过,防止重复标记,当然这个也是有源码作证的,简化后的代码如下:
有了这段源码,这个 bit 为什么为 1 就能轻松的解释了,所以这个翻转是一个正常情况。
- bit 32 为什么会翻转
这个是我无法解释的,也正是因为这个 bit32 的翻转导致 gc 认为这个 obj 是一个损坏的对象,到底是什么原因呢?民间众说纷纭,在我的过往分析旅程中我已见过两例,但我不敢确定自己又遇到了辐射类的奇葩情况,所以也第一时间找朋友确认程序周边是否存在辐射环境。
朋友反馈过来附近有 伺服电机
类,说实话工控的东西我是真的不太懂,只能上网搜搜这玩意是否有辐射,截图如下:
到底是不是这玩意导致的,其实我心里也没底,跟朋友的沟通后说是只出现过一次,这就更加玄乎了。
不管怎么说,我只能给出如下两个方案:
- 上 ECC 纠错内存
- 远离辐射环境
三:总结
在大工控领域里,这是我见过第三例bit位翻转导致的程序崩溃,太无语了,恶魔到底是不是旁边的 伺服电机
? 希望领域内的同行们留言讨论下,让我长长见识,感谢!