1 /*
2 ** Zabbix
3 ** Copyright (C) 2001-2021 Zabbix SIA
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
14 **
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18 **/
19 
20 #include "common.h"
21 #include "log.h"
22 
23 #include <excpt.h>
24 #include <DbgHelp.h>
25 
26 #pragma comment(lib, "DbgHelp.lib")
27 
28 #define STACKWALK_MAX_NAMELEN	4096
29 
30 #define ZBX_LSHIFT(value, bits)	(((unsigned __int64)value) << bits)
31 
32 extern const char	*progname;
33 
34 #ifdef _M_X64
35 
36 #define ZBX_IMAGE_FILE_MACHINE	IMAGE_FILE_MACHINE_AMD64
37 
print_register(const char * name,unsigned __int64 value)38 static void	print_register(const char *name, unsigned __int64 value)
39 {
40 	zabbix_log(LOG_LEVEL_CRIT, "%-7s = %16I64x = %20I64u = %20I64d", name, value, value, value);
41 }
42 
print_fatal_info(CONTEXT * pctx)43 static void	print_fatal_info(CONTEXT *pctx)
44 {
45 	zabbix_log(LOG_LEVEL_CRIT, "====== Fatal information: ======");
46 
47 	zabbix_log(LOG_LEVEL_CRIT, "Program counter: 0x%08lx", pctx->Rip);
48 	zabbix_log(LOG_LEVEL_CRIT, "=== Registers: ===");
49 
50 	print_register("r8", pctx->R8);
51 	print_register("r9", pctx->R9);
52 	print_register("r10", pctx->R10);
53 	print_register("r11", pctx->R11);
54 	print_register("r12", pctx->R12);
55 	print_register("r13", pctx->R13);
56 	print_register("r14", pctx->R14);
57 	print_register("r15", pctx->R15);
58 
59 	print_register("rdi", pctx->Rdi);
60 	print_register("rsi", pctx->Rsi);
61 	print_register("rbp", pctx->Rbp);
62 
63 	print_register("rbx", pctx->Rbx);
64 	print_register("rdx", pctx->Rdx);
65 	print_register("rax", pctx->Rax);
66 	print_register("rcx", pctx->Rcx);
67 
68 	print_register("rsp", pctx->Rsp);
69 	print_register("efl", pctx->EFlags);
70 	print_register("csgsfs", ZBX_LSHIFT(pctx->SegCs, 24) | ZBX_LSHIFT(pctx->SegGs, 16) | ZBX_LSHIFT(pctx->SegFs, 8));
71 }
72 
73 #else
74 
75 #define ZBX_IMAGE_FILE_MACHINE	IMAGE_FILE_MACHINE_I386
76 
print_register(const char * name,unsigned __int32 value)77 static void	print_register(const char *name, unsigned __int32 value)
78 {
79 	zabbix_log(LOG_LEVEL_CRIT, "%-7s = %16lx = %20lu = %20ld", name, value, value, value);
80 }
81 
print_fatal_info(CONTEXT * pctx)82 static void	print_fatal_info(CONTEXT *pctx)
83 {
84 	zabbix_log(LOG_LEVEL_CRIT, "====== Fatal information: ======");
85 
86 	zabbix_log(LOG_LEVEL_CRIT, "Program counter: 0x%04x", pctx->Eip);
87 	zabbix_log(LOG_LEVEL_CRIT, "=== Registers: ===");
88 
89 	print_register("edi", pctx->Edi);
90 	print_register("esi", pctx->Esi);
91 	print_register("ebp", pctx->Ebp);
92 
93 	print_register("ebx", pctx->Ebx);
94 	print_register("edx", pctx->Edx);
95 	print_register("eax", pctx->Eax);
96 	print_register("ecx", pctx->Ecx);
97 
98 	print_register("esp", pctx->Esp);
99 	print_register("efl", pctx->EFlags);
100 	print_register("csgsfs", ZBX_LSHIFT(pctx->SegCs, 24) | ZBX_LSHIFT(pctx->SegGs, 16) | ZBX_LSHIFT(pctx->SegFs, 8));
101 }
102 
103 #endif
104 
105 typedef BOOL (WINAPI *SymGetLineFromAddrW64_func_t)(HANDLE, DWORD64, PDWORD, PIMAGEHLP_LINE64);
106 typedef BOOL (WINAPI *SymFromAddr_func_t)(HANDLE a, DWORD64 b , PDWORD64 c, PSYMBOL_INFO d);
107 
zbx_backtrace(void)108 void	zbx_backtrace(void)
109 {
110 }
111 
print_backtrace(CONTEXT * pctx)112 static void	print_backtrace(CONTEXT *pctx)
113 {
114 	SymGetLineFromAddrW64_func_t	zbx_SymGetLineFromAddrW64 = NULL;
115 	SymFromAddr_func_t		zbx_SymFromAddr	= NULL;
116 
117 	CONTEXT			ctx, ctxcount;
118 	STACKFRAME64		s, scount;
119 	PSYMBOL_INFO		pSym = NULL;
120 	HMODULE			hModule;
121 	HANDLE			hProcess, hThread;
122 	DWORD64			offset;
123 	wchar_t			szProcessName[MAX_PATH];
124 	char			*process_name = NULL, *process_path = NULL, *frame = NULL;
125 	size_t			frame_alloc = 0, frame_offset;
126 	int			nframes = 0;
127 
128 	ctx = *pctx;
129 
130 	zabbix_log(LOG_LEVEL_CRIT, "=== Backtrace: ===");
131 
132 	memset(&s, 0, sizeof(s));
133 
134 	s.AddrPC.Mode = AddrModeFlat;
135 	s.AddrFrame.Mode = AddrModeFlat;
136 	s.AddrStack.Mode = AddrModeFlat;
137 
138 #ifdef _M_X64
139 	s.AddrPC.Offset = ctx.Rip;
140 	s.AddrFrame.Offset = ctx.Rbp;
141 	s.AddrStack.Offset = ctx.Rsp;
142 #else
143 	s.AddrPC.Offset = ctx.Eip;
144 	s.AddrFrame.Offset = ctx.Ebp;
145 	s.AddrStack.Offset = ctx.Esp;
146 #endif
147 	hProcess = GetCurrentProcess();
148 	hThread = GetCurrentThread();
149 
150 	if (0 != GetModuleFileNameEx(hProcess, NULL, szProcessName, ARRSIZE(szProcessName)))
151 	{
152 		char	*ptr;
153 		size_t	path_alloc = 0, path_offset = 0;
154 
155 		process_name = zbx_unicode_to_utf8(szProcessName);
156 
157 		if (NULL != (ptr = strstr(process_name, progname)))
158 			zbx_strncpy_alloc(&process_path, &path_alloc, &path_offset, process_name, ptr - process_name);
159 	}
160 
161 	if (NULL != (hModule = GetModuleHandle(TEXT("DbgHelp.DLL"))))
162 	{
163 		zbx_SymGetLineFromAddrW64 = (SymGetLineFromAddrW64_func_t)GetProcAddress(hModule,
164 				"SymGetLineFromAddr64");
165 		zbx_SymFromAddr = (SymFromAddr_func_t)GetProcAddress(hModule, "SymFromAddr");
166 	}
167 
168 	if (NULL != zbx_SymFromAddr || NULL != zbx_SymGetLineFromAddrW64)
169 	{
170 		SymSetOptions(SymGetOptions() | SYMOPT_LOAD_LINES);
171 
172 		if (FALSE != SymInitialize(hProcess, process_path, TRUE))
173 		{
174 			pSym = (PSYMBOL_INFO) zbx_malloc(NULL, sizeof(SYMBOL_INFO) + MAX_SYM_NAME);
175 			memset(pSym, 0, sizeof(SYMBOL_INFO) + MAX_SYM_NAME);
176 			pSym->SizeOfStruct = sizeof(SYMBOL_INFO);
177 			pSym->MaxNameLen = MAX_SYM_NAME;
178 		}
179 	}
180 
181 	scount = s;
182 	ctxcount = ctx;
183 
184 	/* get number of frames, ctxcount may be modified during StackWalk64() calls */
185 	while (TRUE == StackWalk64(ZBX_IMAGE_FILE_MACHINE, hProcess, hThread, &scount, &ctxcount, NULL, NULL, NULL,
186 			NULL))
187 	{
188 		if (0 == scount.AddrReturn.Offset)
189 			break;
190 		nframes++;
191 	}
192 
193 	while (TRUE == StackWalk64(ZBX_IMAGE_FILE_MACHINE, hProcess, hThread, &s, &ctx, NULL, NULL, NULL, NULL))
194 	{
195 		frame_offset = 0;
196 		zbx_snprintf_alloc(&frame, &frame_alloc, &frame_offset, "%d: %s", nframes--,
197 				NULL == process_name ? "(unknown)" : process_name);
198 
199 		if (NULL != pSym)
200 		{
201 			DWORD		dwDisplacement;
202 			IMAGEHLP_LINE64	line = {sizeof(IMAGEHLP_LINE64)};
203 
204 			zbx_chrcpy_alloc(&frame, &frame_alloc, &frame_offset, '(');
205 			if (NULL != zbx_SymFromAddr &&
206 					TRUE == zbx_SymFromAddr(hProcess, s.AddrPC.Offset, &offset, pSym))
207 			{
208 				zbx_snprintf_alloc(&frame, &frame_alloc, &frame_offset, "%s+0x%lx", pSym->Name, offset);
209 			}
210 
211 			if (NULL != zbx_SymGetLineFromAddrW64 && TRUE == zbx_SymGetLineFromAddrW64(hProcess,
212 					s.AddrPC.Offset, &dwDisplacement, &line))
213 			{
214 				zbx_snprintf_alloc(&frame, &frame_alloc, &frame_offset, " %s:%d", line.FileName,
215 						line.LineNumber);
216 			}
217 			zbx_chrcpy_alloc(&frame, &frame_alloc, &frame_offset, ')');
218 		}
219 
220 		zabbix_log(LOG_LEVEL_CRIT, "%s [0x%lx]", frame, s.AddrPC.Offset);
221 
222 		if (0 == s.AddrReturn.Offset)
223 			break;
224 	}
225 
226 	SymCleanup(hProcess);
227 
228 	zbx_free(frame);
229 	zbx_free(process_path);
230 	zbx_free(process_name);
231 	zbx_free(pSym);
232 }
233 
zbx_win_exception_filter(unsigned int code,struct _EXCEPTION_POINTERS * ep)234 int	zbx_win_exception_filter(unsigned int code, struct _EXCEPTION_POINTERS *ep)
235 {
236 	zabbix_log(LOG_LEVEL_CRIT, "Unhandled exception %x detected at 0x%p. Crashing ...", code,
237 			ep->ExceptionRecord->ExceptionAddress);
238 
239 	print_fatal_info(ep->ContextRecord);
240 	print_backtrace(ep->ContextRecord);
241 
242 	zabbix_log(LOG_LEVEL_CRIT, "================================");
243 
244 	return EXCEPTION_CONTINUE_SEARCH;
245 }
246