浏览器M55内核升级最后一个崩溃

最近做M55内核升级,我负责浏览器的稳定性。陆陆续续把各种崩溃都修复的差不多了,只剩下最后一个崩溃了,其崩溃堆栈如下: 在执行这行代码的时候发生了崩溃: 初步一看,这个崩溃点在Chromium原生的代码里面,所以怀疑是Chromium自己的bug。于是在https://bugs.chromium.org/p/chromium/issues/list里面搜索相关堆栈关键词,看看能不能找到类似的崩溃。很遗憾的是并没有搜到有用的信息。 然后再去https://groups.google.com/a/chromium.org/forum/#!forum/chromium-dev里面碰碰运气,仍然是一无所获。看来是指望不上Chromium社区的帮助了,只能自己动手,丰衣足食。 因为我们抓的崩溃dump只包含栈上的内存,另外浏览器Release版本做了编译器优化,导致很多栈上变量值都优化的不可见。因此崩溃dump里面包含的有用信息并不多,只能看到浏览器崩溃在哪行代码,却不能观察到浏览器崩溃时所处的状态,如Windbg里面查看到变量信息下图所示: 为此,通过Alias函数引用到栈上变量,防止它被优化,Alias函数如下图所示: 然后把自己关心的信息,都存到栈上分配的内存上,比如我想知道崩溃的时候请求的url、cookie等信息,如下图所示: 加上这些收集调试信息的代码,再灰度出去收集新的dump。通过分析新的dump果然有了一些有用的信息。可以看到浏览器崩溃的时候first_party_for_cookies对于的url都是天猫、淘宝的商品页面,而此时网络请求的url也都是https://gm.mmstat.com/tbdetail的url,如下图所示: 可以确定浏览器都是访问淘宝天猫出现的崩溃,但是我本地试了试,并不能复现问题。为了找到可以复现的崩溃场景,我在浏览器崩溃的时候,利用QQ的tencent://协议自动打开QQ跟我联系,代码如下: 继续灰度出去一版,等待崩溃的用户联系我。 我这边继续分析之前的崩溃dump,可以排除是空指针崩溃。发现cookie_store变量很有意思,其中一个dump如下: 可以看到cookie_store指向的net::CookieStore对象的虚函数表地址竟然是0xe43a00f2,已经跑到系统的地址空间里面去了,显然超出了合法的地址范围。而有的dump则是这样: 虽然虚函数表地址是正常的,但是其中虚函数指针都是错的,我猜可能是UAF(Use... Read More | Share it now!

Breakpad崩溃报告系统介绍

Breakpad是Google开发的一套开源的崩溃报告系统,它是跨平台的,支持Windows、Linux、Mac等操作系统,被Chrome、Firefox等各大软件采用。最近做Chromium内核升级,需要把Breakpad升级成Crashpad,故再看看Breakpad项目的实现,写篇博客总结一下。 Breakpad的机制 如上图所示是Breakpad的工作机制,它是参考Windows上那套崩溃处理机制。Windows上面编译器把代码编译成可执行文件,同时生成包含调试信息的PDB符号文件。程序运行发生崩溃时,将崩溃的堆栈等信息存储成一个dump文件。调试器打开dump文件并读取PDB里面的调试信息,就可以看到崩溃的堆栈。因为Breakpad是跨平台的,所以在Linux等其他平台也实现了Windows上的生成调试符号、生成dump、处理dump等功能。 Breakpad分为3个主要的模块: Client。Client模块会被编译到客户端程序中,它的职责包括捕获客户端的异常、生成崩溃的dump文件、上报dump文件到服务器。在Windows平台,我们只需要这部分就足够了。 Symbol... Read More | Share it now!

避免编译器优化掉代码

