Find Out Why Thunderbird Constantly Crashing

Find Out Why Thunderbird Constantly Crashing

Background

I’m currently having two PCs in use: one for working and one for personal use. Both of them have Windows 10 and Mozilla Thunderbird installed.

Since I have various email accounts in use, so I make two instances of Mozilla Thunderbird share the same copy of profile to make my accounts management easier.

Phenomenon

Recently, I found when I first start up my PCs, my Mozilla Thunderbird crashes and Mozilla Thunderbird ‘s crash reporter window shows up.

Mozilla Crash Reporter

After I clicked Restart Thunderbird button, Mozilla Thunderbird also crashes.

Trace

The problem needs to be solved, so I use WinDbg to launch Mozilla Thunderbird and simply waiting for Mozilla Thunderbird to crash.

After a while, WinDbg interprets Mozilla Thunderbird‘s execution and notifies that a bug has occurred.

1
2
3
4
5
6
7
8
9
10
(37bc.15c4): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
*** WARNING: Unable to verify checksum for ToastNotification.dll
*** ERROR: Symbol file could not be found. Defaulted to export symbols for ToastNotification.dll -
eax=132587c8 ebx=00000009 ecx=00000000 edx=00000000 esi=132587c8 edi=00000002
eip=170d1110 esp=00a7bc78 ebp=00a7bc84 iopl=0 nv up ei pl nz na po nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00210202
ToastNotification+0x1110:
170d1110 668b02 mov ax,word ptr [edx] ds:002b:00000000=????

It’s can be seen from the snippet that the bug was caused by ToastNotification.dll and it’s a null pointer dereference bug.

To find out what ToastNotification.dll is, I need to list its detail information.

1
2
3
4
5
6
7
8
9
10
start    end        module name
170d0000 170ef000 ToastNotification C (export symbols) ToastNotification.dll
Loaded symbol image file: ToastNotification.dll
Image path: "Profile Path"\extensions\jid1-OoNOA6XBjznvLQ@jetpack\resources\gnotifier\data\ToastNotification.dll
Image name: ToastNotification.dll
Browse all global symbols functions data
Timestamp: Fri May 15 22:17:24 2015 (5555FFF4)
CheckSum: 00000000
ImageSize: 0001F000
Translations: 0000.04b0 0000.04e4 0409.04b0 0409.04e4

Well, ToastNotification.dll is part of the extension GNotifier

By far, I can draw the conclusion that GNotifier‘s bug causes my Thunderbird to crash.

Further Investigation

Since I knew the source which causes Thunderbird to crash, it’s may be interesting to finding the root problem.

First, print the stack trace might help

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
WARNING: Stack unwind information not available. Following frames may be wrong.
00 00a7bc84 170d1b65 00000000 0dc56660 0dc56680 ToastNotification+0x1110
01 00a7bca4 0465b85d 00000000 0dc56660 0dc56680 ToastNotification!DisplayToastNotification+0x75
02 00a7bcc4 0465b250 0465b2e0 00a7bcf4 00000002 xul!cbrt+0x2e9d
03 00a7bd04 045071f2 0de05580 170d1af0 05b057b4 xul!cbrt+0x2890
04 00a7bd68 04511277 05b4f280 05b4f280 0319b9d1 xul!js::ElementAdder::appendHole+0x31b2
05 00a7bd9c 04433645 05b05630 05b0563c 05b05640 xul!js::ElementAdder::appendHole+0xd237
06 00a7bdc8 0442ea38 24661190 05b4f280 04cdf128 xul!js_GetSCOffset+0x175
07 00a7be04 04505652 05b4f280 00a7be34 00a7be1c xul!JS_NewSharedUint8ClampedArrayWithBuffer+0x1da8
08 00a7be34 0448a764 05b4f280 00000006 00a7c0b0 xul!js::ElementAdder::appendHole+0x1612
09 00a7be54 04363abf 05b4f280 00000010 00000000 xul!JS_NewUint8ClampedArrayWithBuffer+0x344b4
0a 00a7be80 04353bf5 05b4f280 00000000 00000001 xul!js::ZoneGlobalsAreAllGray+0x6a5f
0b 00a7bea0 04353cf1 05b4f290 00a7c8e4 24661190 xul!js::NewFunctionWithReserved+0x375
0c 00a7bec0 04519bf4 05b4f280 00a7bed8 05b4f28c xul!js::NewFunctionWithReserved+0x471
0d 00000000 00000000 00000000 00000000 00000000 xul!js::ElementAdder::appendHole+0x15bb4

Now, I need to print some detail information.

1
2
3
4
5
6
7
8
9
10
0:000> u ToastNotification+0x1110
ToastNotification+0x1110:
170d1110 668b02 mov ax,word ptr [edx]
170d1113 83c202 add edx,2
170d1116 6685c0 test ax,ax
170d1119 75f5 jne ToastNotification+0x1110 (170d1110)
170d111b 8b1d0c010e17 mov ebx,dword ptr [ToastNotification!CloseToastNotification+0xe55c (170e010c)]
170d1121 8d460c lea eax,[esi+0Ch]
170d1124 2bd7 sub edx,edi
170d1126 50 push eax
1
2
0:000> r @edx
edx=00000000

