Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

455 lines
13 KiB

  1. /*++
  2. Copyright (c) 2000-2002 Microsoft Corporation
  3. Module Name:
  4. IgnoreException.cpp
  5. Abstract:
  6. This shim is for handling exceptions that get thrown by bad apps.
  7. The primary causes of unhandled exceptions are:
  8. 1. Priviliged mode instructions: cli, sti, out etc
  9. 2. Access violations
  10. In most cases, ignoring an Access Violation will be fatal for the app,
  11. but it works in some cases, eg:
  12. Deer Hunter 2 - their 3d algorithm reads too far back in a lookup
  13. buffer. This is a game bug and doesn't crash win9x because that memory
  14. is usually allocated.
  15. Interstate 76 also requires a Divide by Zero exception to be ignored.
  16. Notes:
  17. This is a general purpose shim.
  18. History:
  19. 02/10/2000 linstev Created
  20. 10/17/2000 maonis Bug fix - now it ignores AVs correctly.
  21. 02/27/2001 robkenny Converted to use CString
  22. 02/15/2002 robkenny Shim was copying data into a temp buffer without verifying
  23. that the buffer was large enough.
  24. Cleaned up some signed/unsigned comparison mismatch.
  25. --*/
  26. #include "precomp.h"
  27. IMPLEMENT_SHIM_BEGIN(IgnoreException)
  28. #include "ShimHookMacro.h"
  29. APIHOOK_ENUM_BEGIN
  30. APIHOOK_ENUM_END
  31. // Exception code for OutputDebugString
  32. #define DBG_EXCEPTION 0x40010000L
  33. // Determine how to manage second chance exceptions
  34. BOOL g_bWin2000 = FALSE;
  35. DWORD g_dwLastEip = 0;
  36. extern DWORD GetInstructionLengthFromAddress(LPBYTE pEip);
  37. typedef enum
  38. {
  39. eActive = 0,
  40. eFirstChance,
  41. eSecondChance,
  42. eExitProcess
  43. } EMODE;
  44. WCHAR * ToWchar(EMODE emode)
  45. {
  46. switch (emode)
  47. {
  48. case eActive:
  49. return L"Active";
  50. case eFirstChance:
  51. return L"FirstChance";
  52. case eSecondChance:
  53. return L"SecondChance";
  54. case eExitProcess:
  55. return L"ExitProcess";
  56. };
  57. return L"ERROR";
  58. }
  59. // Convert a text version of EMODE to a EMODE value
  60. EMODE ToEmode(const CString & csMode)
  61. {
  62. if (csMode.Compare(L"0") == 0 || csMode.Compare(ToWchar(eActive)) == 0)
  63. {
  64. return eActive;
  65. }
  66. else if (csMode.Compare(L"1") == 0 || csMode.Compare(ToWchar(eFirstChance)) == 0)
  67. {
  68. return eFirstChance;
  69. }
  70. else if (csMode.Compare(L"2") == 0 || csMode.Compare(ToWchar(eSecondChance)) == 0)
  71. {
  72. return eSecondChance;
  73. }
  74. else if (csMode.Compare(L"3") == 0 || csMode.Compare(ToWchar(eExitProcess)) == 0)
  75. {
  76. return eExitProcess;
  77. }
  78. // Default value
  79. return eFirstChance;
  80. }
  81. static const DWORD DONT_CARE = 0xFFFFFFFF;
  82. /*++
  83. This is the list of all the exceptions that this shim can handle. The fields are
  84. 1. cName - the name of the exception as accepted as a parameter and
  85. displayed in debug spew
  86. 2. dwCode - the exception code
  87. 3. dwSubCode - parameters specified by the exception: -1 = don't care
  88. 4. dwIgnore - ignore this exception:
  89. 0 = don't ignore
  90. 1 = ignore 1st chance
  91. 2 = ignore 2nd chance
  92. 3 = exit process on 2nd chance.
  93. --*/
  94. struct EXCEPT
  95. {
  96. WCHAR * cName;
  97. DWORD dwCode;
  98. DWORD dwSubCode;
  99. EMODE dwIgnore;
  100. };
  101. static EXCEPT g_eList[] =
  102. {
  103. {L"ACCESS_VIOLATION_READ" , (DWORD)EXCEPTION_ACCESS_VIOLATION , 0 , eActive},
  104. {L"ACCESS_VIOLATION_WRITE" , (DWORD)EXCEPTION_ACCESS_VIOLATION , 1 , eActive},
  105. {L"ARRAY_BOUNDS_EXCEEDED" , (DWORD)EXCEPTION_ARRAY_BOUNDS_EXCEEDED , DONT_CARE, eActive},
  106. {L"BREAKPOINT" , (DWORD)EXCEPTION_BREAKPOINT , DONT_CARE, eActive},
  107. {L"DATATYPE_MISALIGNMENT" , (DWORD)EXCEPTION_DATATYPE_MISALIGNMENT , DONT_CARE, eActive},
  108. {L"FLT_DENORMAL_OPERAND" , (DWORD)EXCEPTION_FLT_DENORMAL_OPERAND , DONT_CARE, eActive},
  109. {L"FLT_DIVIDE_BY_ZERO" , (DWORD)EXCEPTION_FLT_DIVIDE_BY_ZERO , DONT_CARE, eActive},
  110. {L"FLT_INEXACT_RESULT" , (DWORD)EXCEPTION_FLT_INEXACT_RESULT , DONT_CARE, eActive},
  111. {L"FLT_INVALID_OPERATION" , (DWORD)EXCEPTION_FLT_INVALID_OPERATION , DONT_CARE, eActive},
  112. {L"FLT_OVERFLOW" , (DWORD)EXCEPTION_FLT_OVERFLOW , DONT_CARE, eActive},
  113. {L"FLT_STACK_CHECK" , (DWORD)EXCEPTION_FLT_STACK_CHECK , DONT_CARE, eActive},
  114. {L"FLT_UNDERFLOW" , (DWORD)EXCEPTION_FLT_UNDERFLOW , DONT_CARE, eActive},
  115. {L"ILLEGAL_INSTRUCTION" , (DWORD)EXCEPTION_ILLEGAL_INSTRUCTION , DONT_CARE, eActive},
  116. {L"IN_PAGE_ERROR" , (DWORD)EXCEPTION_IN_PAGE_ERROR , DONT_CARE, eActive},
  117. {L"INT_DIVIDE_BY_ZERO" , (DWORD)EXCEPTION_INT_DIVIDE_BY_ZERO , DONT_CARE, eActive},
  118. {L"INT_OVERFLOW" , (DWORD)EXCEPTION_INT_OVERFLOW , DONT_CARE, eActive},
  119. {L"INVALID_DISPOSITION" , (DWORD)EXCEPTION_INVALID_DISPOSITION , DONT_CARE, eActive},
  120. {L"NONCONTINUABLE_EXCEPTION" , (DWORD)EXCEPTION_NONCONTINUABLE_EXCEPTION, DONT_CARE, eActive},
  121. {L"PRIV_INSTRUCTION" , (DWORD)EXCEPTION_PRIV_INSTRUCTION , DONT_CARE, eFirstChance},
  122. {L"SINGLE_STEP" , (DWORD)EXCEPTION_SINGLE_STEP , DONT_CARE, eActive},
  123. {L"STACK_OVERFLOW" , (DWORD)EXCEPTION_STACK_OVERFLOW , DONT_CARE, eActive},
  124. {L"INVALID_HANDLE" , (DWORD)EXCEPTION_INVALID_HANDLE , DONT_CARE, eActive}
  125. };
  126. #define ELISTSIZE sizeof(g_eList) / sizeof(g_eList[0])
  127. /*++
  128. Custom exception handler.
  129. --*/
  130. LONG
  131. ExceptionFilter(
  132. struct _EXCEPTION_POINTERS *ExceptionInfo
  133. )
  134. {
  135. DWORD dwCode = ExceptionInfo->ExceptionRecord->ExceptionCode;
  136. if ((dwCode & DBG_EXCEPTION) == DBG_EXCEPTION) // for the DebugPrints
  137. {
  138. return EXCEPTION_CONTINUE_SEARCH;
  139. }
  140. CONTEXT *lpContext = ExceptionInfo->ContextRecord;
  141. const WCHAR * szException = L"Unknown";
  142. BOOL bIgnore = FALSE;
  143. //
  144. // Run the list of exceptions to see if we're ignoring it
  145. //
  146. for (int i = 0; i < ELISTSIZE; i++)
  147. {
  148. const EXCEPT *pE = g_eList + i;
  149. // Matched the major exception code
  150. if (dwCode == pE->dwCode)
  151. {
  152. // See if we care about the subcode
  153. if ((pE->dwSubCode != DONT_CARE) &&
  154. (ExceptionInfo->ExceptionRecord->ExceptionInformation[0] != pE->dwSubCode))
  155. {
  156. continue;
  157. }
  158. szException = pE->cName;
  159. // Determine how to handle the exception
  160. switch (pE->dwIgnore)
  161. {
  162. case eActive:
  163. bIgnore = FALSE;
  164. break;
  165. case eFirstChance:
  166. bIgnore = TRUE;
  167. break;
  168. case eSecondChance:
  169. bIgnore = g_bWin2000 || (g_dwLastEip == lpContext->Eip);
  170. g_dwLastEip = lpContext->Eip;
  171. break;
  172. case eExitProcess:
  173. // Try using unhandled exception filters to catch this
  174. bIgnore = TRUE;//g_bWin2000 || IsBadCodePtr((FARPROC)lpContext->Eip);
  175. if (bIgnore)
  176. {
  177. ExitProcess(0);
  178. }
  179. g_dwLastEip = lpContext->Eip;
  180. break;
  181. }
  182. if (bIgnore) break;
  183. }
  184. }
  185. //
  186. // Dump out the exception
  187. //
  188. DPFN( eDbgLevelWarning, "Exception %S (%08lx)\n",
  189. szException,
  190. dwCode);
  191. #ifdef DBG
  192. DPFN( eDbgLevelWarning, "eip=%08lx\n",
  193. lpContext->Eip);
  194. DPFN( eDbgLevelWarning, "eax=%08lx, ebx=%08lx, ecx=%08lx, edx=%08lx\n",
  195. lpContext->Eax,
  196. lpContext->Ebx,
  197. lpContext->Ecx,
  198. lpContext->Edx);
  199. DPFN( eDbgLevelWarning, "esi=%08lx, edi=%08lx, esp=%08lx, ebp=%08lx\n",
  200. lpContext->Esi,
  201. lpContext->Edi,
  202. lpContext->Esp,
  203. lpContext->Ebp);
  204. DPFN( eDbgLevelWarning, "cs=%04lx, ss=%04lx, ds=%04lx, es=%04lx, fs=%04lx, gs=%04lx\n",
  205. lpContext->SegCs,
  206. lpContext->SegSs,
  207. lpContext->SegDs,
  208. lpContext->SegEs,
  209. lpContext->SegFs,
  210. lpContext->SegGs);
  211. #endif
  212. LONG lRet;
  213. if (bIgnore)
  214. {
  215. if ((DWORD)lpContext->Eip <= (DWORD)0xFFFF)
  216. {
  217. LOGN( eDbgLevelError, "[ExceptionFilter] Exception %S (%08X), stuck at bad address, killing current thread.", szException, dwCode);
  218. lRet = EXCEPTION_CONTINUE_SEARCH;
  219. return lRet;
  220. }
  221. LOGN( eDbgLevelWarning, "[ExceptionFilter] Exception %S (%08X) ignored.", szException, dwCode);
  222. lpContext->Eip += GetInstructionLengthFromAddress((LPBYTE)lpContext->Eip);
  223. g_dwLastEip = 0;
  224. lRet = EXCEPTION_CONTINUE_EXECUTION;
  225. }
  226. else
  227. {
  228. DPFN( eDbgLevelWarning, "Exception NOT handled\n\n");
  229. lRet = EXCEPTION_CONTINUE_SEARCH;
  230. }
  231. return lRet;
  232. }
  233. /*++
  234. Parse the command line for particular exceptions. The format of the command
  235. line is:
  236. [EXCEPTION_NAME[:0|1|2]];[EXCEPTION_NAME[:0|1|2]]...
  237. or "*" which ignores all first chance exceptions.
  238. Eg:
  239. ACCESS_VIOLATION:2;PRIV_INSTRUCTION:0;BREAKPOINT
  240. Will ignore:
  241. 1. Access violations - second chance
  242. 2. Priviliged mode instructions - do not ignore
  243. 3. Breakpoints - ignore
  244. --*/
  245. BOOL
  246. ParseCommandLine(
  247. LPCSTR lpCommandLine
  248. )
  249. {
  250. CSTRING_TRY
  251. {
  252. CStringToken csTok(lpCommandLine, L" ;");
  253. //
  254. // Run the string, looking for exception names
  255. //
  256. CString token;
  257. // Each cl token may be followed by a : and an exception type
  258. // Forms can be:
  259. // *
  260. // *:SecondChance
  261. // INVALID_DISPOSITION
  262. // INVALID_DISPOSITION:Active
  263. // INVALID_DISPOSITION:0
  264. //
  265. while (csTok.GetToken(token))
  266. {
  267. CStringToken csSingleTok(token, L":");
  268. CString csExcept;
  269. CString csType;
  270. // grab the exception name and the exception type
  271. csSingleTok.GetToken(csExcept);
  272. csSingleTok.GetToken(csType);
  273. // Convert ignore value to emode (defaults to eFirstChance)
  274. EMODE emode = ToEmode(csType);
  275. if (token.Compare(L"*") == 0)
  276. {
  277. for (int i = 0; i < ELISTSIZE; i++)
  278. {
  279. g_eList[i].dwIgnore = emode;
  280. }
  281. }
  282. else
  283. {
  284. // Find the exception specified
  285. for (int i = 0; i < ELISTSIZE; i++)
  286. {
  287. if (csExcept.CompareNoCase(g_eList[i].cName) == 0)
  288. {
  289. g_eList[i].dwIgnore = emode;
  290. break;
  291. }
  292. }
  293. }
  294. }
  295. }
  296. CSTRING_CATCH
  297. {
  298. return FALSE;
  299. }
  300. //
  301. // Dump results of command line parse
  302. //
  303. DPFN( eDbgLevelInfo, "===================================\n");
  304. DPFN( eDbgLevelInfo, " Ignore Exception \n");
  305. DPFN( eDbgLevelInfo, "===================================\n");
  306. DPFN( eDbgLevelInfo, " 1 = First chance \n");
  307. DPFN( eDbgLevelInfo, " 2 = Second chance \n");
  308. DPFN( eDbgLevelInfo, " 3 = ExitProcess on second chance \n");
  309. DPFN( eDbgLevelInfo, "-----------------------------------\n");
  310. for (int i = 0; i < ELISTSIZE; i++)
  311. {
  312. if (g_eList[i].dwIgnore != eActive)
  313. {
  314. DPFN( eDbgLevelInfo, "%S %S\n", ToWchar(g_eList[i].dwIgnore), g_eList[i].cName);
  315. }
  316. }
  317. DPFN( eDbgLevelInfo, "-----------------------------------\n");
  318. return TRUE;
  319. }
  320. /*++
  321. Register hooked functions
  322. --*/
  323. BOOL
  324. NOTIFY_FUNCTION(
  325. DWORD fdwReason)
  326. {
  327. if (fdwReason == DLL_PROCESS_ATTACH)
  328. {
  329. // Run the command line to check for adjustments to defaults
  330. if (!ParseCommandLine(COMMAND_LINE))
  331. {
  332. return FALSE;
  333. }
  334. // Try to find new exception handler
  335. _pfn_RtlAddVectoredExceptionHandler pfnExcept;
  336. pfnExcept = (_pfn_RtlAddVectoredExceptionHandler)
  337. GetProcAddress(
  338. GetModuleHandle(L"NTDLL.DLL"),
  339. "RtlAddVectoredExceptionHandler");
  340. if (pfnExcept)
  341. {
  342. (_pfn_RtlAddVectoredExceptionHandler) pfnExcept(
  343. 0,
  344. (PVOID)ExceptionFilter);
  345. g_bWin2000 = FALSE;
  346. }
  347. else
  348. {
  349. // Windows 2000 reverts back to the old method which unluckily
  350. // doesn't get called for C++ exceptions
  351. SetUnhandledExceptionFilter(ExceptionFilter);
  352. g_bWin2000 = TRUE;
  353. }
  354. }
  355. return TRUE;
  356. }
  357. HOOK_BEGIN
  358. CALL_NOTIFY_FUNCTION
  359. HOOK_END
  360. IMPLEMENT_SHIM_END