c++代码编译成Release版本,编译器会做一些优化,生成的二进制可执行文件执行更快,体积更小。这种优化会复用栈上不同变量的空间,导致调试的时候,有些局部变量看不到了。比如如下代码: 在调试的的时候,调试器直接运行到printf(“%d”,... Read More | Share it now!

劫持浏览器首页越来越嚣张了

昨晚下班回家有个网友找我,说他的浏览器打开百度总是不停的刷新,让我远程给他看看。 然后就看看,在用户那里用浏览器打开百度果然是不停的刷新,切换到IE内核就正常了。然后用Fiddler抓个包看看,chrome内核下面浏览器打开百度会不停的刷新https://www.baidu.com/”,看网络包的内容,内容是: 百度的https网络包竟然被篡改了,... Read More | Share it now!

DbgHelp教程4——Minidump自定义信息

MiniDumpWriteDump的MINIDUMP_TYPE可以控制往dump里面写入什么信息。不过MINIDUMP_TYPE只是一些固定的类型信息,比如内存、句柄、进程、线程信息,有时候为了定位问题,我们想收集一些其他的数据,然后写入到dump里面。恰好PMINIDUMP_USER_STREAM_INFORMATION参数可以做这个事情。 注意:自定义信息的MINIDUMP_USER_STREAM_INFORMATION的MINIDUMP_USER_STREAM的Type值必须大于MINIDUMP_STREAM_TYPE::LastReservedStream,因为之前这些值都是保留的标准类型。 可惜的是Windbg不能直接读取MINIDUMP_USER_STREAM_INFORMATION的信息,我们只好通过MiniDumpReadDumpStream接口去读取dump里面的数据。 根据MINIDUMP_STREAM_TYPE的描述,我们还可以从dump文件里面读取一些预置的信息,比如线程列表、模块列表、内存列表、异常信息、系统信息、句柄信息、函数表信息、等等。   ... Read More | Share it now!

DbgHelp教程3——Minidump

Minidump文件用来事后调试。DbgHelp库提供写Minidump的MiniDumpWriteDump方法。其实不光是程序崩溃的时候可以写dump,运行的任何时候都可以写个dump。 虽然名称是MiniDump,但有可能写的dump文件比FullDump都大,因为dump里面包含的信息不一样。MiniDumpWriteDump包含了一个MINIDUMP_TYPE参数,指定了dump类型: MiniDumpNormal。默认类型,只有当前线程的调用栈 MiniDumpWithDataSegs。所有加载模块的数据段 MiniDumpWithFullMemory。进程所有可达的内存,这个生成的dump文件非常大 MiniDumpWithHandleData。包含句柄信息 以下就是一个生成dump的例子: 往往我们希望在程序发生未处理的异常导致崩溃的时候来生成dump,因此我们可以注册这些这些异常的处理回调函数,在回调函数里面生成dump。 最常用的是SetUnhandledExceptionFilter函数,当程序发生未处理的SEH异常的时候,就会回调注册的处理函数。还有_set_invalid_parameter_handler函数,它可以注册CRT检测到的无效参数异常处理函数。_set_purecall_handler它可以注册纯虚函数调用异常处理函数。更多的异常函数,可以参考http://www.codeproject.com/Articles/207464/Exception-Handling-in-Visual-Cplusplus。 chromium里面的崩溃收集系统使用的Breakpad,跨平台,设计精良。有空写一些文章来介绍。 ... Read More | Share it now!

DbgHelp教程2——栈回溯

调试的时候经常用到栈回溯来定位问题。获得栈回溯并不难,直接通过CaptureStackBackTrace这个API就可以获得。另外可以可以通过DbgHelp库里面的StackWalk64函数获得。 用CaptureStackBackTrace获取调用栈很简单: StackWalk64的方法稍微复杂一点,首先要获得CONTEXT。获取CONTEXT有两种办法,一是通过内联汇编获取: 另外一种是调用RtlCaptureContext获取: 获取到CONTEXT之后就用StackWalk64遍历调用栈: 我们获得的调用栈都只是一些栈帧地址,如果要知道明确的函数名,还需要把地址翻译成符号名,封了一个PrintCallStackBackTrace,就是把栈帧地址翻译成可读的符号地址:   ... Read More | Share it now!