The register edx is the null pointer, so it’s time to trace where edx‘s value came from.

1
2
3
4
5
6
7
8
9
10
11
12
0:000> u ToastNotification+0x1100 L10
ToastNotification+0x1100:
170d1100 55 push ebp
170d1101 8bec mov ebp,esp
170d1103 53 push ebx
170d1104 56 push esi
170d1105 8bf1 mov esi,ecx
170d1107 8b4d08 mov ecx,dword ptr [ebp+8]
170d110a 8bd1 mov edx,ecx
170d110c 57 push edi
170d110d 8d7a02 lea edi,[edx+2]
170d1110 668b02 mov ax,word ptr [edx]

It can been seen ToastNotification+0x1100 is a function, and edx‘s value came from this function’s first parameter.

Since I don’t have ToastNotification.dll‘s debug symbol and I can not find some valuable clue, adopt a different strategy might help.

I decide to use IDA Pro to analyze ToastNotification.dll to see if it would help.

After dragged ToastNotification.dll into IDA Pro‘s window and jumped to the corresponding address, I indeed find something valuable.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
.text:10001100 ; int __stdcall sub_10001100(int, int, int, wchar_t *Src)
.text:10001100 sub_10001100 proc near ; CODE XREF: DisplayToastNotification+70p
.text:10001100
.text:10001100 arg_0 = dword ptr 8
.text:10001100 arg_4 = dword ptr 0Ch
.text:10001100 arg_8 = dword ptr 10h
.text:10001100 Src = dword ptr 14h
.text:10001100
.text:10001100 push ebp
.text:10001101 mov ebp, esp
.text:10001103 push ebx
.text:10001104 push esi
.text:10001105 mov esi, ecx
.text:10001107 mov ecx, [ebp+arg_0]
.text:1000110A mov edx, ecx
.text:1000110C push edi
.text:1000110D lea edi, [edx+2]
.text:10001110
.text:10001110 loc_10001110: ; CODE XREF: sub_10001100+19j
.text:10001110 mov ax, [edx]
.text:10001113 add edx, 2
.text:10001116 test ax, ax
.text:10001119 jnz short loc_10001110
.text:1000111B mov ebx, ds:WindowsCreateString
.text:10001121 lea eax, [esi+0Ch]
.text:10001124 sub edx, edi
.text:10001126 push eax
.text:10001127 sar edx, 1
.text:10001129 push edx
.text:1000112A push ecx
.text:1000112B call ebx ; WindowsCreateString
.text:1000112D test eax, eax
.text:1000112F jns short loc_1000113A
.text:10001131
.text:10001131 loc_10001131: ; CODE XREF: sub_10001100+5Bj
.text:10001131 ; sub_10001100+7Ej ...
.text:10001131 pop edi
.text:10001132 pop esi
.text:10001133 xor al, al
.text:10001135 pop ebx
.text:10001136 pop ebp
.text:10001137 retn 10h
.text:1000113A ; ---------------------------------------------------------------------------
.text:1000113A
.text:1000113A loc_1000113A: ; CODE XREF: sub_10001100+2Fj
.text:1000113A mov edx, [ebp+arg_4]
.text:1000113D mov ecx, edx
.text:1000113F lea edi, [ecx+2]
.text:10001142
.text:10001142 loc_10001142: ; CODE XREF: sub_10001100+4Bj
.text:10001142 mov ax, [ecx]
.text:10001145 add ecx, 2
.text:10001148 test ax, ax
.text:1000114B jnz short loc_10001142
.text:1000114D sub ecx, edi
.text:1000114F lea eax, [esi+10h]
.text:10001152 push eax
.text:10001153 sar ecx, 1
.text:10001155 push ecx
.text:10001156 push edx
.text:10001157 call ebx ; WindowsCreateString
.text:10001159 test eax, eax
.text:1000115B js short loc_10001131
.text:1000115D mov edx, [ebp+arg_8]
.text:10001160 mov ecx, edx
.text:10001162 lea edi, [ecx+2]
.text:10001165
.text:10001165 loc_10001165: ; CODE XREF: sub_10001100+6Ej
.text:10001165 mov ax, [ecx]
.text:10001168 add ecx, 2
.text:1000116B test ax, ax
.text:1000116E jnz short loc_10001165
.text:10001170 sub ecx, edi
.text:10001172 lea eax, [esi+14h]
.text:10001175 push eax
.text:10001176 sar ecx, 1
.text:10001178 push ecx
.text:10001179 push edx
.text:1000117A call ebx ; WindowsCreateString
.text:1000117C test eax, eax
.text:1000117E js short loc_10001131
.text:10001180 mov eax, [ebp+Src]
.text:10001183 test eax, eax
.text:10001185 jz short loc_10001197
.text:10001187 push eax ; Src
.text:10001188 call sub_10002B3E
.text:1000118D add esp, 4
.text:10001190 mov [esi+18h], eax
.text:10001193 test eax, eax
.text:10001195 jz short loc_10001131
.text:10001197
.text:10001197 loc_10001197: ; CODE XREF: sub_10001100+85j
.text:10001197 call ds:GetCurrentThreadId
.text:1000119D pop edi
.text:1000119E mov [esi+1Ch], eax
.text:100011A1 mov al, 1
.text:100011A3 pop esi
.text:100011A4 pop ebx
.text:100011A5 pop ebp
.text:100011A6 retn 10h
.text:100011A6 sub_10001100 endp

