一:背景
1. 讲故事
前些天有位朋友在微信上联系到我,说他们的程序在客户那边崩掉了,让我帮忙看下怎么回事,dump也拿到了,那就上手分析吧。
二:WinDbg 分析
1. 哪里的崩溃
既然是程序的崩溃,自然是有原因的,皮裤套棉裤,必定有缘故,不是皮裤太薄就是棉裤没毛,用 !analyze -v
观察下异常信息。
从卦中信息看这是一个经典的 访问违例
,但崩溃在 EEPolicy::HandleFatalError
处就有点匪夷所思了,HandleFatalError 方法主要是用来在抛异常之前修整异常上下文的,这个方法固若金汤,一般不会出问题的,但不管怎么样,还是看下 rsp+40h
到底是什么东西。
上面的 c0000005
很显然是访问违例,看样子这里有点混乱,也不是第一崩溃现场,这里就不过多纠结了,那怎么去找真正的崩溃点呢?还有一个方法就是去找 RaiseException
或者 KiUserExceptionDispatch
返回点之前的有用函数,参考如下:
从卦中看,程序崩溃在 libxxx_manage!get_clean_xxx
中,看样子是一个 C++ 写的动态链接库,这就有点无语了。。。
2. C++ 库为什么会崩
要想寻找答案,最好的办法就是观察 000000006c942893
处的汇编代码,参考如下:
从上面的汇编代码来看,这是 get_clean_xxx 方法的序幕代码,问题出在 rbp 的内容为0上,但 rbp 又来自于 rcx,根据 x64调用协定,rcx 即方法的第一个参数,看样子是这个参数为 null 导致的,参考如下:
3. get_clean_xxx 参数为null吗
这个问题比较简单,继续用 !clrstack
观察下 Pinvoke 之上的 C# 代码。
接下来就是看下托管层的 C# 代码是如何写的,截图如下:
从图中可以清楚的看到,xxxChannel 传给C++ 的时候没有判断是否为null,导致崩溃的发生,那还有没有其他的佐证呢?其实也是有的,如果符号给力还可以使用 !clrstack -a
去找到 xxxChannel
传下去的值。
可以清楚的看到确实是 0,到这里就一切真相大白,对参数加一个判断即可,那这东西到底是谁的责任呢?我觉得双方都有问题吧。
- 写托管层的人有点飘。
- 写非托管层的人未作防御性编程,还是年轻太相信人了。
三:总结
这次生产事故彻底破坏了两个语言团队之间的相互合作的信任度,信任重建可就难了,不怕神一样的对手,就怕猪猪一样的队友,放在这里还是挺合适的,哈哈,开个小玩笑。