Chromium installer介绍

mini_installer是Chromium的安装和升级系统的一部分。它的有两个主要作用: 从自身资源中抽取出chrome.packed.7z和setup.exe程序 调用setup.exe进行实际的安装操作 mini_installer很小 mini_installer之所以叫mini,是因为它真的很小。除去资源的大小,它本身大约只有5kb,而我们创建一个空的控制台的程序也有100多kb。 mini_installer做的这么小的原因是:Chromium的差量升级包实际上也是一个mini_installer。为了使差量包尽可能的小,所以mini_installer本身也要非常小。 mini_installer只是一个壳,本身并没有很复杂的逻辑,它是调用setup.exe做真正的安装和升级逻辑。另外它还没有链接CRT库。没有链接CRT库带来了一个副作用就是不能调用CRT库里面的函数,只能调用Windows... Read More | Share it now!

调试Utility进程崩溃

M55内核升级中发现Utility进程崩溃很多。以前Utility进程的崩溃只占到总崩溃量1%不到,现在却增加到了10%以上。 Utility进程崩溃的堆栈都很类似,如下: 我们看0号线程,也就是主线程。这个调用栈没有wMain函数,却有exit函数。可以看出来程序崩溃在进程退出的时候。最后的我们的代码崩溃帧是在BrokerServicesBase::~BrokerServicesBase里面,其代码如下: 我认为0号线程是正常的。我们再看1号线程。1号线程是程序发生异常,生成dump的线程。可以看到是ntdll!TppWaiterpThread引发了ntdll!KiUserExceptionDispatcher。好奇怪,异常竟然来自系统模块。我们看下Windbg的dump分析: 可以看到ExceptionCode的值为c000070a,查一下ntstatus.h头文件,值c000070a表示STATUS_THREADPOOL_HANDLE_EXCEPTION,意思是线程池等待的句柄异常,可以看到句柄是0000012c。 然后用!handle命令行查看一下进程当前拥有的句柄情况: 可以看到进程句柄表里面并没有0000012c句柄。因此我猜测可能是因为0000012c句柄已经关闭了,而TppWaiterpThread还在使用0000012c这个无效的句柄导致的异常。 因此我想收集一下句柄分配和释放的信息,然后根据dump查看一下无效的句柄是怎么分配和释放的。幸好chromium里面有一个ActiveVerifier机制,ActiveVerifier是用来跟踪句柄使用情况的,它跟踪了一部分句柄创建的和Hook了CloseHandle函数。在StartTracking和CloseHandle里面加一个我自己写的LogHandle函数,这个函数会把句柄分配和释放的StackTrace跟进句柄值记录到环境变量中。 因为Utility进程创建和释放的句柄并不多,所以把这个调试信息放在环境变量中是方便和合适的。 灰度出去,收集崩溃信息。这次果然收集到了有用的信息,先看dump分析: 这次异常的句柄值是00000198,再看看环境变量中的调试信息: 00000198果然被CloseHandle了。然后根据后面的地址查看关闭的调用栈: 从代码里面可以看到00000198句柄已经随着ChildProcess的析构而关闭变成无效了,其他地方继续使用这个句柄是有问题的。 00000198句柄是ChildProcess的shutdown_event_成员变量。这个shutdown_event_会被ChildThreadImpl传递给其IPC::SyncChannel的channel_成员。而SyncChannel的shutdown_watcher_会调用系统的RegisterWaitForSingleObject去利用线程池去等待00000198句柄对应Event。 所以问题的根源是ChildProcess的析构函数CloseHandle了shutdown_event_,但是SyncChannel里面的shutdown_watcher_通过RegisterWaitForSingleObject继续等待shutdown_event_句柄。MSDN里RegisterWaitForSingleObjec介绍说:If... Read More | Share it now!

浏览器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!

Chromium架构之启动

总结一下Chromium浏览器从wWinMain到浏览器循环开始的这段逻辑。 wWinMain Win32程序的入口都是wWinMain,Chromium浏览器也不例外。 初始化Crashpad 一开始就调用SignalInitializeCrashReporting去初始化Crashpad崩溃报告系统。这里会创建一个ChromeCrashReporterClient的对象,用来处理crash相关的逻辑。初始化完成之后就会启动一个crashpad-handler的进程。其他浏览器进程崩溃后,这个crashpad-handler进程就会为它们生成dump。 fast... Read More | Share it now!

创建新的Chromium extension api

不同编程语言的开发效率是不一样的,比如c++可以实现一些很底层的功能,但是开发速度比较慢,对开发者的要求也很高。而javascript开发速度很快,编程的抽象层次很高,无法调用一些系统底层的api。于是我们想到可以把c++与javascript结合起来,c++给javascript提供一些底层的api接口,javascript调用c++的接口并专注于业务开发,这样岂不两全其美。 事实上真有这样做的,比如Chromium浏览器,浏览器本身是用c++编写的,提供了很多extension... Read More | Share it now!

Chromium UI框架view

如果把widget当作画板,那么view就相当于铺在画板上的画纸。widget上面不会画什么东西,我们的界面元素都是画在view上,然后view又盖在widget上面。 在non_client_view.h的注视里面可以看到以下的内容: 可以看到最底层是Widget,然后RootView盖在Widget上面。RootView包含了NonClientView,而NonClientView又包含NonClientFrameView和ClientView。View不仅可以显示元素,而且可以当作容器包含其他的View。所有的View形成了一个树状的层次结构,低底层的根是RootView。 RootView附着在一个Widget上面,Widget从操作系统里面接受事件,转换成与View兼容的事件,然后再传递给RootView。RootView则按照树状层次结构一层层的传递事件。 听起来还是很抽象,我们可以实际动手试一试,我们利用views::PrintViewHierarchy(this)打印出RootView的View... Read More | Share it now!

Chromium预置搜索引擎

Chromium是Google主导一个开源浏览器项目,而Google是以搜索引擎起家的,因此Chromium里面搜索是个很重要的功能,给它增加了很多特性。我写篇博客介绍一下Chromium里面的搜索功能。 预置的搜索引擎 在浏览器的地址栏里面输入chrome://settings/searchEngines就可以管理搜索引擎。里面有一些预置的搜索引擎,你可以删除它们也可以添加一些你自定义的搜索引擎:如下图所示: 预置的搜索引擎是由Chromium代码prepopulated_engines.json定义的,这个文件的注释值得一读。文件其实就是包含了json格式的一些数据,在编译的时候会动态生成prepopulated_engine.h/cc文件。值得注意的是:每次修改过prepopulated_engines.json文件里面的数据都需要自增int_variables.kCurrentDataVersion的值,否则你的修改可能不会生效。原因是浏览器运行之后会把预置搜索引擎的数据保存在Web... Read More | Share it now!

Chromium UI框架widget

很早之前就想把Chromium的UI框架总结一遍。知道一个事物并向别人描述清楚并非是一件容易的事情,而且它又是如此的复杂,我没有把握把Chromium的UI框架给别人讲清楚,只有尽力而为。 我准备分三部分来讲Chromium的UI框架。首先是Widget,它是View的宿主,也是连接操作系统原生的UI系统的枢纽,同时还与CC(Chrome... 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!