There are three WindowsCreateString function calls in the function, since GNotifier is an open source app, I can investigate its source code directly.

After searching WindowsCreateString globally in the project, I finally found the following code snippet.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// GNotifier/win8_shim/src/ToastNotification.cpp
bool
ToastNotificationHandler::Init(const wchar_t* aImage, const wchar_t* aTitle, const wchar_t* aMessage, const wchar_t* aName)
{
HRESULT hr;
hr = WindowsCreateString(aImage, (UINT32)wcslen(aImage), &mImage);
if (FAILED(hr)) {
return false;
}
hr = WindowsCreateString(aTitle, (UINT32)wcslen(aTitle), &mTitle);
if (FAILED(hr)) {
return false;
}
hr = WindowsCreateString(aMessage, (UINT32)wcslen(aMessage), &mMessage);
if (FAILED(hr)) {
return false;
}
if (aName) {
mName = _wcsdup(aName);
if (!mName) {
return false;
}
}

mThreadId = GetCurrentThreadId();

return true;
}

It’s now clear that the function did not check whether its parameters is null or not, so the bug occurs.

It’s time to find out why parameters go null, so I need to trace how Init is called. I finally found the target.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// GNotifier/win8_shim/src/ToastNotification.cpp

// external API to show toast notification via JS-CTYPES
DllExport
bool
DisplayToastNotification(const wchar_t* aImage, const wchar_t* aTitle, const wchar_t* aMessage, const wchar_t* aName, void* aCallbackActive, void* aCallbackDismiss)
{
if (!sAppId) {
return false;
}

ToastNotificationHandler* handler = new ToastNotificationHandler();

if (!handler->Init(aImage, aTitle, aMessage, aName)) {
delete handler;
return false;
}

if (!handler->DisplayNotification((EventCallback)aCallbackActive, (EventCallback)aCallbackDismiss)) {
delete handler;
return false;
}

sNotifications.push_back(handler);

return true;
}

Please note the comment external API to show toast notification via JS-CTYPES, by searching the project again, I finally found

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// Do a real notification
windows.notify = function(iconURL, title, text, notifier, closeHandler, clickHandler){
try{
const clickCallbackType = ctypes.FunctionType(ctypes.stdcall_abi, ctypes.void_t).ptr;
const closeCallbackType = ctypes.FunctionType(ctypes.stdcall_abi, ctypes.void_t).ptr;
var DisplayToastNotification = winToast.declare("DisplayToastNotification",
ctypes.winapi_abi,
ctypes.bool,
ctypes.jschar.ptr,
ctypes.jschar.ptr,
ctypes.jschar.ptr,
ctypes.jschar.ptr,
clickCallbackType,
closeCallbackType);

var closeCallback = null;
if (closeHandler && typeof(closeHandler)==="function") {
closeCallback = closeCallbackType(function(){
closeHandler();
});
callbackFunArray.push(closeCallback);
}

var clickCallback = null;
if (clickHandler && typeof(clickHandler)==="function") {
clickCallback = clickCallbackType(function(){
clickHandler();
});
callbackFunArray.push(clickCallback);
}

return DisplayToastNotification(iconURL, title, text, notifier, clickCallback, closeCallback);

} catch(e){
console.log(e);
return false; }
}

By reading the code below, It’s clear how the bug occurs:

  1. GNotifier uses js-ctypes to register DisplayToastNotification to notify messages in Windows;
  2. For some notifications don’t have an icon, the iconURL parameter will be null;
  3. DisplayToastNotification simply allocate a new ToastNotificationHandler instance and the new object’s Init interface;
  4. The Init interface does not check its parameters and simply passes those parameters to WindowsCreateString even if some of all of them are null pointers;
  5. The whole application crashes.

Another interesting result can also be observed: the DisplayToastNotification‘s prototype already knew, so the message that causes the application to crash can also be found out by simply print out its parameters.

1
2
0:000> du poi(@ebp + 0xC)
0dc56660 "SOGO : Contacts"
1
2
0:000> du poi(@ebp + 0x10)
0dc56680 "No changes."
1
2
0:000> du poi(@ebp + 0x14)
0dc566a0 "Thunderbird"

Well, it’s SOGO: an extension used for accessing online calendars.

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×